Thursday, December 22, 2016

Self User Registration feature WSO2 Identity Server 5.3.0.

In this blog post, I am explaining about the self-registration feature in WSO2 Identity Server 5.3.0 release which will be released soon.


Self User Registration 


In previous releases of Identity Server (IS 5.0.0, 5.1.0, 5.2.0), it can be used UserInformationRecovery Soap Service for self-registration feature.

You can follow this for more information about the soap service and how it can be configured.

Rest API support for Self-registration is available in IS 5.3.0 release.

UserInformationRecovery Soap APIs is also available in IS 5.3.0 release for supporting backward compatibility. You can try the Rest service through Identity Server login page (https://localhost:9443/dashboard)


You can't test the SOAP service through the login page. It can be tested using the user info recovery sample


How to configure self-registration rest API


  1. Verify following configurations in <IS_HOME>/repository/conf/identity/identity.xml file
    • <EventListener ype="org.wso2.carbon.user.core.listener.UserOperationEventListener"name="org.wso2.carbon.identity.mgt.IdentityMgtEventListener" orderId="50" enable="false"/>
    • <EventListener type="org.wso2.carbon.user.core.listener.UserOperationEventListener" name="org.wso2.carbon.identity.governance.listener.IdentityStoreEventListener" orderId="97" enable="true">
    • <EventListener type="org.wso2.carbon.user.core.listener.UserOperationEventListener" name="org.wso2.carbon.identity.scim.common.listener.SCIMUserOperationListener  orderId="90" enable="true"/>
  2. Configure email setting in <IS_HOME>/repository/conf/output-event-adapters.xml file. 
  3. Start the WSO2 IS server and login to the management console.
  4. Click on Resident found under the Identity Providers section on the Main tab of the management console.
  5. Expand the Account Management Policies tab, then the Password Recovery tab and configure the following properties as required.
  6. Enable account lock feature to support self-registration with email confirmation feature




Once the user is registered, a notification will be sent to the user's email account if the
"Enable Notification Internally Management" property is true.

Note: If it is not required to lock user once the registration is done, it is required disable both 
Enable Account Lock On Creation and Enable Notification Internally Management properties. Otherwise it will send a confirmaiton mail to the users email account.


APIs

  • Register User
This API is used to create the user in Identity Server. You can try this from login page. (https://localhost:9443/dashboard/)

Click Register Now button and submit the form with data. Then it will send a notification and lock the user based on the configuration. 
  • Resend Code
This is used to resend the confirmation mail again.

You can try this from login page. First, register a new user and try to login to the Identity Server using the registered user credentials without click on the email link received via Identity Server for confirming the user. Then, you will see following in the login page. Click Re-Send button to resend the confirmation link.



  • Validate Code
This API will be used to validate account confirmation link sent in the email. 

Monday, October 3, 2016

Password History Extension for WSO2 Identity Server 5.2.0




WSO2 Identity Server 5.2.0 was released in last month (September 2016). You can download the Identity Sever 5.2.0 from here.

It supports a lot of Identity and Access Management features OOTB and you can find them from here.
Currently, In Identity Server 5.2.0 version does not support password history validation feature OOTB.  (This feature will be supported OOTB in next release which is planned in December 2016).
Although this feature is not supported OOTB, it can be supported easily through an extension. In this blog, I have implemented a sample  which will support following features for IS 5.2.0.


  • Password cannot have been used in previous 'n' password changes
  • Password cannot have been previously used in past 'm' hours. 

Here the 'n' and 'm' should be configurable parameters. 


You can go through following steps to add password history feature in IS 5.2.0.
  1. Download Identity Server 5.2.0 from here
  2. Go through the installation guide and install Java and Maven.
  3. Download the Extention source code from here.
  4. Goto inside password_history folder and run the command "mvn clean install"
  5. Copy password_history/target/org.wso2.custom-1.0.0.jar file to <IS_HOME>/repository/components/dropins folder
  6. password_history/src/main/resources/dbScripts directory contains following db scripts files. Run the relevant configuration file based on your database configured in identity.xml file.
    • db2.sql  
    • informix.sql 
    • mysql.sql    
    • oracle.sql
    • h2.sql   
    • mssql.sql     
    • oracle_rac.sql  
    • postgresql.sql
  7. Copy password_history/src/main/resources/password-history-identity-mgt.properties file into <IS_HOME>/repository/conf/Identity directory. It has following configrable parameters and configure them according to the requirements.
    • #If true, password history feature will be enabled
    • PasswordHistory.Enable=true

    • #Password cannot have been used in the previous 'X' password changes
    • PasswordHistory.Count=5

    • #Password cannot have been previously used in the past 24 hours
    • PasswordHistory.Time=24

    • #Password Digest Algorithm
    • PasswordHistory.hashingAlgorithm=SHA-256

    • #Password History data store extension point
    • PasswordHistory.dataStore=org.wso2.custom.store.impl.DefaultPasswordHistoryDataStore
  8. Start Identity Server
  9. Then you are done. You can try the feature by adding user and updating credentials.

Tuesday, March 8, 2016

How to Write a Custom User Store Manager - WSO2 Identity Server 5.1.0

WSO2 Identity Sever OOTB support following user stores.
  • 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



Thursday, February 18, 2016

WSO2 Identity Server 5.1.0 behind Proxy(Load Balancer)

WSO2 Identity Server behind Proxy or Load Balancer.

In this blog, I am going to explain step by step guide to how to configure WSO2 Identity Server 5.1.0 with a proxy port and proxy host.

Configuring Proxy Port


By default WSO2 Identity Server is running on 9443 port. Here I am going to explain the way of configuring a proxy port of 443.

  • Open <wso2is-5.1.0>/repository/conf/tomcat/catalina-server.xml file and add the proxy port 443 in https connector as follows.
        
<Connector protocol="org.apache.coyote.http11.Http11NioProtocol"

           port="9443"

           proxyPort="443"

Note: It is not possible to configure proxy port from load balancer itself since there is a post request while authenticating to IS Dashboard. So, If you are planning to use Identity server Dashboard, this configuration is a must.




Configuring Proxy Host


1. Use the same hostname in both Identity Server and Loadbalancer
  • Open <wso2is-5.1.0>/repository/conf/carbon.xml file and configure the hostname and management hostname as follows


    <HostName>wso2.is.com</HostName>

    <MgtHostName>wso2.is.com</MgtHostName>


  • Create a new Keystore with the new hostname. Following is the keytool command to create new

keytool -genkey -alias wso2carbon -keyalg RSA -keysize 1024 -dname "CN=wso2.is.com,OU=Home,O=Home,L=SL,S=WS,C=LK" -keypass wso2carbon -keystore wso2carbon.jks -storepass wso2carbon


Create new two keystores for client-trustore.jks and wso2carbon.jks. 

You can follow [1] for more information on how to configure keystores in WSO2 servers. 

  • Configure Nginx configuration as follows


upstream ssl.wso2.as.com {
    server wso2.is.com:9443;
}

server {
listen 443;
    server_name wso2.is.com;
    client_max_body_size 100M;

    ssl on;
    ssl_certificate /etc/ssl/nginx/nginx-repo.crt;
    ssl_certificate_key /etc/ssl/nginx/nginx-repo.key;

    location / {
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;


                proxy_redirect https://ssl.wso2.as.com https://wso2.is.com;

                proxy_pass https://ssl.wso2.as.com;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
    }

}


You need to change the path of certificate and certificate_key. 


  • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/jaggeryapps/dashboard/conf/site.json file as follows


{
    "proxy" : {
        "proxyHost" : "wso2.is.com",
        "proxyHTTPSPort" : "443",
        "proxyContextPath" : "",
        "servicePath" : "/services"
    }
}

  • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/jaggeryapps/portal/conf/site.json file as follows
{

{
    "proxy" : {
        "proxyHost" : "wso2.is.com",
        "proxyHTTPSPort" : "443",
        "proxyContextPath" : ""
    },
    "fido" : {
        "appId" : ""
    }
}

  • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/webapps/shindig/WEB-INF/web.xml

<context-param>
        <param-name>system.properties</param-name>
        <param-value>
            <![CDATA[
        shindig.host=wso2.is.com
        shindig.port=443
        aKey=/shindig/gadgets/proxy?container=default&url=
     ]]>

  • Import the load balancer certificate into client-trustore.jks file.


Note: Load balancer certificate should be IS hostname. 



2. Configure a proxyName in catalina-server.xml file. 




If you want to use a proxyname which is diffrent from the Identity Server hostname, you can do it configuring catalina-server.xml file.

If your load balancer hostname is is.wso2.com and Identity Server hostname a-s00001572, following configurations need to be done inorder to work Identity Server behind that proxy name and port.


  • Open <wso2is-5.1.0>/repository/conf/carbon.xml file and configure the hostname and management hostname as follows


    <HostName>a-s00001572</HostName>

    <MgtHostName>a-s00001572</MgtHostName>



    • Create a new Keystore with the new hostname. Following is the keytool command to create new

    keytool -genkey -alias wso2carbon -keyalg RSA -keysize 1024 -dname "a-s00001572,OU=Home,O=Home,L=SL,S=WS,C=LK" -keypass wso2carbon -keystore wso2carbon.jks -storepass wso2carbon
    


    Create new two keystores for client-trustore.jks and wso2carbon.jks. 

    You can follow [1] for more information on how to configure keystores in WSO2 servers. 


    • Configure Nginx configuration as follows

    upstream ssl.wso2.as.com {
        server A-S00001572:9443;
    }
    
    server {
    listen 443;
        server_name is.wso2.com;
    
        client_max_body_size 100M;
    
        ssl on;
        ssl_certificate /etc/ssl/nginx/nginx-repo.crt;
        ssl_certificate_key /etc/ssl/nginx/nginx-repo.key;
    
        location / {
                    proxy_set_header X-Forwarded-Host $host;
                    proxy_set_header X-Forwarded-Server $host;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
                    proxy_redirect https://A-S00001572:9443 https://A-S00001572;
    
    
                    proxy_pass https://ssl.wso2.as.com;
                    proxy_http_version 1.1;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "upgrade";
        }
    
    }


    You need to change the path of certificate and certificate_key.

    • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/jaggeryapps/dashboard/conf/site.json file as follows


    {
        "proxy" : {
            "proxyHost" : "wso2.is.com",
            "proxyHTTPSPort" : "443",
            "proxyContextPath" : "",
            "servicePath" : "/services"
        }
    }
    

    • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/jaggeryapps/portal/conf/site.json file as follows
    {

    {
        "proxy" : {
            "proxyHost" : "wso2.is.com",
            "proxyHTTPSPort" : "443",
            "proxyContextPath" : ""
        },
        "fido" : {
            "appId" : ""
        }
    }
    

    • Configure proxy port and host in <wso2is-5.1.0> /repository/deployment/server/webapps/shindig/WEB-INF/web.xml

    <context-param>
            <param-name>system.properties</param-name>
            <param-value>
                <![CDATA[
            shindig.host=wso2.is.com
            shindig.port=443
            aKey=/shindig/gadgets/proxy?container=default&url=
         ]]>
    

    • Import the load balancer certificate into client-trustore.jks file.


    Note : Load balancer certificate should be IS hostname. 


    Running the Server. 



    Now you are done. You will be able to log into Identity Server Management Console and Dashboard web app from following URLs. 


    Management Console : https://wso2.is.com/carbon/
    Dashboard :https://wso2.is.com/dashboard



    [1] https://docs.wso2.com/display/Carbon440/Configuring+Keystores+in+WSO2+Products