This article introduces the functionality of ‘entitlement handler’, which provides XACML fine grained authorization in secured service access using ‘username token’ authentication. An end-to-end sample use case of entitlement handler is explained using WSO2 AppServer and WSO2 Identity Server. Also the necessary configuration steps and further possible improvements and flexibilities are included.
XACML – Extensible access control markup language
RBAC – Role Based Access Control
ABAC – Attribute Based Access Control
PEP - Policy Enforcement Point
PDP – Policy Decision Point
IS – Identity Server
AS – AppServer
When considering access control on a service, there are two major options to go for as RBAC and ABAC. With ABAC user can be authorized easily, considering even very tiny details like the time period they are trying to invoke the service or the domain of their e-mail address. With ABAC it is not compulsory to consider the particular identity or the role of the user as it allows to provide authorization in a more flexible manner that can be made powerful, considering more other parameters. XACML provides the standard to define policies and requests in order to achieve the above mentioned functionalities. With the 'entitlement handler', a user can be authorized using XACML fine grained authorization, without a single change to the service and without typing a single line of code, if the default configuration is sufficient for the scenario to be used.
As the name implies ‘entitlement handler’ is an Axis2 handler and packaged as ‘entitlement module’ (entitlement.mar) which can be engaged to any web service which uses ‘username token’ authentication. It can be engaged in any level from service level to the global level as preferred. Also with little modifications in module.xml of the module or service.xml of the service, it can be configured as desired and further improvements can be easily made as explained below at the end.
WSO2 Identity Server 3.2.0 or above
WSO2 AppServer 4.1.0 or above
In the scenario there is a client trying to access ‘echo’ service hosted in AS. We are applying ‘username token’ authentication and XACML fine-grained authorization on the service so that a user can call the service only if the email address of the user is from ‘wso2.com’ domain.
High level view
In this scenario the entitlement handler acts as the PEP and is configured to use WSO2 IS as the PDP. The service secured with username token is hosted in AS and the entitlement handler is engaged to the service. So now when a client tries to call a service, the handler will intercept it before the service and check whether that request can be allowed to proceed to the service, according to the PDP.
Here you can download the entitlement module source code and build it using maven2. In the target directory you will find the entitlement.mar package. Instead of building from source you can also download the entitlement.mar and directly use it, which is all we need to proceed.
As we need both WSO2 IS and WSO2 AS to be up and running at the same time, we need to change the ports used by one server, as by default both are set to same port numbers. Let’s change the WSO2 AS port numbers simply by modifying the carbon.xml. It can be found inside CARBON_HOME/repository/conf of the server and modify the file with this <Offset>2</Offset> under the <Ports> tag, which is originally set to be <Offset>0</Offset>. Now just have to remember that when we start the servers being in the relevant bin directories, they will be running on different ports that IS on HTTPS – 9443 and AS on HTTPS – 9445.
Entitlement module configuration
- As the module is calling the entitlement service of the WSO2 IS to get authenticated in the server, we need its service stub org.wso2.carbon.identity.entitlement.stub-3.2.0.jar. Download it from the given link and place it inside CARBON_HOME /repository/components/lib of the AS where we are going to place the module to give authorization on hosted services.
- We can define exactly at which phase of which flow, the module should intercept the message from a client, using the axis.xml of the AS. For the entitlement handler it needs to intercept message after the security phase where rampart module put relevant security headers. In order to achieve this modify the CARBON_HOME/repository/conf/axis2.xml as follows, inside InFlow, after the ‘Security’ phase.
<phaseOrder type="InFlow"> <phase name="Security"/> <phase name="Entitlement"/>
- The following configurations can be done even after the server is up and running and if we wish we can configure them as follows too.
a) We can engage the module to the service we want to provide fine-grained authorization by simply adding this line in service.xml, ‘<module ref = “entitlement”/>’. If the module is present in the server then you do not have to manually engage the module to the service.
b) Inside the module.xml of the module there are several parameters that need to be set as following. For this scenario, setting up the "trustStoreLocation" parameter to point to the wso2carbon.jks is sufficient and others can be kept same.
<parameter name="remoteServiceUrl">https://localhost:9443/services/</parameter> <parameter name="remoteServiceUserName">admin</parameter> <parameter name="remoteServicePassword">admin</parameter> <parameter name="remoteIp">127.0.0.0</parameter> <parameter name="decisionEvaluatorClass"></parameter> <parameter name="trustStoreLocation">---path to CARBON_HOME/resources/security/wso2carbon.jks---</parameter> <parameter name="trustStorePassword">wso2carbon</parameter>
Engaging the entitlement module
1. Now let’s start the AS and once you hit the URL ‘https://localhost:9445/carbon/’ and logged into the server, you will be on main menu. Then under module hit add and upload the entitlement.mar package to the AS as follows.
2. Once done it will ask to restart the server and hit ok. Wait a little until server get restarted and once done hit list under module. It will show a list as follows including entitlement module. Hit on name ‘entitlement’.
3. It will show the information of the module as follows. Hit ‘Edit Module Parameters'.
4.Now we are directed to another option to do the same configuration we did modifying the module.xml. If there is any change needed, we can do it now.
5. Once we finished configuring the module, now we can engage it to a desired service. This step is not needed if we engaged the module in service.xml in the previous section and just go to next step and check. Once we hit Web Services List it will list down all the services present and whether they are secured or not. In this scenario we will secure echo service and so let’s hit ‘unsecured’ in front of the service.
6. Then you will be directed to apply desired security aspects and as entitlement handler is implemented to support username token authentication let’s apply that as follows and go next.
7. Now it will ask for which user group this security should be concerned and give ‘everyone’ for the moment and finish.
8. Once you finished, it will give a message as ‘Security applied successfully’, click ok for the message and show the following. Note that now the endpoints are shown with https port only and security is active. Hit on the ‘Modules’.
9. Now if you engaged the entitlement module in service.xml you will see it in the currently engaged modules list and if not you can engage it as follows by selecting the module from drop down menu.
10. Also now if you list the web services it will be something like follows, showing ‘echo’ service is secured.
Now we have finished all the configurations and secured the web service with XACML fine-grained authorization. But still we have not made the PDP ready for the scenario. So let’s look into that.
Configuring the PDP
1. Once the IS is started being at bin directory, you will be guided to the Main menu for the URL ‘https://localhost:9445/carbon/’, after logging in as admin. There under Administration you can import a new policy as follows.
2. Browse for the downloaded policy.xml and upload it to the server.
3. Once the policy is uploaded successfully, it will show as follows and hit 'Enable' which will then change to be 'Disable'.
Here is the policy we are using and a brief explanation on what it is doing.
<Policy xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os" PolicyId="urn:sample:xacml:2.0:samplepolicy-01" RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"> <Description>Sample XACML Authorization Policy</Description> <Target> <Subjects/> <Actions/> <Resources> <Resource> <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match"> <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">https://localhost:9445/services/echo/</AttributeValue> <ResourceAttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" DataType="http://www.w3.org/2001/XMLSchema#string"/> </ResourceMatch> </Resource> </Resources> <Environments/> </Target> <Rule Effect="Permit" RuleId="primary-group-rule"> <Target> <Subjects/> <Actions> <Action> <ActionMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal"> <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</AttributeValue> <ActionAttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" DataType="http://www.w3.org/2001/XMLSchema#string"/> </ActionMatch> </Action> </Actions> <Resources/> <Environments/> </Target> <Condition> <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of"> <Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match"/> <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">wso2.com</AttributeValue> <SubjectAttributeDesignator AttributeId="http://wso2.org/claims/emailaddress" DataType="http://www.w3.org/2001/XMLSchema#string"/> </Apply> </Condition> </Rule> <Rule Effect="Deny" RuleId="deny-rule"/></Policy>
This policy will be applied for requests coming for the resource resource 'https://localhost:9445/services/echo/' and it says to 'Permit' for the action 'read' under the condition that email address of the user has 'wso2.com' as a regex. Any other request that policy get applied with get 'Deny' as the decision.
4. Lets add few users to the server so that we can check how entitlement handler works. Hit 'Add New User' and give the password and then hit on 'User profile'.
5. Add few users as preferred and for the default client code to work, there need to be a user called Jon, with password '12345' and email address firstname.lastname@example.org. Create another user in the same way whose email address is from some other domain too.
6. We can try the functionality of the policy by just hitting on 'TryIt' and filling the parameters. According to the policy if we put 'https://localhost:9445/services/echo/' as Resource, emailaddress as Subject Attribute Name, read as action and other attributes as preferred with Subject Attribute Value having a wso2.com email address, evaluation will give 'Permit' as follows.
7. To ensure the policy, let's only change the domain to something other than wso2.com and here I am putting wso3.com, which gives 'Deny' as response.
Now let's try out our scenario. Run the client code you have downloaded with user name 'Jon' with his password and you will receive the response. Try the client with some other user name whom you defined in IS previously and do not have a 'wso2.com' email address. It will not allow you to proceed and in the console of AS you will see 'User not authorized to perform the action.', as a result of the duty done by entitlement handler. (To fix the dependencies of the given client, you can simply add CARBON_HOME/lib /api/org.wso2.carbon.securevault-3.2.0.jar and CARBON_HOME/repository/ componenets/plugins of IS as dependencies)
Now if you are curious to know how this is done you can look into the source code and following are the operations done inside the handler. It just read the relevant parameters and if allowing the message to continue to the service, it will be the same message it received without any alteration.
The advantage of using a handler for the purpose is we can place it just in front of the service closely and can configure it in multiple levels with different parameters.
There are possible improvements for the handler that this can be implemented with authentication mechanisms other than 'username token' and to support multiple of them. Also the flexibility is there for the user to come up with a selection of a PDP in own desire and implement a just the way to call the PDP and get response.