Tuesday, September 24, 2013

Write a simple JDBC PIP attribute finder module for WSO2 Identity Server

With this post I am going to discuss on how you can implement a simple JDBC PIP attribute finder module for WSO2 IS. I am using the latest released IS version ( WSO2 IS version 4.5.0 ) which you can download from here.

To have your own customized PIP module, the main task is to implement an attribute finder. It is not that hard since we already have the modeling interfaces. You can simply extend the AbstractPIPAttributeFinder ( abstract class) or implement PIPAttributeFinder ( interface ) to create your attribute finder.

I will provide step by step guide on how to
  • Create your own attribute finder
  • Register your PIP module in WSO2 IS
  • Test your attribute finder

Create your own attribute finder


I am going to create a JDBC attribute finder where the attributes required are stored in a database. I am going to use mysql for this sample.

I will add a sample code for our attribute finder and I'm going to address it as JDBCAttributeFinder.

package org.wso2.identity.samples.entitlement.pip.jdbc;

import org.apache.commons.dbcp.BasicDataSource;
import org.wso2.carbon.identity.entitlement.pip.AbstractPIPAttributeFinder;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * This is sample implementation of PIPAttributeFinder in Wso2 Entitlement Engine Here we are
 * calling to a external user base to find given attribute Assume that user store is reside on mysql
 * database
 */
public class JDBCAttributeFinder extends AbstractPIPAttributeFinder {

    /**
     * DBCP connection pool is used to create connection to database
     */
    private BasicDataSource dataSource;

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

    /**
     * initializes the Attribute finder module. creates a connection with JDBC database and the
     * retrieve attribute names from following sample table
     * +--------------+----------------+-----------------+---------+
     * | ATTRIBUTE_ID | ATTRIBUTE_NAME | ATTRIBUTE_VALUE | USER_ID |
     * +--------------+----------------+-----------------+---------+
     * | 1 | EmailOfUser | asela@gmail.com | 1 |
     * | 2 | EmailOfUser | bob@gmail.com | 2 |
     * | 3 | EmailOfUser | peter@gmail.com | 3 |
     * | 4 | CountryOfUser | SL | 1 |
     * | 5 | CountryOfUser | USA | 2 |
     * | 6 | CountryOfUser | UK | 3 |
     * | 7 | AgeOfUser | 23 | 1 |
     * | 8 | AgeOfUser | 19 | 2 |
     * | 9 | AgeOfUser | 31 | 3 |
     * +--------------+----------------+-----------------+---------+
     *
     *
     * @throws Exception throws when initialization is failed
     */
    public void init(Properties properties) throws Exception {
        /**
         * JDBC connection parameters
         */
        String dbUrl = properties.getProperty("databaseUrl");
        String driver = properties.getProperty("driverName");
        String userName = properties.getProperty("userName");
        String password = properties.getProperty("password");
/**
 * SQL statement to retrieve all attributes from database
 */
        String sqlStmt = "SELECT * FROM UM_USER_ATTRIBUTE";

        Connection connection = null;
        PreparedStatement prepStmt = null;
        ResultSet resultSet = null;

        dataSource = new BasicDataSource();
        dataSource.setUrl(dbUrl);
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);

