Wednesday, April 3, 2013

How to write Custom JDBC user store manager with WSO2IS 4.1.1 alpha

Download id 4.1.1- alpha here
Lets think about real world business scenario, Simple company has their own database with Customer name and the password. now they need to do some security implementation with WSO2 Identity Server and they cannot duplicate those data as well as cannot change their db schema.

That means we cannot take those users and insert into the WSO2 IS database as well as they cannot change their DB schema to our WSO2 IS database schema. Don't worry WSO2 Identity server has capability to solve this issue. lets look at the solution with mysql DB.

Steps:
1. Plug the external db as secondary user store.
2. Create some roles in Identity Server and assign the users (coming from external DB).
3. Give the permission to the role.
4. Access the Identity server.

Custom DB Schema
[sourcecode language="sql"]
CREATE TABLE CUSTOMER_DATA (
CUSTOMER_ID INTEGER NOT NULL AUTO_INCREMENT,
CUSTOMER_NAME VARCHAR(255) NOT NULL,
PASSWORD VARCHAR(255) NOT NULL,
PRIMARY KEY (CUSTOMER_ID),
UNIQUE(CUSTOMER_NAME)
);
[/sourcecode]


1. First we have to write down the Custom JDBCUserStoreManager to compatible with the above schema. So you need to create a class with any name that you preferred and extends JDBCUserStoreManager. I have created class call SampleJDBCUserStoreManager.java (download mvn project here).

In my custom class I'm not going to override all methods in JDBCUserStoreManager because this user store is going to plug as read only mode.

2. Let's look at the master-datasource.xml
I have created two data source because I'm going to connect to the two user stores as primary and secondary. you bettor read my early blog post call (Multiple User Stores configuration in WSO2 Identity Server) but there is lot of api changes in 4.1.1 alpha.
[sourcecode language="xml"]
<datasources-configuration xmlns:svns="http://org.wso2.securevault/configuration">

<providers>
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
</providers>

<datasources>

<datasource>
<name>WSO2_CARBON_DB</name>
<description>The datasource used for registry</description>
<jndiConfig>
<name>jdbc/WSO2CarbonDB</name>
</jndiConfig>
<definition type="RDBMS">
<configuration>
<url>jdbc:mysql://localhost:3306/WSO2_CARBON</url>
<username>root</username>
<password>root</password>
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<maxActive>50</maxActive>
<maxWait>60000</maxWait>
<testOnBorrow>true</testOnBorrow>
<validationQuery>SELECT 1</validationQuery>
<validationInterval>30000</validationInterval>
</configuration>
</definition>

</datasource>

<datasource>
<name>Sample_DB</name>
<description>The datasource used for user manager</description>
<jndiConfig>
<name>jdbc/SampleUserStore</name>
</jndiConfig>
<definition type="RDBMS">
<configuration>
<url>jdbc:mysql://localhost:3306/custom_users</url>
<username>root</username>
<password>root</password>
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<maxActive>50</maxActive>
<maxWait>60000</maxWait>
<testOnBorrow>true</testOnBorrow>
<validationQuery>SELECT 1</validationQuery>
<validationInterval>30000</validationInterval>
</configuration>
</definition>
</datasource>

</datasources>

</datasources-configuration>
[/sourcecode]

3. Go to user-mgt.xml
Here I have define two user store manages and the first definition pic as primary user store manager form the Identity server.
The secondary one is the custom user store manager that i have written early.
[sourcecode language="xml"]

<UserManager>
<Realm>
<Configuration>
<AddAdmin>true</AddAdmin>
<AdminRole>admin</AdminRole>
<AdminUser>
<UserName>admin</UserName>
<Password>admin</Password>
</AdminUser>
<EveryOneRoleName>everyone</EveryOneRoleName> <!-- By default users in this role sees the registry root -->
<Property name="dataSource">jdbc/WSO2CarbonDB</Property>
<Property name="MultiTenantRealmConfigBuilder">org.wso2.carbon.user.core.config.multitenancy.SimpleRealmConfigBuilder</Property>
<!-- Use the following MultiTenantRealmConfigBuilder with LDAP based UserStoreManagers-->
<!--Property name="MultiTenantRealmConfigBuilder">org.wso2.carbon.user.core.config.multitenancy.CommonLDAPRealmConfigBuilder</Property-->
</Configuration>

