Wednesday, November 20, 2013

Building a data entitlements ecosystem with WSO2 IS, ESB, and DSS

Introduction 

Most enterprises have a tendency to consider data security as a function attached to or supported by the business logic layer.  A common practice is to use a key identifier such as user roles or permissions to be part of the data filtering criteria often combined with other business specific filtering.  Traditionally this practice has evolved with the comfort which system designers took in making sure that business components and data components would reside within the same deployment and system administration boundaries.

Things have evolved.  With enterprises adapting more loosely coupled and SOA driven architectures, the need for federated security has become a point of discussion across all application layers, including data.  Commonly known as data entitlements, the idea is to use enterprise-wide security models at the point of producing and storing data in and out of the data providers so that the applications will not have to handle filtering based on entitlements.

So why is this important?  I think the answer can be found if you take a closer look at some typical issues seen on enterprises related to data duplication and consolidation nightmares, overlapping security policies and policy administration overheads.

Let me try to explain the problem through a common use case.

'SomeCo' is a premium advertising company with multiple business units to handle a large client base.  They have a divided sales organization specialized for banking & financial, manufacturing & retail, food and beverages.  The company maintains a pool of applications that needs to comply with data entitlements policies across the enterprise.  The platform needs to make sure that sales information related to a particular domain is only made visible to the members of that particular sales group as well as anyone with an organizational role above a senior manager.  This policy needs to be enforced across all applications that present sales information to the users.




Let’s take a look at a typical ecosystem that WSO2 can provide for SomeCo’s enterprise data entitlements need.  For this solution, I am going to leverage the WSO2 ESB, IS and DSS.   I believe the purpose of using DSS should be quite familiar to most… and that is to expose SomeCo’s sales data as services.   We are going to use WSO2 IS’s attribute based authorization service built on XACML to define entitlements policies for our sample service call.   The idea is, based on some user identifier (i.e. UsernameToken), we will query the WSO2 IS for a set of claims through a XACML request and built our dynamic query based on claims received as part of the response before calling the DSS service.


Solution




- Our aim is to provide a unified interface to the consumer applications to access Sales data so that applications do not need to handle data filtering based on users’s security attributes (such as user roles).

- Assume the scenario where the users of these different sales organizations need to query sales details through different applications (i.e. Sales forecasting, revenue predictions etc).    To achieve this, our solution needs to expose a service API (i.e. let’s say an API called getSalesInfo), which typically is an ESB endpoint which these applications can connect to.

- Each application needing to consume this API will send its request payload with the authentication headers (in this sample a UsernameToken) to the ESB endpoint.

- After the ESB authenticates the request (usually against the same user store which the applications are connected to), the ESB flow needs to acquire a set of claims this user is entitled for.  WSO2 ESB provides an entitlements mediator for this purpose.  The primary role of the entitlements mediator is to create and send a XACML request to WSO2 IS, and branch out the API flow based on the decision (i.e. Permit, Deny, indeterminate, Not Applicable).  In addition to the decision, this mediator can also receive claims (in the form of advices) back from IS which is really what we are interested in to build the dynamic DSS query.

- As an example, we can receive user’s roles as advices inside the XACML response so that we can build the DSS service’s filter criteria based on roles.

- As you might have noticed, the caller applications didn’t have to worry at all about filtering for data entitlements and it was all handled by the service API we have created using the WSO2 components.


ESB Mediation Flow



The heart of the mediation flow is the part where we extract claims out of the XACML response to build our dynamic query.  In many instances, the filter we might need to construct would usually be quite specific to transnational data; hence we may need to translate those claims into a filter query which the DSS service will be able to successfully map to SQL.   Well, we can think of many possible options for doing this, but for simplicity I implemented a simple mediator to build the filter query.  

Please refer [R2] for the Java code I used to read the advices from the Message context, and then create the filter query for the DSS service.

Resources

[R1] Sample XACML Policy with AdviceExpressions for returning claims to the caller (marked in red). 
Note the XACML policy defines that user's role is returned with the decision. The entitlements mediator can grab this and pass this on to the ESB sequence which can construct the dynamic query based on these claims.  In this example, we are using the user's role, but it can be any attribute that is in the user store and retrieved by Policy Information Point (PIP)

