Showing posts with label restapi. Show all posts
Showing posts with label restapi. Show all posts

Tuesday, August 30, 2016

Getting KeyCloakContext From ServletRequest

When REST APIs are protected with keycloak authentication, we might need to get user realm in the backend to get some user information.

In the ServletRequestFilter implementation,


public class ApplicationFilter implements Filter {

private Base64 decoder = new Base64(true);

        @Inject
ServletRequestHolder requestHolder;

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
try {
checkAuthentication(req);
....
//TODO
}

/**
* Get the logged in user info from the request.
* @param request
*/
private void checkAuthentication(ServletRequest request) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
KeycloakSecurityContext kContext = (KeycloakSecurityContext) httpServletRequest
.getAttribute(KeycloakSecurityContext.class.getName());
String bearerToken;

if (kContext != null) {

bearerToken = kContext.getTokenString();
String[] jwtToken = bearerToken.split("\\.");
byte[] decodedClaims = decoder.decode(jwtToken[1]);
JSONObject jsonObj = new JSONObject(new String(decodedClaims));
.....
//TODO
}

Thursday, July 7, 2016

JAX-RS Custom Exception Mapper

When the service(backend) throws any custom exception, using <javax.ws.rs.ext.ExceptionMapper> we can catch it , and send a relevant message to the client (/caller).

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * Map the server exceptions to return to the client
 *
 */

@Provider
public class CustomJaxRsExceptionMapper implements ExceptionMapper {

@Override
     @Produces("application/json")
public Response toResponse(ServiceException e) {

          Map map = new HashMap();
  map.put("Exception", e.getMessage());

Response response = Response.status(500).entity(map).build();
return response;
}
}

Tuesday, June 16, 2015

Custom OAuth Grant-Type Support in APIManager


    According to OAuth 2.0 Spec , there are four main grant-types support is available in an OAuth 2.0 authorization server. It supports custom grant type also. WSO2 IS supports OAuth 2.0 spec and it can act as OAuth 2.0 authorization server.

    WSO2 APIManager uses OAuth tokens for API Security. (APIManager uses IS OAuth component to achieve OAuth support). User can write his own gran-type support for API security.

    For this, he has to write; 

  1. GrantTypeHandler : Write the grant type implementation in the handler class. For this implementation user can use AuthorizationGrantHandler  interface or by extending AbstractAuthorizationGrantHandler
  2. GrantTypeValidator: This implementation will validates all the request which are sent to token endpoint. For this implementation, use the "AbstractValidator" class which is available in Amber library from Apache.

Eg: For example, If i want to authorize the requests based on certificates (e.g.;. Grant-type is "cert-auth",) 
OauthHandler:

package org.test.oauth;

import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;

import org.wso2.carbon.identity.oauth2.model.RequestParameter;
import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
import org.wso2.carbon.identity.oauth2.token.handlers.grant.AbstractAuthorizationGrantHandler;
import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO;

public class CertificateGrantHandler extends AbstractAuthorizationGrantHandler{
public static final String CERTIFICATE = "sslclientcertb64";

@Override
    public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx)
            throws IdentityOAuth2Exception {

        boolean authStatus = false;
        
    OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO = tokReqMsgCtx.getOauth2AccessTokenReqDTO();
   
    String clientCert = null;
    // extract request parameters
        RequestParameter[] parameters = oAuth2AccessTokenReqDTO.getRequestParameters();

        // find out client certificate
        for(RequestParameter parameter : parameters){
            if(CERTIFICATE.equals(parameter.getKey())){
                if(parameter.getValue() != null && parameter.getValue().length > 0){
                clientCert = parameter.getValue()[0];
                }
            }
        }        
    return authStatus;
    }
}

Validator:


package org.test.oauth;

import org.apache.amber.oauth2.common.validators.AbstractValidator;
import javax.servlet.http.HttpServletRequest;

public class CertificateGrantValidator  extends AbstractValidator {

    public OauthCertificateGrantValidator() {

        // mobile number must be in the request parameter
        requiredParams.add(OauthCertificateGrantHandler.CERTIFICATE);
    }
}

Add the new grant-type in the identity.xml of the APIManager.

eg:


<SupportedGrantType>
<GrantTypeName>cert_auth</GrantTypeName>
<GrantTypeHandlerImplClass>org.test.oauth.CertificateGrantHandler</GrantTypeHandlerImplClass>
<GrantTypeValidatorImplClass>org.test.oauth.CertificateGrantValidator</GrantTypeValidatorImplClass>
</SupportedGrantType>

