This site contains the documentation that is relevant to older WSO2 product versions and offerings.
For the latest WSO2 documentation, visit https://wso2.com/documentation/.

Writing a Custom User Store Manager

This page demonstrates the process of writing a simple custom user store manager for WSO2 products and specifically in WSO2 Identity Server. The items you need to be aware of are as follows:

You can download the sample here.

The sample scenario

Consider a scenario where a company has a simple user store which contains the customer_id, customer_name and password (for the moment let's not worry about salting etc., as the purpose of this scenario is to demonstrate getting a custom user store into action). The company may want to keep this as it is, as there may be other services depending on this and still require to have identities managed. Obviously it is not a good practice to duplicate this sort of sensitive data to another database to be used by the Identity Server as the cost of securing both databases is high and can potentially lead to conflicts. This is where a custom User Store Manager comes handy, with the high extensibility of the Carbon platform.

This scenario is demonstrated with only basic authentication.

By default, WSO2 products have four implementations of User Store Managers which are listed below:

  • 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

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");

There are only two sample entries in this user store. Our aim is to ensure that these already available users are also visible to the Identity Server. According to this scenario, only the basic authentication should ideally be supported by the User Store Manager.

Writing the custom user store manager

There are just three things to adhere to when writing the User Store Manager and the rest is done for us.

Implement the 'org.wso2.carbon.user.api.UserStoreManager' interface.

There are several other options to do this, i.e., by implementing 'org.wso2.carbon.user.core.UserStoreManager' interface or extending 'org.wso2.carbon.user.core.common.AbstractUserStoreManager' class as appropriate. In this case, as we are dealing with a JDBC User Store, the best option is to extend the existing JDBCUserStoreManager class and override the methods as required.

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 the OSGI framework

This is just simple step to make sure the new custom user store manager is available through the OSGI framework. With this step, the configuration of the new user store manager becomes easy with the UI in later steps. We just need to place the following class inside the project.

/**
 * @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

There needs to be this method 'getDefaultUserStoreProperties()' as follows. The required properties are mentioned in the class 'CustomUserStoreConstants'. In the downloaded sample, it can be clearly seen how this is used.

@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 <PRODUCT_HOME>/repository/components/dropins and drop mysql-connector-java-<>.jar inside <PRODUCT_HOME>/repository/components/lib. Start the server with ./wso2carbon.sh from <PRODUCT_HOME>/bin. In the start-up logs you see the following log printed:

INFO {org.wso2.sample.user.store.manager.internal.CustomUserStoreMgtDSComponent} -  CustomUserStoreManager bundle activated successfully.

Configuring the Identity Server

In the management console, add the newly created custom user store as follows:

  1. Log in to the management console and click User Store Management sub menu under Configure menu.
  2. The User Store Management page opens. Initially, there are no secondary user stores.

  3. Click Add Secondary User Store.

  4. The User Store Manager page opens. In the User Store Manager Class dropdown you can see the created custom user store manager which is listed as an option to use as the implementation class. Select it and fill the properties according to the user store.

    The custom user store manager appears in the dropdown list only if it is registered before in the OSGI framework. 

    Additionally you can see the properties defined in the constants class.
     

  5. Ensure that all the mandatory fields are filled and a valid domain name is given and click Add. A message appears saying that the user stores are being added. Refresh the page after a few seconds to check the status. 

  6. If the new user store is successfully added, it will appear in the User Store Management page. After adding to the server, you can edit the properties of the new secondary user store and enable/disable it in a dynamic manner.

For more information on creating a secondary user store, see here. If our schema changes at any time, it can be edited here in a dynamic manner. 

The next step is to verify whether the users are there.

  1. Go to 'Users and Roles' in the management console. In the list of users, you can now see the user details which were taken directly from the custom user store.
  2. If you check the roles these users are assigned to, you would probably see them assigned to the 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.

Thus you have successfully configured WSO2 Identity Server to use your own custom user store.