How to Write a Custom User Store Manager - WSO2 Identity Server 4.5.0
With this post I will be demonstrating writing a simple custom user store manager for WSO2 Carbon and specifically in WSO2 Identity Server 4.5.0 which is released recently. The Content is as follows,
- Use case
- Writing the custom User Store Manager
- Configuration in Identity Server
Use Case
By default WSO2 Carbon has four implementations of User Store Managers as follows.
- org.wso2.carbon.user.core.jdbc.JDBCUserStoreManager
- org.wso2.carbon.user.core.ldap.ReadOnlyLDAPUserStoreManager
- org.wso2.carbon.user.core.ldap.ReadWriteLDAPUserStoreManager
- org.wso2.carbon.user.core.ldap.ActiveDirectoryLDAPUserStoreManager
So this is the scenario I am to demonstrate with only basic authentication.
We have the following user store which is currently in use at the company.
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) ); INSERT INTO CUSTOMER_DATA (CUSTOMER_NAME, PASSWORD) VALUES("pushpalanka" ,"pushpalanka"); INSERT INTO CUSTOMER_DATA (CUSTOMER_NAME, PASSWORD) VALUES("lanka" ,"lanka");
I have only two entries in user store. :) Now what we want is to let these already available users to be visible to Identity Server, nothing less, nothing more. So it's only the basic authentication that User Store Manager should support, according to this scenario.
Writing the custom User Store Manager
There are just 3 things to adhere when we writing the User Store Manager and the rest will be done for us.- Implement the 'org.wso2.carbon.user.api.UserStoreManager' interface
CustomUserStoreManager extends JDBCUserStoreManager
@Override public boolean doAuthenticate(String userName, Object credential) throws UserStoreException { if (CarbonConstants.REGISTRY_ANONNYMOUS_USERNAME.equals(userName)) { log.error("Anonymous user trying to login"); return false; } Connection dbConnection = null; ResultSet rs = null; PreparedStatement prepStmt = null; String sqlstmt = null; String password = (String) credential; boolean isAuthed = false; try { dbConnection = getDBConnection(); dbConnection.setAutoCommit(false); sqlstmt = realmConfig.getUserStoreProperty(JDBCRealmConstants.SELECT_USER); prepStmt = dbConnection.prepareStatement(sqlstmt); prepStmt.setString(1, userName); rs = prepStmt.executeQuery(); if (rs.next()) { String storedPassword = rs.getString("PASSWORD");
if ((storedPassword != null) && (storedPassword.trim().equals(password))) { isAuthed = true; } } } catch (SQLException e) { throw new UserStoreException("Authentication Failure. Using sql :" + sqlstmt); } finally { DatabaseUtil.closeAllConnections(dbConnection, rs, prepStmt); } if (log.isDebugEnabled()) { log.debug("User " + userName + " login attempt. Login success :: " + isAuthed); } return isAuthed; }
- Register Custom User Store Manager in OSGI framework
/** * @scr.component name="custom.user.store.manager.dscomponent" immediate=true * @scr.reference name="user.realmservice.default" * interface="org.wso2.carbon.user.core.service.RealmService" * cardinality="1..1" policy="dynamic" bind="setRealmService" * unbind="unsetRealmService" */ public class CustomUserStoreMgtDSComponent { private static Log log = LogFactory.getLog(CustomUserStoreMgtDSComponent.class); private static RealmService realmService; protected void activate(ComponentContext ctxt) { CustomUserStoreManager customUserStoreManager = new CustomUserStoreManager(); ctxt.getBundleContext().registerService(UserStoreManager.class.getName(), customUserStoreManager, null); log.info("CustomUserStoreManager bundle activated successfully.."); } protected void deactivate(ComponentContext ctxt) { if (log.isDebugEnabled()) { log.debug("Custom User Store Manager is deactivated "); } } protected void setRealmService(RealmService rlmService) { realmService = rlmService; } protected void unsetRealmService(RealmService realmService) { realmService = null; } }
- Define the Properties Required for the User Store Manager
@Override public org.wso2.carbon.user.api.Properties getDefaultUserStoreProperties(){ Properties properties = new Properties(); properties.setMandatoryProperties(CustomUserStoreConstants.CUSTOM_UM_MANDATORY_PROPERTIES.toArray (new Property[CustomUserStoreConstants.CUSTOM_UM_MANDATORY_PROPERTIES.size()])); properties.setOptionalProperties(CustomUserStoreConstants.CUSTOM_UM_OPTIONAL_PROPERTIES.toArray (new Property[CustomUserStoreConstants.CUSTOM_UM_OPTIONAL_PROPERTIES.size()])); properties.setAdvancedProperties(CustomUserStoreConstants.CUSTOM_UM_ADVANCED_PROPERTIES.toArray (new Property[CustomUserStoreConstants.CUSTOM_UM_ADVANCED_PROPERTIES.size()])); return properties; }
The advanced properties carries the required SQL statements for the user store, written according to the custom schema of our user store.
Now all set to go. You can build the project with your customization to the sample project or just use the jar in the target. Drop the jar inside CARBON_HOME/repository/components/dropins and drop mysql-connector-java-<>.jar inside CARBON_HOME/repository/components/lib. Start the server with ./wso2carbon.sh from CARBON_HOME/bin. In the start-up logs you will see following log printed.
INFO {org.wso2.sample.user.store.manager.internal.CustomUserStoreMgtDSComponent} - CustomUserStoreManager bundle activated successfully.
Configuration in Identity Server
In the management console try to add a new user store as follows.In the shown space we will see our custom user store manager given as an options to use as the implementation class, as we registered this before in OSGI framework. Select it and fill the properties according to the user store.
Also in the property space we will now see the properties we defined in the constants class as below.
If our schema changes at any time we can edit it here in dynamic manner. Once finished we will have to wait a moment and after refreshing we will see the newly added user store domain, here I have named it 'wso2.com'.
So let's verify whether the user are there. Go to 'Users and Roles' and in Users table we will now see the users details who were there in the custom user store as below.
If we check the roles these users are assigned to Internal/everyone role. Modify the role permission to have 'login' allowed. Now if any of the above two users tried to login with correct credentials they are allowed.
So we have successfully configured Identity Server to use our Custom User Store without much hassel.
Cheers!
Ref: http://malalanayake.wordpress.com/2013/04/03/how-to-write-custom-jdbc-user-store-manager-with-wso2is-4-1-1-alpha/
Note: For the updated sample for Identity Server - 5.0.0, please use the link, https://svn.wso2.org/repos/wso2/people/pushpalanka/SampleCustomeUserStoreManager-5.0.0/