<UserStoreManager class="org.wso2.carbon.user.core.jdbc.JDBCUserStoreManager">
<Property name="ReadOnly">false</Property>
<Property name="dataSource">jdbc/WSO2CarbonDB</Property>
<Property name="MaxUserNameListLength">100</Property>
<Property name="Disabled">false</Property>
<Property name="IsEmailUserName">false</Property>
<Property name="DomainCalculation">default</Property>
<Property name="PasswordDigest">SHA-256</Property>
<Property name="StoreSaltedPassword">true</Property>
<Property name="ReadGroups">true</Property>
<Property name="WriteGroups">true</Property>
<Property name="UserNameUniqueAcrossTenants">false</Property>
<Property name="PasswordJavaRegEx">^[\S]{5,30}$</Property>
<Property name="PasswordJavaScriptRegEx">^[\S]{5,30}$</Property>
<Property name="UsernameJavaRegEx">^[^~!#$;%^*+={}\\|\\\\&lt;&gt;,\'\"]{3,30}$</Property>
<Property name="UsernameJavaScriptRegEx">^[\S]{3,30}$</Property>
<Property name="RolenameJavaRegEx">^[^~!#$;%^*+={}\\|\\\\&lt;&gt;,\'\"]{3,30}$</Property>
<Property name="RolenameJavaScriptRegEx">^[\S]{3,30}$</Property>
<Property name="UserRolesCacheEnabled">true</Property>
<Property name="MaxRoleNameListLength">100</Property>
<Property name="MaxUserNameListLength">100</Property>
</UserStoreManager>

<UserStoreManager class="org.wso2.carbon.jdbc.sample.SampleJDBCUserStoreManager">
<Property name="ReadOnly">false</Property>
<Property name="dataSource">jdbc/SampleUserStore</Property>
<Property name="MaxUserNameListLength">100</Property>
<Property name="Disabled">false</Property>
<Property name="IsEmailUserName">false</Property>
<Property name="DomainCalculation">default</Property>
<Property name="PasswordDigest">SHA-256</Property>
<Property name="StoreSaltedPassword">true</Property>
<Property name="ReadGroups">true</Property>
<Property name="WriteGroups">true</Property>
<Property name="UserNameUniqueAcrossTenants">false</Property>
<Property name="PasswordJavaRegEx">^[\S]{5,30}$</Property>
<Property name="PasswordJavaScriptRegEx">^[\S]{5,30}$</Property>
<Property name="UsernameJavaRegEx">^[^~!#$;%^*+={}\\|\\\\&lt;&gt;,\'\"]{3,30}$</Property>
<Property name="UsernameJavaScriptRegEx">^[\S]{3,30}$</Property>
<Property name="RolenameJavaRegEx">^[^~!#$;%^*+={}\\|\\\\&lt;&gt;,\'\"]{3,30}$</Property>
<Property name="RolenameJavaScriptRegEx">^[\S]{3,30}$</Property>
<Property name="UserRolesCacheEnabled">true</Property>
<Property name="MaxRoleNameListLength">100</Property>
<Property name="MaxUserNameListLength">100</Property>
<Property name="DomainName">sample.com</Property>

<Property name="SelectUserSQL">SELECT * FROM CUSTOMER_DATA WHERE CUSTOMER_NAME=?</Property>
<Property name="UserFilterSQL">SELECT CUSTOMER_NAME FROM CUSTOMER_DATA WHERE CUSTOMER_NAME LIKE ? ORDER BY CUSTOMER_ID</Property>
</UserStoreManager>

<AuthorizationManager
class="org.wso2.carbon.user.core.authorization.JDBCAuthorizationManager">
<Property name="AdminRoleManagementPermissions">/permission</Property>
<Property name="AuthorizationCacheEnabled">true</Property>
</AuthorizationManager>
</Realm>
</UserManager>

[/sourcecode]

You can see I'm passing the custom SQLs as "SelectUserSQL" and "UserFilterSQL"

4. You need to put following jars in to the $IS_HOME/repository/component/libs
mysql-connector-java-5.1.7-bin.jar and Sample_user_store-1.0.jar

5. Now I'm going to start the Identity server and sign in as admin

6. Go to users then you can see the users coming from secondary user store as read only
is

7. Now we need to create the internal role because we cannot create the roles inside of the secondary user store manager due to read only mode.So you can do it as follows.

is1

you have to give the permission to the created role

is2

Now you can assign the users from secondary user store.

is

8. Login to the IS from the secondary users.

is

1 comment: