- 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
- org.wso2.carbon.identity.user.store.remote.CarbonRemoteUserStoreManger
There are some cases, we have to write a custom implementation. Here I am explaining an step by step guide of how to write a custom users store manager for Identity Server 5.1.0.
Requirement: A company already has a user database and need to authenticate to Identity Server through that database.
Following is the schema of USER database.
CREATE TABLE TEST_USER ( USER_ID INT NOT NULL PRIMARY KEY, USER_NAME VARCHAR(100), ENCRYPTED_USER_PASSWORD VARCHAR(100), EMAIL_ADDRESS VARCHAR(240), EMPLOYEE_ID INT ); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (1, "testadmin", "testpass", "admin@act.org", 1000); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (2, "user1", "user1", "user1@act.org", 1001); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (3, "user2", "user2", "user2@act.org", 1002); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (4, "user3", "user3", "user3@act.org", 1003); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (5, "user4", "user4", "user4@act.org", 1004); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (6, "user5", "user5", "user5@act.org", 1005); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (7, "user6", "user6", "user6@act.org", 1006); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (8, "user7", "user7", "user7@act.org", 1007); INSERT INTO TEST_USER (USER_ID, USER_NAME, ENCRYPTED_USER_PASSWORD, EMAIL_ADDRESS, EMPLOYEE_ID) VALUES (9, "user8", "user8", "user8@act.org", 1008);
Writing Custom User Store Manager
- You can find the sample code from here.
- Our custom User store is a jdbc based user store and we can write it by extending the org.wso2.carbon.user.core.jdbc.JDBCUserStoreManager.
- We need to override the doAuthenticate method to authenticate using the new database.
@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("ENCRYPTED_USER_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; }
- We have to define custom SQL queries and we can make them as configurable by overriding getDefaultUserStoreProperties method as follows.
@Override public org.wso2.carbon.user.api.Properties getDefaultUserStoreProperties() { Properties properties = new Properties(); properties.setMandatoryProperties(CustomUserStoreManagerConstants.MANDATORY_PROPERTIES.toArray (new Property[CustomUserStoreManagerConstants.MANDATORY_PROPERTIES.size()])); properties.setOptionalProperties(CustomUserStoreManagerConstants.OPTIONAL_PROPERTIES.toArray (new Property[CustomUserStoreManagerConstants.OPTIONAL_PROPERTIES.size()])); properties.setAdvancedProperties(CustomUserStoreManagerConstants.ADVANCED_PROPERTIES.toArray (new Property[CustomUserStoreManagerConstants.ADVANCED_PROPERTIES.size()])); return properties; }
- We can set the mandatory, optional and advanced configuration as follows.
package com.wso2.carbon.custom.user.store.manager; import org.wso2.carbon.user.api.Property; import org.wso2.carbon.user.core.UserStoreConfigConstants; import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; import java.util.ArrayList; public class CustomUserStoreManagerConstants { public static final ArrayList<Property> MANDATORY_PROPERTIES = new ArrayList<Property>(); public static final ArrayList<Property> OPTIONAL_PROPERTIES = new ArrayList<Property>(); public static final ArrayList<Property> ADVANCED_PROPERTIES = new ArrayList<Property>(); static { setMandatoryProperty(JDBCRealmConstants.DRIVER_NAME, "Driver Name", "", "Full qualified driver name"); setMandatoryProperty(JDBCRealmConstants.URL, "Connection URL", "", "URL of the user store database"); setMandatoryProperty(JDBCRealmConstants.USER_NAME, "User Name", "", "Username for the database"); setMandatoryProperty(JDBCRealmConstants.PASSWORD, "Password", "", "Password for the database"); setProperty(UserStoreConfigConstants.disabled, "Disabled", "false", UserStoreConfigConstants.disabledDescription); setProperty("ReadOnly", "Read Only", "true", "Indicates whether the user store of this realm operates in the user read only mode or not"); setProperty(UserStoreConfigConstants.SCIMEnabled, "SCIM Enabled", "false", UserStoreConfigConstants.SCIMEnabledDescription); setAdvancedProperty("SelectUserSQL", "Select User SQL", "SELECT * FROM TEST_USER WHERE USER_NAME=?", ""); setAdvancedProperty("UserFilterSQL", "User Filter SQL", "SELECT USER_NAME FROM TEST_USER WHERE USER_NAME LIKE" + " ? ORDER BY USER_NAME", ""); setAdvancedProperty("IsUserExistingSQL", "Is User Existing SQL", "SELECT USER_NAME FROM TEST_USER WHERE " + "USER_NAME=? ", ""); } private static void setProperty(String name, String displayName, String value, String description) { Property property = new Property(name, value, displayName + "#" + description, null); OPTIONAL_PROPERTIES.add(property); } private static void setMandatoryProperty(String name, String displayName, String value, String description) { Property property = new Property(name, value, displayName + "#" + description, null); MANDATORY_PROPERTIES.add(property); } private static void setAdvancedProperty(String name, String displayName, String value, String description) { Property property = new Property(name, value, displayName + "#" + description, null); ADVANCED_PROPERTIES.add(property); } }
- Register Custom User Store Manager in OSGI framework
/** * @scr.component name="com.wso2.carbon.custom.user.store.manager.component" immediate="true" * @scr.reference name="realm.service" * interface="org.wso2.carbon.user.core.service.RealmService"cardinality="1..1" * policy="dynamic" bind="setRealmService" unbind="unsetRealmService" */ public class CustomUserStoreManagerServiceComponent { private static Log log = LogFactory.getLog(CustomUserStoreManagerServiceComponent.class); private static RealmService realmService; protected void activate(ComponentContext ctxt) { Hashtable<String, String> props = new Hashtable<String, String>(); CustomUserStoreManager customUserStoreManager = new CustomUserStoreManager(); ctxt.getBundleContext().registerService(UserStoreManager.class.getName(), customUserStoreManager, props); log.info("CustomUserStoreManager bundle activated successfully.."); } protected void deactivate(ComponentContext ctxt) { if (log.isDebugEnabled()) { log.info("CustomUserStoreManager bundle is deactivated"); } } protected void setRealmService(RealmService realmService) { log.debug("Setting the Realm Service"); CustomUserStoreManagerServiceComponent.realmService = realmService; } protected void unsetRealmService(RealmService realmService) { log.debug("UnSetting the Realm Service"); CustomUserStoreManagerServiceComponent.realmService = null; } public static RealmService getRealmService() { return realmService; } }
Identity Server Configurations.
- Compile the custom user store manager code and then you will get com.wso2.carbon.custom.user.store.manager-1.0.0.jar OSGI bundlle.
- Copy com.wso2.carbon.custom.user.store.manager-1.0.0.jar into <IS_HOME>/repository/components/dropins folder.
- Configure new database in <IS_HOME>//repository/conf/datasources/master-datasources.xml file as follows
<datasource> <name>CustomUserDB</name> <description>Custom User Database</description> <jndiConfig> <name>jdbc/CustomUserDB</name> </jndiConfig> <definition type="RDBMS"> <configuration> <url>jdbc:mysql://localhost:3306/Custom</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>
- Start Identity Server.
- Go to Add User button inside Home/Identity.
- Then you can fill the configurations as above image
- Then assign login permission to Internal/everyone role
- Try to login to management console using user1/user1