Monday, August 15, 2011

Fine grained authorization to RESTful services with XACML - Part I

XACML - extensible access control markup language, provides an flexible, fine grained and scalable way of achieving policy based access control.

WSO2 carbon product platform provides a fine grained access management solution with Policy Based Access Control (PBAC) based on XACML.

In this post, I will walk you through a sample scenario on how to build up a solution to control access to a RESTful service with XACML using WSO2 product stack. 
You can find an article on access management with XACML for a SOAP based service from here. We have some additional steps to perform in order to get XACML authorization working for RESTful services.

Scenario:
We have a RESTful back-end service which exposes methods to manage student entries with HTTP verbs: GET,  POST, PUT and DELETE. We need to allow access to perform POST, PUT and DELETE operations of the service only to admin users (since those methods do modifications in the system) and allow access to GET method to all the other users.

Architecture:
One key aspect of PBAC solutions based on XACML is the reference architecture which defines key components involve in access management according to their responsibility. Following are the logical components present in our setup:

1.PAP - Policy Administration Point - WSO2 Identity Server - this is where XACML authorization policies are governed.

2.PEP - (Policy Enforcement Point) - WSO2 ESB - this is where users' requests to services are intercepted and the access is allowed or denied according to the decision given by the PDP.

3.PDP - (Policy Decision Point) - WSO2 Identity Server - this is where authorization decision is taken by evaluating the XACML authorization request sent by PEP, against a matching  XACML policy found in PAP.

4. PIP - Policy Information Point - User store of WSO2 Identity Server - this is from where required attributes of the user who is trying to access the resource will be read by PDP in order to take the authorization decision.

Although there are four logical components listed above, we will have only two physical components in our access management solution since WSO2 Identity Server will play the roles of PAP, PDP and PIP as shown below.
                                       Image 1: Solution Architecture

Setting up the scenario & the solution:

1. Hosting the back-end service: 
  • You can obtain the source and archive of the REST service from here. If you want to learn more about RESTful services in Axis2, this article is a good reference.
  • We will host this back-end service in WSO2 App Server. (You can also host it in axis2 webapp deployed in Apache Tomcat as well.) Download and run the App Server.
  • Upload RestService.aar to the app server by logging into its management console through the browser (https://localhost:9443/carbon/). You may need to wait for few minutes to see the service in the services list after uploading. 
  • Click on the RestService and obtain its url. e.g: http://localhost:9766/services/RestService
                        Image 2: Hosting the service in Application Server.

2. Setting up WSO2 Identity Server (IS):
This plays the roles of PAP,PDP and PIP in our solution as mentioned above.
  • Download Identity Server, change its Ports.Offset to 1 in carbon.xml found in [IS_HOME]/repository/conf and run the server.
  • Login to management console (https://localhost:9444/carbon/) as admin.
  • Create a user called 'hasini' who will be belonged to 'everyone' role by default. And give 'login' permission to 'everyone' role.
  • Now download and import this XACML policy into IS through Entitlement->Administration->Import New Entitlement Policy as shown below. Once imported, 'enable' the policy from the icon in front of the policy name.
                                Image 3: Policy administration in Identity Server


3. Setting up WSO2 ESB as PEP:
  • Download ESB and change its port offset to 2, as we did with IS above.
  • We need to use the user store of IS, as the central user store of ESB as well. Therefore change user-mgt.xml of ESB which is found in [ESB_HOME] /repository/conf to point to the user store of IS by setting the following property:
ldap://localhost:10390
  • Give 'login' permission to 'everyone' role.
  • Now we need to configure a secured proxy service with entitlement mediator in its 'in sequence' in order to intercept and authorize REST requests sent to our back-end service.
  • Obtain the proxy service configuration from here and add it to the ESB configuration through Manage->Service Bus->Source View as shown below.
                                   Image 4: Configuring ESB proxy service

Remarks On Proxy Service:

        
            
                
Please note the following configuration of the proxy service as shown abovev which authenticates the user, obtains the authorization decision by communicating with IS and allows/denies the access to back end service.
  1. Proxy service is secured with User Name Token so that only the authenticated users will be able to access it.
  2. Endpoint is set to the back-end RESTful service.
  3. In sequence has three property mediators which are configured to specifically handle RESTful invocations.
  4. There is an entitlement mediator which communicates with EntitlementService in IS for obtaining authorization decisions.
  5. Header mediator removes the security header before sending the request to back-end service.
Running the client:
Let us have a look at our sample client code  (which you can download from here) which invokes the RESTful back end service which is secured by ESB proxy service. 
Here I need to highlight the REST way of invoking the proxy service when it is secured with User Name Token security policy.
//login as admin
            HttpTransportProperties.Authenticator authenticator = new HttpTransportProperties.Authenticator();
            authenticator.setUsername("admin");
            authenticator.setPassword("admin");
            //enable REST when invoking the service
            stub._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);
            stub._getServiceClient().getOptions().setProperty(Constants.Configuration.ENABLE_REST, Constants.VALUE_TRUE);

Following is how we perform the subsequent operations on the service stub.
//create student by admin
            System.out.println("Creating student by admin...");
            Student student = new Student();
            student.setName("Will");
            student.setAge(32);
            student.setSubjects(new String[]{"maths", "science"});
            stub.add(student);

            stub._getServiceClient().cleanupTransport();

            //login as non admin user.
            authenticator.setUsername("hasini");
            authenticator.setPassword("hasini");

            stub._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);
            stub._getServiceClient().getOptions().setProperty(Constants.Configuration.ENABLE_REST, Constants.VALUE_TRUE);

            //get student by non admin user
            System.out.println("Getting student by non admin user..");
            StudentName studentName = new StudentName();
            studentName.setName("Will");
            Student result = stub.get(studentName);
            System.out.println("Student's age="+result.getAge()+" successfully read by non admin user..");

            //update student by non admin user
            System.out.println("Updating student by non admin user..");
            result.setAge(40);
            result.setSubjects(new String[]{"english", "history"});
            stub.update(result);

