JBoss specific JAAS module

A Simplicité® application is a JEE application which relies on standard application server authentication methods (JAAS).

The following example applies to version 4.0 and 4.2 of JBoss application server.

By default Simplicité® authenticates its users against its own database using the login and password stored in the m_user table (and accessible at the logical level using the User and UserPwd business objects).

For the JBoss application server the default JAAS module descriptor is packaged as a Service ARchive (SAR) that includes the descriptor (called login-config.xml) of the JAAS module using, by default the one included in the authSimplicite-x.y.z.jar or the specifically overridden one put in the auth folder.

The default descriptor is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policy
   PUBLIC "-//JBoss//DTD JBOSS Security Config 3.0//EN"
   "http://www.jboss.org/j2ee/dtd/security_config.dtd">

<policy>
    <application-policy name="myapp">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
                <module-option name="dsJndiName">java:/myapp</module-option>
                <module-option name="principalsQuery">select usr_password from m_user where usr_login=? and (usr_active is null or usr_active='1')</module-option>
                <module-option name="rolesQuery">select 'simplicite', 'Roles' from m_user where usr_login=? and (usr_active is null or usr_active='1')</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

It relies on a standard JAAS module provided by JBoss (optionally it can be altered to use MD5 encryption for passwords), alternatively you can also use any other standard JAAS module provided by JBoss.

But you may need to have a very specific authentication procedure, is such a case you must write your own JAAS module. The rest of this article does not aim at being a JAAS tutorial but only gives a simple example of an alternative JAAS module.

This one authenticate a user against a corporate LDAP and then checks that the user actually exists in the m_user table with an active status:

package com.simplicite.auth;

import java.security.acl.Group;
import java.security.Principal;
import javax.security.auth.login.LoginException;
import javax.sql.*;
import java.sql.*;
import java.util.Properties;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import org.jboss.security.SimpleGroup;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

public class MyLdapLoginModule extends UsernamePasswordLoginModule {
    public MyLdapLoginModule() {
    }

    private transient SimpleGroup userRoles = new SimpleGroup("Roles");

    protected String getUsersPassword() throws LoginException {
        return "";
    }

    protected Group[] getRoleSets() throws LoginException {
        Group[] roleSets = { userRoles };
        return roleSets;
    }

    protected boolean validatePassword(String inputPassword, String expectedPassword) {
        boolean isValid = false;

        try {
            if ((inputPassword != null) && (inputPassword.length() < 1)) throw new Exception("Missing password");
            if (!checkDBUser()) throw new Exception("User unknown in the database");
            createLdapInitContext(getUsername(), inputPassword);
            isValid = true;
        } catch (Throwable e) {
            super.setValidateError(e);
        }

        return isValid;
    }

    private boolean checkDBUser() throws Exception {
        boolean res = false;

        Context initContext = new InitialContext();
        Context envContext  = (Context) initContext.lookup("java:/comp/env");
        DataSource ds = (DataSource) envContext.lookup("jdbc/myapp");

        Connection conn = ds.getConnection();

        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("select count(*) from m_user where usr_login = '" + getUsername() + "' and usr_active = '1'");
        res = (rs.next() && (rs.getInt(1) > 0));
        rs.close();
        conn.close();

        return res;
    }

    private InitialLdapContext getLdapContext(String ldapUrl, String principal, Object credential) throws Exception {
        Properties env = new Properties();

        env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
        env.setProperty(Context.PROVIDER_URL, ldapUrl);

        env.setProperty(Context.SECURITY_PRINCIPAL, principal);
        env.put(Context.SECURITY_CREDENTIALS, credential);

        return new InitialLdapContext(env, null);
    }

    private void createLdapInitContext(String username, Object credential) throws Exception {
        String ldapUrl = (String) options.get("ldapUrl");

        InitialLdapContext ctx = getLdapContext(ldapUrl, "", "");
        String userDN = null;

        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        controls.setReturningAttributes(null);
        controls.setTimeLimit(10000);
        Object[] filterArgs = { username };

        NamingEnumeration answer = ctx.search("ou=myorgunit,o=myorg", "uid={0}", filterArgs, controls);
        if (answer.hasMore()) {
            SearchResult sr = (SearchResult) answer.next();
            userDN = sr.getName().toString() + ",ou=myorgunit,o=myorg";
        }

        ctx.close();

        ctx = getLdapContext(ldapUrl, userDN, credential);

        Principal p = super.createIdentity("simplicite");
        userRoles.addMember(p);

        ctx.close();
    }
}

The corresponding descriptor is as follows (it provides the LDAP URL as a module parameter):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policy PUBLIC
    "-//JBoss//DTD JBOSS Security Config 3.0//EN"
    "http://www.jboss.org/j2ee/dtd/security_config.dtd">

<policy>
<application-policy name="myapp">
    <authentication>
        <login-module code="com.simplicite.auth.MyLdapLoginModule" flag="required">
            <module-option name="ldapUrl">ldap://myldapurl:389</module-option>
        </login-module>
    </authentication>
</application-policy>
</policy>