Thursday, October 15, 2015

[WSO2 ESB] Using Secure vault to store passwords securely in your synapse configuration

When you are using WSO2 ESB to do some mediation there come the times that you have to send user credentials to the backend services for authentication and authorization purposes. In that case, you have to store the usernames and passwords in your synapse configuration. One problem of that approach is that other people get to see the credentials which is not very secured.

To overcome that issue you can use the secure vault feature in ESB. What happens there is, first you create a registry entry for your password with an alias for the password. In the registry the password is NOT stored in plain text, but encrypted using the certificate in wso2carbon.jks file. This entry is stored as a property under the location /_system/config/repository/components/secure-vault in the registry. Then in your synapse configuration you use the alias instead of the password, and use a custom xpath expression to lookup for the password from the registry and decode it before sending it to the endpoint.

First of all you need to configure the ciphering part. For that you need configure the 'repository/conf/security/secret-conf.properties' file and add the following.


keystore.identity.location=<ESB_HOME_Absolute_path>/repository/resources/security/wso2carbon.jks
keystore.identity.type=JKS
keystore.identity.store.password=identity.store.password
keystore.identity.store.secretProvider=org.wso2.carbon.securevault.DefaultSecretCallbackHandler
secretRepositories.file.provider=org.wso2.securevault.secret.repository.FileBaseSecretRepositoryProvider
secretRepositories.file.location=repository/conf/security/cipher-text.properties
secretRepositories=file
keystore.identity.key.password=identity.key.password
carbon.secretProvider=org.wso2.securevault.secret.handler.SecretManagerSecretCallbackHandler
keystore.identity.key.secretProvider=org.wso2.carbon.securevault.DefaultSecretCallbackHandler
keystore.identity.alias=wso2carbon


Restart the ESB server if you have already started it. Log in, and go to the "Manage Passwords" link under the "Secure Vault Tool" entry in the left hand menu. In that page you can create a new password (or username) entry in the registry by providing a key(alias) and the actual password. Then your password will be stored in the registry encrypted with the key in wso2carbon.jks .

Now setting up the alias is done, let's see how to retrieve it in your synapse configuration. You can use the custom xpath expression called "wso2:vault-lookup('alias')" to retrieve the decrypted password mapping to the alias. See the example given below.


<log>
<property name="Sec_Password" expression="wso2:vault-lookup('pass')"/>
</log>




Monday, October 12, 2015

[WSO2 ESB] How to retrieve details of the API at runtime from synapse configuration

I have come across that many users of WSO2 ESB are using it for creating and deploying APIs and they have this requirement to retrieve the details of the API which is being called currently at runtime, from somewhere out side the API (in example from a different sequence outside the API).

Following are some synapse level properties that you can use to get some details of the REST API that is being called at runtime, and how to show those details using the log mediator.


<property name="apiname" expression="$ctx:SYNAPSE_REST_API"></property>
<property name="contxt" expression="$ctx:REST_API_CONTEXT"></property>
<property name="urlpre" expression="$ctx:REST_URL_PREFIX"></property>
<property name="restmethod" expression="$ctx:REST_METHOD"></property>

                    
<log>
   <property name="*******************URL_PREFIX***********" expression="get-property('urlpre')"></property>
</log>
<log>
   <property name="*******************API_NAME***********" expression="get-property('apiname')"></property>
</log>
<log>
   <property name="*******************API_CONTEXT***********" expression="fn:substring-after(get-property('contxt'),'/')"></property>
</log>
<log>
   <property name="*******************REST_METHOD***********" expression="get-property('restmethod')"></property>
</log>

[WSO2 API manager] Converting API GET requests to send a POST request to the backend

Some users of WSO2 API manager has this requirement of changing the HTTP method of the incoming request and send it to the backend with a different HTTP method. As an example some users want to change a GET request coming to the API extract some values from the query parameters and send it as a POST request after generating a payload which is required to the backend. Let's see how we can achieve this with WSO2 API Manager, for this blog post I'm using the latest API manager 1.9.1 version but this would work for earlier versions as well.

First of all let's get the requirement clarified first. 