As you can see, we perform add student operation as admin user and get student and update student operations as a non admin user. According to the scenario we mentioned at the very beginning, only the first two operations should be successful and the third operations should be denied.
Since the first and the third operation are asynchronous, we can not observer whether operation was allowed or denied from client side. To observe that, you need to enable debug level log for entitlement mediator in ESB.

Steps for running the client:
  • Add the following folders to the class path of the sample: [ESB_HOME]/repository/components/plugins, [ESB_HOME]/lib/api
  • Change the keystore path in line 40 of TestRestServiceStub to the wso2carbon.jks keystore of ESB which is in [ESB_HOME]/repository/resources/security
When you run the client, you will observe that add student operation and get student operation will pass and the update student operation will be denied (please refer to ESB logs) based on  the user who invokes the operation and the defined XACML policy.

In this way, you can achieve fine grained access control with PBAC solutions with XACML. Although this sample incorporates users' role as the attribute to define access control rules, you can use other attributes as well and manage access to your enterprise resources in a fine grained manner.

I believe you got an overall idea of how to build up an access management solution with XACML using WSO2 products stack.

What's Next...
In Pat-II of this, I will take you through the XACML policy used in this sample and the XACML requests sent to PDP in order to discuss how authorization decisions are taken.

Thanks for reading this post and have a good day...!