Generate token using;
curl -k -d "grant_type=cert_auth&sslclientcertb64=" -H "Authorization: Basic , Content-Type: application/x-www-form-urlencoded" http://localhost:8280/token

Tuesday, January 20, 2015

Adding URL parameters in WSO2 APIManager

It is a common use case, where users might want to add URL parameters  which are evaluated dynamically in their endpoints.
We can use same approach what we do in ESB, but it may confuse users on how to implement it in APIManager.
APIManager supports adding custom sequences for the API mediation logic. There are few ways available to add custom mediation plugins.
One, which we can simply use for this requirement is, store the mediation logic in registry as a sequence and select that in the in/out flow of the API invocation when publishing API.

Eg:
If we want to invoke the API with something like(note: we pass PhoneNumber parameter)
http://localhost:8280/dimtry/1.0?PhoneNumber=+16506697051 and send to the backend: http://ws.cdyne.com/phoneverify/phoneverify.asmx/CheckPhoneNumber? PhoneNumber=+16506697051&LicenseKey=0"

In the above example, backend service expects two URL parameters for the GET call to return proper response. But user will pass only one parameter(i.e.: phone number). We need to extract that user parameter and within api mediation logic we need to add another license key parameter too.

To achieve this, When publish an API define HTTP endpoint with parameters and assign values to those parameters in the custom sequence.

Eg: <http uri-template=" http://ws.cdyne.com/phoneverify/phoneverify.asmx/CheckPhoneNumber?Phonenumber={uri.var.PhoneNumber}&LicenseKey={uri.var.LicenseKey}"/>


Here, you need to pass two parameters, which can be passed through the custom sequence. Edit the existing log-in message sequence like below and save;(you can create new sequence and save it)

<sequence xmlns="http://ws.apache.org/ns/synapse" name="log_in_message">

                <property name="uri.var.LicenseKey" value="0" scope="default" type="STRING"/>
                <property name="uri.var.PhoneNumber" expression="substring-after(get-property('To'),'/dimtry/1.0?PhoneNumber=')" scope="default" type="STRING"/>
</sequence>

If you check above sequence,  phonumber is extracted from incoming request using an xpath and hardcoded a value for licensekey, which user can modify according to his requirement.
Now when  we create and publish the api, select above insequence.

APIConfiguration:


Send a GET request for the API like;


You will get response;


Saturday, November 29, 2014

Synapse Handler

Synapse handler is similar to axis2 handler , that can be used in RESTAPIs to handle incoming request and the responses.

Sample handler ;


package test;

import org.apache.synapse.MessageContext;
import org.apache.synapse.rest.AbstractHandler;

public class TestSynapseHandler extends AbstractHandler {
@Override
public boolean handleRequest(MessageContext messageContext) {
String idKey = "idKey";
String idValue = "abc";
messageContext.setProperty(idKey, idValue);
return true;
}

@Override
public boolean handleResponse(MessageContext messageContext) {
// TODO Response handling
return true;
}

}

Handler has to be registered in the RESTAPI configuration.

Sample configuration;

<api xmlns="http://ws.apache.org/ns/synapse" name="calAPI" context="/cal">
   <resource methods="GET">
      <nSequence>
         <send>
            <endpoint>
               <address uri="http://localhost:9763/services/calservice"/>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send>
            <endpoint>
               <default/>
            </endpoint>
         </send>
      </outSequence>
   </resource>
<handlers>
        < handler class="test.TestSynapseHandler"/>
   </handlers>
</api>












Sunday, August 31, 2014

Invoking SOAP service via an API in WSO2 APIManager

WSO2 APIManager provides  comprehensive features to support API management capabilities. Users can  publish APIs for REST and SOAP services. To test REST service, REST testing tools are available in API store. But for SOAP services there is no inbuilt tool to test the API. Users can use  any SOAP client to test the API. A simple easy to use tool is SOAPUI.
To test SOAP service api, user needs to set a http auth header to invoke api successfully.
There is no any difference  between REST service api and SOAP service api when creating,  publishing or subscribing to API. After generating an accesstoken, user can  use REST client to invoke API by setting "Authorization" header. 

Eg: Authorization: Bearer ozVF2bhIBnd6rgziVJNr1Vu0J54a

Same header has to be set as HTTP header in soap-ui too. Note that, this is not ws-security header.


Wednesday, October 3, 2012

REST Support in WSO2ESB - Handling JSON messages