<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17"  PolicyId="CustomerServiceSales" RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" Version="1.0">
   <Target></Target>
   <Rule Effect="Permit" RuleId="Rule1">
      <Target>
         <AnyOf>
            <AllOf>
               <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
                  <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">customerproxy</AttributeValue>
                  <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
               </Match>
               <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                  <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">read</AttributeValue>
                  <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" Category="urn:oasis:names:tc:xacml:3.0:attribute-category:action" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
               </Match>
               <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                  <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">sales</AttributeValue>
                  <AttributeDesignator AttributeId="http://wso2.org/claims/role" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
               </Match>
            </AllOf>
         </AnyOf>
      </Target>
   </Rule>
   <AdviceExpressions>
      <AdviceExpression AdviceId="customerService" AppliesTo="Permit">
         <AttributeAssignmentExpression AttributeId="employee.role">
            <AttributeDesignator AttributeId="http://wso2.org/claims/role" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"></AttributeDesignator>
         </AttributeAssignmentExpression>
      </AdviceExpression>
   </AdviceExpressions>
</Policy> 



[R2] Mediator code to build filter query

@Override
public boolean mediate(MessageContext ctx) {

  try {
  // Retrieve advices from the message context
  String rawXML = ctx.getProperty("adviceXml").toString();

   
  OMElement advice = AXIOMUtil.stringToOM(rawXML);

  Iterator<OMElement> advicesIter = advice
.getFirstChildWithName(new QName("", "Result"))
.getFirstChildWithName(new QName("", "AssociatedAdvice"))
.getFirstChildWithName(new QName("", "Advice"))
.getChildren();

   String sqlfilter = " ";

   // Build filter query.  In this sample we will use the role names as they are.
   while (advicesIter.hasNext()) {

 OMElement elem = advicesIter.next();

 String filterColumnName = elem.getAttribute(
 new QName("", "AttributeId")).getAttributeValue();
         sqlfilter += filterColumnName + "='" + elem.getText() + "'";

 if (advicesIter.hasNext()) {

 sqlfilter += " OR ";
 }
}

ctx.setProperty("DSSFilter", sqlfilter);

    } catch (XMLStreamException e) {
    
    }

  return true;

}



[R3[ ESB flow

<proxy name="getSalesInfo"
  transports="https http"
  startOnLoad="true"
  trace="disable">
  
  <description/>
  
  <target inSequence="InSeq" outSequence="OutSeq">
     <endpoint>
     <address uri="http://localhost:9765/services/customerservice"/>
     </endpoint>
   </target>
   </proxy>

   <sequence name="OutSeq">
      <send/>
      <log level="full"/>
   </sequence>
   
<sequence name="InSeq">
      <entitlementService remoteServiceUrl="https://localhost:9444/services/" remoteServiceUserName="admin" remoteServicePassword="enc:kuv2MubUUveMyv6GeHrXr9il59ajJIqUI4eoYHcgGKf/BBFOWn96NTjJQI+wYbWjKW6r79S7L7ZzgYeWx7DlGbff5X3pBN2Gh9yV0BHP1E93QtFqR7uTWi141Tr7V7ZwScwNqJbiNoV+vyLbsqKJE7T3nP8Ih9Y6omygbcLcHzg=">
         <onReject>
            <makefault version="soap12">
               <code xmlns:soap12Env="http://www.w3.org/2003/05/soap-envelope"
                     value="soap12Env:Receiver"/>
               <reason value="UNAUTHORIZED"/>
               <node/>
               <role/>
               <detail>XACML Authorization failed</detail>
            </makefault>
         </onReject>
         <onAccept>
            <send>
               <endpoint>
                  <address uri="http://localhost:9765/services/customerservice"/>
               </endpoint>
            </send>
         </onAccept>
         <obligations/>
         <advice/>
      </entitlementService>

   </sequence>

References

[a] Blog : Guide to write XACML policies in WSO2 Identity Server 2.0
http://blog.facilelogin.com/2009/06/guide-to-write-xacml-policies-in-wso2.html

[b] Managing Entitlement
http://docs.wso2.org/display/IS450/Managing+Entitlement

No comments:

Post a Comment