15 comments:

  1. Hi, I am not able to download 'XACML policy' file. Its saying not found. Can you please send it to sameerkumar15@gmail.com.

    Thanks

    ReplyDelete
  2. Hi Sameer,

    Sorry about that. I updated the link and now it works fine.
    Also you can download the complete set of resources for this sample from this location: https://sites.google.com/site/securedecentralizedblog/is/Resources_for_the_sample.zip?attredirects=0&d=1
    BTW: I have shown the XACML policy in detail in PART-II of this post.

    Thanks,
    Hasini.

    ReplyDelete
  3. Hello...sorry for my english, is not so good.I am a follower of yor work.
    In this very moment i am developing a security component (PEP) for java applications using XACML as authorization languaje and the Identity Server as policies server.I pretend through this component, externalize authorization through the centralization of security policies in the Identity Server.

    ReplyDelete
  4. For this I consumed the services AuthenticationAdmin and EntitlementService exposed in the Identity Server as you did here:http://hasini-gunasinghe.blogspot.com/2011_12_01_archive.html.I know that in the function"getDecision" can pass a object "decision" and not a parameter sampleRequest (XACML request).

    ReplyDelete
  5. I am interested in knowing if the object "decision" addition setAction,setResource,setSubject,setEnviroment , allows set more parameters. Please, if you not understand me let me know.

    ReplyDelete
  6. The best way to contact me is through my e-mail: aprieto@estudiantes.uci.cu
    Thanks for your time, I really would appreciate your help.

    ReplyDelete
  7. Hi,
    thank u for great job, i have a problem in last part, steps for running the client, i dont understand the way you compile the code, what kind of application is the source code, how can i compile it in the eclipse? could you please explain how should i make a project to compile his code?

    ReplyDelete
  8. Hi,

    This is just a java source code..

    Please follow the two steps that I have mentioned under "Steps for running the client" and those libraries to your classpath.

    Thanks,
    Hasini.

    ReplyDelete
  9. Hi Hasini,
    thank you for your comments,I have an issue at the moment, when i run the client application the admin user can add new student but when it gies to get the user's information via this code section:
    //get student by non admin user
    System.out.println("Getting student by non admin user..");
    StudentName studentName = new StudentName();
    studentName.setName("Will");
    Student result = stub.get(studentName);
    System.out.println("Student's age="+result.getAge()+" successfully read by non admin user..");
    it returns below exception:
    org.apache.axis2.AxisFault: The input stream for an incoming message is null.
    at org.apache.axis2.transport.TransportUtils.createSOAPMessage(TransportUtils.java:92)

    do you have any idea plz?

    thanks in advance

    ReplyDelete
    Replies
    1. Hi,

      What is the non-admin user that you are trying to invoke this operation as?
      You see this error at client side usually because policy is not evaluated as permit for non-admin user to perform this operation.

      Thanks,
      Hasini.

      Delete
  10. Hi,
    "hasini", as you mentioned in the instructions, but it doesn't matter which user is invoking the operations, i changed the source code to be invoked by admin but the result is the same and this exception will be appeared.

    Thanks ,

    ReplyDelete
  11. Hi ,
    please look at below log from ESB logs:

    TID: [] [WSO2 ESB] [2012-07-16 20:18:04,378] INFO {org.apache.synapse.mediators.builtin.LogMediator} - To: /services/xacml_rest_proxy/student/Will, MessageID: urn:uuid:1f71b78e-1671-405a-889c-cb2cd4c11bb5, Direction: request, Envelope: {org.apache.synapse.mediators.builtin.LogMediator
    TID: [] [WSO2 ESB] [2012-07-16 20:15:56,560] INFO {org.apache.synapse.mediators.builtin.LogMediator} - To: /services/xacml_rest_proxy/student/Will2222, MessageID: urn:uuid:1c106521-aa6b-456c-9151-2fac15f0430e, Direction: request, Envelope: 32Will2222mathsscience {org.apache.synapse.mediators.builtin.LogMediator
    TID: [] [WSO2 ESB] [2012-07-16 20:15:50,677] INFO {org.apache.synapse.mediators.builtin.LogMediator} - To: /services/xacml_rest_proxy/student/Will, MessageID: urn:uuid:df287158-c12f-425f-b59f-6187aee177e5, Direction: request, Envelope: 32Willmathsscience {org.apache.synapse.mediators.builtin.LogMediator

    as you see , add and update envelope is ok but get is not ok and is empty, i traced it in debug mode and in the stub the envelope content is ok for all operations but in the ESB in the get operation it will be empty, i dont understand why?

    ReplyDelete
    Replies
    1. Hi,

      Can you please attach the synapse configuration for the proxy service that you used? You can find it under: ESB_Home/repository/deployment/server/synapse-configs/default/proxy-services

      Thanks,
      Hasini.

      Delete
  12. Dear Hasini,
    fortunately, I found the problem and now it is working properly, the problem was in the ESB in which the proxy server wasnt defined as a secure service.
    I have a scenario that I have two different Identity servers, that should be considered for each message, now i defined two entitlement service in the ESB server, and it is working properly but i can not stop the sequence when the first entitlement service comes with deny response.
    also i can not understand how can i find which entitlement service returns deny response?
    and finally when the user is not authorized to do an action for example every body role try to delete a student, in the client application, how the software can be informed about the status? now it doesn't throw any exception in client side.

    Thanks

    ReplyDelete
  13. ERROR - WSO2Registry Resource cannot be empty
    ERROR - EntitlementMediator org.apache.synapse.SynapseException: Resource cannot be empty
    while invoking the proxy service

    ReplyDelete

Note: Only a member of this blog may post a comment.