XACML PIP with WSO2 Identity Server

Access control is a mechanism where we control and limit access to resources and information based on user privileges. Entitlement management is a process where it grants access privileges to users or resources after fine-grained authentication. WSO2 Identity Server uses XACML for providing this functionality. XACML is an OASIS standard which describes a policy language and an access control decision request/ response definition.

WSO2 Identity server supports XACML 3.0 and you can you the features available with it. I'm going to discuss about how to write a custom PIP for WSO2 Identity Server. 
I'm going to use a scenario where a bank account going to be updated based on an attribute resides on an external database. Here the customer can update his/ her own account. But if the user is other than the customer coming with the required permission in here it's "/permission/applications/saml2-banking-web-app.com/update_account/POST", then the custom PIP will retrieve the account attributes from the database and if the attribute value is "Standard" the request will get "Permit" response.

Here's the policy I have used to validate users against XACML customer function of permission tree evaluation and the attribute retrieved from the database. 


<Policy xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" PolicyId="pip_evaluate_permission_tree_policy" RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable" Version="1.0">
    <Description>
This policy provides the ability to authorize users based on the below mentioned permission and account type or the account owner
    </Description>
    <Target>
        <AnyOf>
            <AllOf>
                <Match MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">
/permission/applications/saml2-banking-web-app.com/update_account/POST
                    </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="false"/>
                </Match>
            </AllOf>
        </AnyOf>
    </Target>
    <Rule Effect="Permit" RuleId="permission-tree-authorized">
        <Condition>
            <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:or">
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:and">
                    <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:eval-permission-tree">
                        <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
                            <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"/>
                        </Apply>
                        <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-one-and-only">
                            <AttributeDesignator AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id" Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="true"/>
                        </Apply>
                    </Apply>
                    <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:any-of">
                        <Function FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-equal"/>
                        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">STANDARD</AttributeValue>
                        <AttributeDesignator Category="urn:oasis:names:tc:xacml:1.0:subject-category:resource" AttributeId="http://demo.xacml.com/id/account-type" DataType="http://www.w3.org/2001/XMLSchema#string" MustBePresent="false"/>
                    </Apply>
                </Apply>
                <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-is-in">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">customer</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="false"/>
                </Apply>
            </Apply>
        </Condition>
    </Rule>
    <Rule Effect="Deny" RuleId="deny_others"/>
</Policy>


First here's a code snippet depicts a custom PIP which I have used in my XACML policy. You have to implement the PIPAttributeFinder interface for that matter.



/**
 * This is sample implementation of PIPAttributeFinder in WSO2 entitlement engine. Here we are
 * calling to an external database to find given attribute;
 */
public class AccountJDBCAttributeFinder extends AbstractPIPAttributeFinder {


    private static final String ACCOUNT_TYPE = "http://demo.xacml.com/id/account-type";

 /**
  * Connection pool is used to create connection to database
  */
 private DataSource dataSource;

 /**
  * List of attribute finders supported by the this PIP attribute finder
  */
 private Set<String> supportedAttributes = new HashSet<String>();

    @Override
 public void init(Properties properties)  throws Exception{
        
        String dataSourceName = (String) properties.get("DataSourceName");

        if(dataSourceName == null || dataSourceName.trim().length() == 0){
            throw new Exception("Data source name can not be null. Please configure it in the entitlement.properties file.");
        }

        dataSource = InitialContext.doLookup(dataSourceName);

        supportedAttributes.add(ACCOUNT_TYPE);
    }

    @Override
    public String getModuleName() {
        return "Account Attribute Finder";
    }

    @Override
    public Set<String> getAttributeValues(String subjectId, String resourceId, String actionId,
                                          String environmentId, String attributeId, String issuer) throws Exception{

        /*
   * SQL statement to retrieve attribute value for given attribute id from database
   */
  String sqlStmt = "select ACCOUNT_TYPE from ACCOUNT_INFO where ACCOUNT_NO='" + actionId + "';";

  Set<String> values = new HashSet<String>();
  PreparedStatement prepStmt = null;
  ResultSet resultSet = null;
  Connection connection = null;

  try {
   connection = dataSource.getConnection();
   if (connection != null) {
    prepStmt = connection.prepareStatement(sqlStmt);
    resultSet = prepStmt.executeQuery();
    while (resultSet.next()) {
     values.add(resultSet.getString(1));
    }
   }
  } catch (SQLException e) {
   throw new Exception("Error while retrieving attribute values", e);
  }finally {
            try{
                if(resultSet != null){
                    resultSet.close();
                }
                if(prepStmt != null){
                    prepStmt.close();
                }
                if(connection !=  null){
                    connection.close();
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }

  return values;
 }
    
    @Override
 public Set<String> getSupportedAttributes() {
  return supportedAttributes;
 }
}

And you need to do add the following to the entitlement.properties file in <IS_HOME>/repository/conf/identity/ directory and do the necessary changes.


PIP.AttributeDesignators.Designator.2=org.xacmlinfo.xacml.pip.jdbc.AccountJDBCAttributeFinder
#Define JNDI datasource name as property value
org.xacmlinfo.xacml.pip.jdbc.AccountJDBCAttributeFinder.1=DataSourceName,jdbc/DEMODB



Comments

Popular Posts