Showing posts with label WSO2. Show all posts
Showing posts with label WSO2. Show all posts

Wednesday, July 15, 2015

[WSO2 APIM ] [Gateway] How to limit traffic to WSO2 APIM Gateway using API Handlers

Recently I had to find a solution to limit the traffic that passes through the WSO2 APIM Gateway. The requests that are allowed to pass through are the requests from the testing team. The architecture of the system is quite straight forward. Please find the drawing of the system architecture in brief below.
When it comes to limiting the traffic to the Gateway, there are several ways to handle this. This can be handled from the network level (e.g. routing rules) or even simply starting the server on a another port that is not visible to the outside world. Here, in this article I am going to describe how to do it from the Gateway. Using this method is only another option to achieve the goal.

 When an API is created from the WSO2 APIM, using the publisher, the publisher will propagate the changes to the WSO2 APIM Gateway. By this it means it will create a synapse configuration on the Gateway. This synapse configuration of the API will hold a set of handlers. These handlers are placed to achieve different functionality. For e.g. the APIAuthenticationHandler is intended to validate the token. More information on the handlers can be found at [1]. The Handlers are placed in an order in the synapse configuration, and they will be executed in the order they appear. Since the first point of contact in the API is their handlers, we can use a handler to filter out the request if it is from the testing device or not.

 To achieve this, we need to have an identifier being sent from the request. If we have this, it is easily possible to filter them out. First things first, we need to figure out what the identifier is. In my case, the requests sends the device ID in the header, under the parameter "Auth". So in my handler I will read the header and check for this Auth value.
 How do I tell which device Id's are able to continue? For this, I will read the device Id's from a system property so that the allowed device Id's could be passed from the command line as a system property when the server is start up.

 Okay, so given a brief description of what we are going to achieve, lets see how we can do this.

1. Create the Custom Handler. To create the custom handler we need to create a maven project, and create a new class. This class must be extending the "org.apache.synapse.rest.AbstractHandler". Please find the sample code below.

package org.wso2.CustomHandler;

import java.util.Map;

import org.apache.synapse.MessageContext;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.rest.AbstractHandler;

public class GatewayTrafficContrlHandler extends AbstractHandler {

 public boolean handleRequest(MessageContext arg0) {
  String deviceId = null;
  String identifier = null;
  String[] identifiers;

  //Obtain the identifier .
  Map headers = (Map)((Axis2MessageContext) arg0).getAxis2MessageContext().
  getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
  identifier = (String) headers.get("Auth");

  if (identifier == null || identifier.isEmpty()) {
   //Get the first identifier which is the device ID.
   identifiers = identifier.split("\\|");
   if (identifiers != null && identifiers.length > 0) {
    deviceId = identifiers[0];
   }

   //Get the device ID list  which is passed as a system property from                        //command line.Only these deviceId's will be allowed to pass 
                        //through the handler.
   String[] supportedDeviceIdList = null;
   String supportedDeviceIds = System.getProperty("deviceIds");
   if (supportedDeviceIds != null && !supportedDeviceIds.isEmpty()) {
    supportedDeviceIdList = supportedDeviceIds.split(",");
   }

   //Check if the device Id which is sent in the request is in the 
                        //list of deviuce Id's passed as a system property.
   if (supportedDeviceIdList != null && supportedDeviceIdList.length > 0) {
    for (int index = 0; index < supportedDeviceIdList.length; index++) {
     if (supportedDeviceIdList[index].equals(deviceId) == true) {
      return true;
     }
    }
   }
  }
  return false;
 }

 public boolean handleResponse(MessageContext arg0) {
  // TODO Auto-gGatewayTrafficContrlHandlerenerated method stub
  return false;
 }

}

2. Once we create the Class to extract and find for the identifier, we then need to build the jar.
3. Copy the created Jar to the /repository/components/lib folder. 
4. Then start the APIM server with the system property -DdeviceIds, and log into the APIM      
     Gateway's management console. for e.g :- ./wso2server.sh -DdeviceIds=123,456,789 
5. Go to Service Bus > Source View in the Main menu.
6. In the configuration select the API, and then check the Handler section. In the handler section, add 
    the class to point to the newly created handler.


 <handlers>
        <handler class="org.wso2.CustomHandler.GatewayTrafficContrlHandler"/>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler"/>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.throttling.APIThrottleHandler">
            <property name="id" value="A"/>
            <property name="policyKey" value="gov:/apimgt/applicationdata/tiers.xml"/>
        </handler>
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtUsageHandler"/>
        <handler class="org.wso2.carbon.apimgt.usage.publisher.APIMgtGoogleAnalyticsTrackingHandler"/>
        <handler class="org.wso2.carbon.apimgt.gateway.handlers.ext.APIManagerExtensionHandler"/>
    </handlers>