        try {
            connection = dataSource.getConnection();
            if (connection != null) {
                prepStmt = connection.prepareStatement(sqlStmt);
                resultSet = prepStmt.executeQuery();
                while (resultSet.next()) {
                    String name = resultSet.getString(2);
                    supportedAttributes.add(name);
                }
            }
        } catch (SQLException e) {
            throw new Exception("Error while initializing JDBC attribute Finder", e);
        }
    }

    /**
     * This returns the name of the module
     * @return Returns a String that represents the module name
     */
    public String getModuleName() {
        return "JDBCPIPAttributeFinder";
    }

    /**
     * This returns the Set of Strings the attributeId that are retrieved
     * in initialization
     *
     * @return Set of String
     */
    public Set getSupportedAttributes() {
        return supportedAttributes;
    }

    /**
     * This is the overloaded simplify version of the getAttributeValues() method. Any one who extends the
     * AbstractPIPAttributeFinder can implement this method and get use of the default
     * implementation of the getAttributeValues() method which has been implemented within
     * AbstractPIPAttributeFinder class
     *
     * @param subject Name of the subject the returned attributes should apply to.
     * @param resource The name of the resource the subject is trying to access.
     * @param action  The name of the action the subject is trying to execute on resource
     * @param environment The name of the environment the subject is trying to access the resource
     * @param attributeId The unique id of the required attribute.
     * @param issuer The attribute issuer.
     *
     * @return Returns a Set of Strings that represent the attribute
     *         values.
     * @throws Exception throws if fails
     */
     public Set getAttributeValues( String subject, String resource, String action, String environment, String attributeId, String issuer) throws Exception {

        String sqlStmt = "select ATTRIBUTE_VALUE from UM_USER_ATTRIBUTE where ATTRIBUTE_NAME='" + attributeId + "' and USER_ID=(select USER_ID from UM_USER where USER_NAME='" + subject + "');";

        Set values = new HashSet();
        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);
        }
        return values;
    }
}


You need to create your PIP module by using this class.


Register your PIP module in WSO2 IS


I will provide the action that you should follow to get your module running.
  • Build your module and copy the jar to CARBON_HOME/repository/components/lib
  • Copy the JDBC driver to CARBON_HOME/repository/components/lib ( here the mysql-connector )
  • Register your attribute finder by adding it to CARBON_HOME/repository/conf/security/entitlement.properties as follow ( make sure that you change the dbUserName and userPassword )

PIP.AttributeDesignators.Designator.2=org.wso2.identity.samples.entitlement.pip.jdbc.JDBCAttributeFinder
org.wso2.identity.samples.entitlement.pip.jdbc.JDBCAttributeFinder.1=databaseUrl,jdbc:mysql://localhost:3306/piptestdb
org.wso2.identity.samples.entitlement.pip.jdbc.JDBCAttributeFinder.2=userName,dbUserName
org.wso2.identity.samples.entitlement.pip.jdbc.JDBCAttributeFinder.3=password,userPassword
org.wso2.identity.samples.entitlement.pip.jdbc.JDBCAttributeFinder.4=driverName,com.mysql.jdbc.Driver

I am attaching the db script that can be used to generate data which is required for this sample, the sample module and the mysql-connector jar to make your job more easy


Test your attribute finder


Given below is a sample policy which can be used to test your new JDBC attribute finder.
You can find the uploaded policy here.

 
 
 
 
 foo
 
 
 
 
 
 
 
 
 
 
 bar
 
 
 
 
 
 
 
 
 
 
 
 18
 
 
 
 
 
 30
 
 
 
 
 





This policy says that only the users whose age is between 18 and 30 can access the resource “foo” and perform action “bar”

-  Copy the above sample policy to an xml file, start WSO2 IS and upload the policy file through Policy Administrator.
         Follow Policy Administration > Add New Entitlement Policy > Import Existing Policy

-  Enable the policy through Policy View


-  Publish the policy through Policy Administrator.



-  Click on Tryit and send a request. ( given below is a sample request which you can download from here)






foo




Bob




bar





Now you would see that your newly created attribute finder has come into play :)

There are some configuration changes if you are trying a 3.x.x version. This would help you to identify those changes if you are using an earlier version.

2 comments:

  1. Hi,

    I just tried what you suggested in WSO IS 4.6, and it looks like the policy that you have gives an error when imported into WSO2.

    ReplyDelete
  2. Hi Jim,

    My apologies for not noticing the comment. Did you try using the policy that I have attached here? Seems the pasted one has some issues due to blogger template. But the attached one should work with IS 4.6.0

    Thanks.

    ReplyDelete