I have a JAXRS service hosted in WSO2 Appserver which I want to expose through API Manager. In my service I have to send a POST request to the URL,

http://localhost:9763/jaxrs_basic/services/customers/customerservice
with a xml payload similar to below to create a new customer in the system.

<Customer>
   <name>Jack</name>
</Customer>


My real requirement is to expose this POST operation through an API such that, when the client sends a GET request to the API with name as a query parameter, it will generate the necessary payload and send it as a POST request to the back end URL above.

So Let's see how to do it.

First of all you need to create an API in APIManager publisher app and publish it. Make sure you add a resource similar to /sample/{name} with a uri-template in the API allowing GET method. And after that go to API Store log in and subscribe to the API you created. If you need help in this initial steps refer the API manager documentation [1] for further instructions.

Now, we have done the initial part, we have an API created with a GET resource. Let's see what are the modifications that we need to do for this API to convert this GET request to a POST request to be sent to the back end.

Find the API configuration xml file for your newly created API in <APIM_HOME>/repository/deployment/server/synapse-configs/default/api/ directory, and open it using a text editor.

Find the resource definition similar to below.

<resource methods="GET" uri-template="/sample/{name}" faultSequence="fault">

Now let's change few things inside the <inSequence> of this resource definition, and let me explain why we are changing them in that manner.

<property name="HTTP_METHOD" value="POST" scope="axis2" type="STRING"/>

Add the above to change the HTTP method of the outbound request to POST method.

<property name="messageType" value="text/xml" scope="axis2"/>
This will set the message type of the outbound payload to text/xml content type.

    <payloadFactory media-type="xml">
                  <format>
                      <Customer xmlns="">
                        <name>$1</name>
                      </Customer>
                  </format>
                  <args>
                    <arg expression="get-property('uri.var.name')"/>
                  </args>
    </payloadFactory>
Then you need to create the xml payload you need with the provided customer name input, that was passed as a query parameter in the GET request. You can use the payloadFactory mediator similar to above configuration to build the xml payload.

<property name="REST_URL_POSTFIX" value="" scope="axis2"/>
Finally you need to add this property in order to prevent the resource path that you send to the API GET resource being appended to the service endpoint URL. Note the empty value set in this property.

Ok that's it!!! Now it would change your API request like below to send to the backend service.

Request to API:

GET : http://hostname:port/apicontext/1.0.0/sample/john 


Request sent to the backend service :

POST : http://localhost:9763/jaxrs_basic/services/customers/customerservice
<Customer>
   <name>john</name>
</Customer>



The full synapse configuration for the GET resource is below.


<resource methods="GET" uri-template="/sample/{name}" faultSequence="fault">
      <inSequence>
         <filter source="$ctx:AM_KEY_TYPE" regex="PRODUCTION">
            <then>
               <property name="api.ut.backendRequestTime"
                         expression="get-property('SYSTEM_TIME')"/>
               <property name="HTTP_METHOD" value="POST" scope="axis2" type="STRING"/>
               <property name="messageType" value="text/xml" scope="axis2"/>
               <payloadFactory media-type="xml">
                  <format>
                      <Customer xmlns="">
                        <name>$1</name>
                      </Customer>
                  </format>
                  <args>
                    <arg expression="get-property('uri.var.name')"/>
                  </args>
               </payloadFactory>
               <property name="REST_URL_POSTFIX" value="" scope="axis2"/>
               <send>
                  <endpoint name="admin--JAXRSbasic_APIproductionEndpoint_0">
                     <http uri-template="http://localhost:9763/jaxrs_basic/services/customers/customerservice"/>
                  </endpoint>
               </send>
            </then>
            <else>
               <sequence key="_sandbox_key_error_"/>
            </else>
         </filter>
      </inSequence>
      <outSequence>
         <class name="org.wso2.carbon.apimgt.usage.publisher.APIMgtResponseHandler"/>
         <send/>
      </outSequence>
   </resource>


References

[1] https://docs.wso2.com/display/AM191/Quick+Start+Guide#QuickStartGuide-CreatinganAPIfromscratch