In mobile applications we mostly use JSON messages, since they are easy to process. XML messages need more computational power to build the message.

 To process json messages, we need to enable the required message builder and formatter in the axis2 configuration.

 <messageFormatter  contentType="application/json" class="org.apache.axis2.json. JSONMessageFormatter"/>  

 <messageBuilder contentType="application/json"  class="org.apache.axis2.json. JSONBuilder"/>

Lets look at a sample scenario, where we need to 'GET' a json message from the backend service.
In this sample, we try to retrieve an user details which is stored in a database. To retrieve user details, we may define a simple "select" operation as a data-service.
Say, our data service runs at following endpoint;

http://localhost:9764/services/GetUserDetailsService.

As a second step,we need to define a suitable API for this..
Lets consider the "GET"  will be the only verb we are going to handle.
<api name="getusers" context="/public/user">
      <resource methods="GET"
                url-mapping="/*"
                inSequence="get-users-in-seq"
                faultSequence="fault"/>
   </api>

In the "get-users-in-seq" we might need to call our backend data service to retrieve user details.

 <sequence name="get-users-in-seq">  
      <property xmlns:ns="http://org.apache.synapse/xsd"
                name="userid"
                expression="substring-after(get-property('To'),'/public/user/')"/>
      <payloadFactory>
         <format>
           <p:getUserInfo xmlns:p="http://ws.wso2.org/dataservice">
               <p:Id>$1</p:Id>
            </p:getUserInfo>
        </format>
         <args>
            <arg xmlns:ns="http://org.apache.synapse/xsd"
                 expression="substring-after(get-property('To'),'/public/user/')"/>
         </args>
      </payloadFactory>
      <property name="SOAPAction" value="urn:getUserInfo" scope="transport"/>
      <send receive="recevingGetUserSeq">
         <endpoint>
            <address uri="http://localhost:9764/services/GetUserDetailsService"
                     format="soap11"/>
         </endpoint>
      </send>
   </sequence>
In the above sequence,

We construct the soap message using payloadfactory mediator, in a form which is expected by the backend data-service and send that to backend service. (note that, you can identify the SOAP message format, if you create a soapui project with the dataservice's wsdl.)

Backend service now will return the soap response, which we need to convert back as a json message and need to send back to the client.

   <sequence name="ecevingGetUserSeq">
         <xslt key="gov:/transformations/getUserTransform.xslt">
         <property xmlns:ns="http://org.apache.synapse/xsd"
                   name="userId"
                   expression="get-property('userid')"/>
      </xslt>
      <property name="messageType" value="application/json" scope="axis2"/>
      <send/>
   </sequence>
Here we use a XSLT script, which will formulate the expected json format which client expects.(ie: it will pick relevant info from the soap response and will construct a json message out of it)
Note that, we need to set the  "MessgeType"  property to "application/json", else axis2 wont pick the right message formatter.

We can execute this API with the following curl command to extract the user "Alice's" details.

   curl -v http://localhost:8280/public/user/Alice

Related post;
REST support in WSO2ESB - Introduction

Friday, September 14, 2012

REST support in WSO2ESB - Introduction

Version 4.0.3/later versions have an effective REST API mechanism to support REST invocations. The HTTP verbs such as GET/POST/PUT/DELETE..  can be handled with a simple configuration .

Sample REST API configuration to handle a GET request;
 <api name="echoAPI" context="/echo">
      <resource methods="GET"
                uri-template="/{string}"
                inSequence="echo-in-seq"
                outSequence="echo-out-seq"
                faultSequence="fault"/>
   </api> 
If you notice that ,here we define the context as "echo" which would be appear in the http url. User has to define a meaningful context if he wants to allow others  to use this API.
The url of the above API would be;
http://localhost:8280/echo
In this API we restricted ESB to handle only the GET requests. So, if user sends  requests for other verbs, those will be dropped at ESB end.

For the ur-template, we get only single parameter from the enduser.
So, to invoke this API, user has to send a single query parameter.
http://localhost:8280/echo?testString

If user sends multiple query parameters he has to define something like;
uri-template="/{string1}/{string2}"
uri-template="/{string1}&{string2}"

But it totally depends on the service implementation, how user going to handle the template.

If we have number of query parameters we could simply define the url-mapping like;

url-mapping="/*"

This will accept all the GET requests coming with the following URL format;
http://localhost:8280/echo


Related post ;
REST Support in WSO2ESB - Handling JSON messages