7. Make sure the newly created handler is the first handler in the list.
8. Once we do the change save, and observe the console for reloading of the API. 
9. Test the Handler by doing a REST API call.

And that's it. 

 The above solution will only let the requests with deviceId's 123 or 456 or 789 to pass through.

  References 

[1] https://docs.wso2.com/display/AM160/Architecture#Architecture-APIHandlers
[2] https://docs.wso2.com/display/AM160/Writing+a+Custom+Authentication+Handler

Tuesday, May 19, 2015

[WSO2-ESB] URI- template encoding in WSO2 ESB for reserved characters

 In a recent engagement with the WSO2 ESB, I've come across a situation where WSO2 ESB does not encode some reserved characters when using a URI-template as the endpoint to a get call.

In this particular instance it was the '&' that was not getting encoding and the <space>.

The reason is that WSO2 ESB uses the java.net libraries  and it ends up not encoding the characters correctly.

As a solution to this, we can use the script mediator in WSO2 ESB. We could get the value in the script mediator and encode it using the  javascript encode uri method, and encode our values correctly.

An example as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<api
    xmlns="http://ws.apache.org/ns/synapse"
     name="testAPI"
     context="/testAPI">
    <resource methods="POST">
        <inSequence>
            <log level="full"/>
            <property name="uri.var.fullname" expression="//APIRequest/Fullname/text()"/>
            <property name="uri.var.address" expression="//APIRequest/HomeAddress/text()"/>
            <script language="js">var fullname = mc.getProperty('uri.var.fullname');var homeAddress = mc.getProperty('uri.var.address');                 mc.setProperty("uri.var.fullname",encodeURIComponent(fullname));                 mc.setProperty("uri.var.address",encodeURIComponent(homeAddress));                 </script>
            <header name="Content-Type" scope="transport" action="remove"/>
            <send>
                <endpoint>
                    <http method="get"
                     uri-template="https://localhost/test?apikey=1234&amp;api=54&amp;fullname={+uri.var.fullname}&amp;address={+uri.var.address}"/>
                </endpoint>
            </send>
        </inSequence>
    </resource>
</api> 
 
 
 
In this what is done is that the API request values has been extracted and stored in a property using a property mediator.

Then by using the script mediator  we make use of javascript and store the value as a var and encode it using the "encodeURIComponent" method.

In the "encodeURIComponent" yet, we still have some characters that are not encoded. These include the !'()*~.

References
[1] https://docs.wso2.com/display/ESB480/Getting+Started+with+REST+APIs#GettingStartedwithRESTAPIs-URItemplates
[2] https://docs.wso2.com/display/ESB480/Template+Endpoint
[3] https://docs.wso2.com/display/ESB480/Property+Mediator
[4] https://docs.wso2.com/display/ESB480/Script+Mediator

Friday, April 17, 2015

Unable to get the hostName or IP address of the server when starting WSO2 Products

Today I faced an issue when a wso2 server that needed restarting did not start up, after shutting down due to the error

{org.apache.synapse.ServerConfigurationInformation} -  Unable to get the hostName or IP address of the server {org.apache.synapse.ServerConfigurationInformation}
java.net.UnknownHostException: <hostname> : <hostname> : Name or service not known
at java.net.InetAddress.getLocalHost(InetAddress.java:1473)
at org.apache.synapse.ServerConfigurationInformation.initServerHostAndIP(ServerConfigurationInformation.java:215)
at org.apache.synapse.ServerConfigurationInformation.<init>(ServerConfigurationInformation.java:66)
at org.apache.synapse.ServerConfigurationInformationFactory.createServerConfigurationInformation(ServerConfigurationInformationFactory.java:48)
at org.wso2.carbon.mediation.initializer.ServiceBusInitializer.initESB(ServiceBusInitializer.java:365)
at org.wso2.carbon.mediation.initializer.ServiceBusInitializer.activate(ServiceBusInitializer.java:182)


The issue in my case was that it was not a wso2 bug. It is an issue with the machine.

The OS I was using was ubuntu. The issue was that the hostname was not correctly set.


The easy way  - temporary fix

1) Run the following command. (assuming the hostname you want to set is abc001)

                    #    hostname abc001

2) To verify the hostname just run the following command

                           # hostname


it should output the hostname that we set as the output.


Proper fix

1) We need to fix this properly. In that case, we need to change the /sysconfig/network file.

open this file as sudo or super user, and change the hostname in that file to be the hostname you require.

                           HOSTNAME=abc001

2) Then you need to update the etc/hosts file, to reflect this change. you should map the entry

                       127.0.0.1 abc001 

                               or
                      <local-ip> abc001

3) After that you need to restart the network service.

                   #service network restart
                                       or 
                   #/etc/init.d/network restart

4) Then verify the hostname has been set by running the command

                    # hostname


References

1) http://www.howtogeek.com/50631/how-to-change-your-linux-hostname-without-rebooting/