Using Java EE Security Manager with WildFly

One feature of Java EE not known by every developer is the Java EE Permission declaration. As a matter of fact, every Java EE product must be now capable of running with a Java security manager that enforces Java security permissions, and that prevents application components from executing operations for which they have not been granted the required permissions.

What is a Permission?

In Java terms, a permission represents access to a system resource. When running in a Security Manager context, in order for a resource access to be allowed the corresponding permission must be explicitly granted to the code attempting the access. A sample policy file entry that grants code from the /home/jboss directory read access to the file /tmp/abc is

grant codeBase "file:/home/jboss/" {
   permission java.io.FilePermission "/tmp/abc", "read";
};

Let’s see now how to apply the same concepts also to a Managed environment, like the application server.

Running JBoss / WildFly with a Security Manager

The first step for using a Security Manager in the applicaiton server is activating it. In order to do that, you can either set the -secmgr flag to the startup script or set the SECMGR variable to true, by uncommenting in your standalone.conf the following line:

# Uncomment this to run with a security manager enabled
SECMGR="true"

Start the Application server. Now try to deploy a sample Servlet which tries to save a files on the disk:

PrintWriter writer = new PrintWriter("file.txt", "UTF-8");
writer.println("The first line");
writer.println("The second line");
writer.close();

Once you deploy the Servlet, you will manage to meet the Security Manager!

java.security.AccessControlException: WFSM000001: Permission check failed (permission “(“java.io.FilePermission” “file.txt” “write”)” in code source “(vfs:/content/Security.war/WEB-INF/classes )” of “null”)

So how do we grant permission to write to our application. Mostly like we do with a Java S2E application, we declare the list of permissions in a file. This file, named permission.xml needs to be placed in the META-INF folder of your application:

<permissions xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
             http://xmlns.jcp.org/xml/ns/javaee/permissions_7.xsd"
	version="7">

	<permission>
		<class-name>java.io.FilePermission</class-name>
		<name>*</name>
		<actions>read,write</actions>
	</permission>

</permissions>

Notice the read,write action, which will authorize us to perform read and write actions on a File for every class which is part of the Deployment. Deploy the application and check that it works now.

Another kind of resource which is typically shielded by the Security Manager is reading/writing a System Property. You can check by yourself that reading a System Property now returns a Security Exception:

String home = System.getProperty("java.home");
2016-10-10 09:33:26,306 ERROR [io.undertow.request] (default task-59) UT005023: Exception handling request to /Security/test: java.security.AccessControlException: WFSM000001: Permission check failed (permission "("java.util.PropertyPermission" "java.home" "read")" in code source "(vfs:/content/Security.war/WEB-INF/classes )" of "null")

Just like we did for the File, we can let an application read/write a System Property via a permission block:

<permission>
	<class-name>java.util.PropertyPermission</class-name>
	<name>*</name>
	<actions>read,write</actions>
</permission>

Coding Permissions in the configuration file

Besides adding a deployment descriptor, it is also possible to define security policies at Subsystem level. This implies that Security checks could be valid for multiple applications using a particular class. Out of the box, the application server contains a subsystem named security-manager which defines just the upper limit of Permission policies which can be deployed on the application server:

<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
    <deployment-permissions>
        <maximum-set>
            <permission class="java.security.AllPermission"/>
        </maximum-set>
    </deployment-permissions>
</subsystem>

In order to define permissions for a specific class, we need to add an entry to the minimum-permission element: the following CLI command, reproduces the same permission you needed to read/write on a file:

/subsystem=security-manager/deployment-permissions=default:write-attribute(name=minimum-permissions,value=[{class="java.io.FilePermission",name="*",actions="read,write"}])

The result, will be the following security-manager configuration:

<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
    <deployment-permissions>
        <minimum-set>
            <permission class="java.io.FilePermission" name="*" actions="read,write"/>
        </minimum-set>
        <maximum-set>
            <permission class="java.security.AllPermission"/>
        </maximum-set>
    </deployment-permissions>
</subsystem>

Restricting permissions at module level

Finally, it is worth mentioning that permissions which you have defined via permission.xml or the configuration file, can be narrowed through the module.xml file which contains a set of classes which are loaded by the application server. For example, let’s say we want to impose a write restriction at module level for our Servlet, then within the module.xml file of the javax.servlet.api module we would need to code the following permissions block:

<module xmlns="urn:jboss:module:1.3" name="javax.servlet.api">
    <resources>
        <resource-root path="jboss-servlet-api_3.1_spec-1.0.0.Final.jar"/>
    </resources>
    <permissions>
 
       <grant permission="java.io.FilePermission" name="/home/jboss/wildfly-10.1.0.Final/modules/system/layers/base/javax/servlet/api/main/jboss-servlet-api_3.1_spec-1.0.0.Final.jar" actions="read"/>

   </permissions>
</module>

As you can see from the following log, if you try to violate the restriction imposed by the module permission, a different error log, related to the specificmodule will be issued:

2016-10-10 09:38:51,295 ERROR [io.undertow.request] (default task-28) UT005023: Exception handling request to /Security/test: java.security.AccessControlException: WFSM000001: Permission check failed (permission "("java.io.FilePermission" "file-name.txt" "write")" in code source "(jar:file:/home/jboss/wildfly-10.1.0.Final/modules/system/layers/base/javax/servlet/api/main/jboss-servlet-api_3.1_spec-1.0.0.Final.jar!/ )" of "null")

Securing a Web application with LDAP and Keycloak

In the second tutorial about LDAP and WildFly we will learn how to create an LDAP based User Federation configured on Keycloak.

If you are new to Keycloak please refer to the base documentation: http://keycloak.jboss.org/docs. We have also provided two tutorials which could be a good start as well:

Keycloak can federate external user databases. Out of the box there’s support for LDAP and Active Directory.

Shortcut!

If you want to get started quickly, simply import the Ldap-demo Realm which is available on GitHub: https://github.com/keycloak/keycloak/blob/master/examples/ldap/ldaprealm.json.

Next move to “Configure your Web application” section.

Configuring LDAP User Federation

In order to configure a federated LDAP store go to the Keycloak admin console: http://localhost:8080/auth/admin

You should start creating a new Realm to be used by your applications:

Next, select your Realm and click on the Users Federation menu option to get you to the User Federation page. When you get to this page, there is an “Add Provider” select box. You should see “ldap” within this list. Selecting “ldap” will bring you to the ldap configuration page. We will add here the ApacheDS LDAP settings, which are the same we have covered in the first tutorial:

Verify at fist that the connection is OK by clicking on the Test connection. Next verify also the authentication against the default ApacheDS bind DN: “uid=admin,ou=system

Mappers

Out of the box, Keycloak is configured to import only username, email, first and last name, but you are free to configure mappers and add more attributes or delete default ones. It supports password validation via LDAP/AD protocols and different user metadata synchronization modes.

Here is the full list of attributes for our schema:

Clients configuration

Complete your configuration by generating a Client which will let you generate and install the configuration on your application. From the Clients menu, click on create an choose a Base URL for your application, such as “ldap-portal“.

Save your configuration. Next step will be Exporting the client configuration in JSON or XML format. Click on the ldap-app:

Select the Installation tab and the Format Option (in our case JSON). Once done, click on Download and save it locally with a name such as keycloak.json.

Configure your Web application

Configuring your Web application requires two simple steps:

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <module-name>ldap-portal</module-name>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>LDAPApp</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ldap-user</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK</auth-method>
        <realm-name>does-not-matter</realm-name>
    </login-config>

    <security-role>
        <role-name>ldap-user</role-name>
    </security-role>
</web-app>

Now test an Test the example. In your web browser point to http://localhost:8080/ldap-portal . You can login either as jbrown with password password or as bwilson with password password . You can see that access token contains all the claims and role mappings corresponding to the LDAP data provided in LDIF.

How to create a Custom JBoss Login Module

This tutorial is a simple walk through the creation of a custom Login module with JBoss EAP 6 / WildFly application server.

Note: If you want to develop a custom login module on the latest security infrastructure (Elytron) we recommend checking also this tutorial: How to create a custom Elytron Realm

Getting Started with PicketBox Login Modules

PicketBox framework ships with a set of ready to run login modules which can be used for a variety of contexts such as File based, Database or LDAP authentication. Sometimes you will need to create your own Login Module and this tutorial will guide you through the steps needed to do it.

The starting point will be one the existing login modules, in our case we will extend the org.jboss.security.auth.spi.UsernamePasswordLoginModule that is equipped with PicketLink module:

package com.mastertheboss;

import java.security.acl.Group;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;

import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

 
public class BasicLoginModule extends UsernamePasswordLoginModule {

    public void initialize(Subject subject, CallbackHandler callbackHandler,
            Map sharedState,
            Map options) {

        super.initialize(subject, callbackHandler, sharedState, options);
    }

    /**

     * (required) The UsernamePasswordLoginModule modules compares the result of this

     * method with the actual password.

     */

    @Override
    protected String getUsersPassword() throws LoginException {

        System.out.format("MyLoginModule: authenticating user '%s'\n", getUsername());

        // Lets pretend we got the password from somewhere and that it's, by a chance, same as the username

        String password = super.getUsername();

        // Let's also pretend that we haven't got it in plain text but encrypted

        // (the encryption being very simple, namely capitalization)

        password = password.toUpperCase();

        return password;

    }

    @Override
    protected boolean validatePassword(String inputPassword, String expectedPassword) {

        // Let's encrypt the password typed by the user in the same way as the stored password

        // so that they can be compared for equality.

        String encryptedInputPassword = (inputPassword == null)? null : inputPassword.toUpperCase();

        System.out.format("Validating that (encrypted) input psw '%s' equals to (encrypted) '%s'\n"

                , encryptedInputPassword, expectedPassword);

		// Password check strategy: find the password from your storage (e.g. DB) and check that it's equal		

        // with inputPassword. We always return true, meaning password check will be skipped

        return true;

    }

    /**

     * (required) The groups of the user, there must be at least one group called

     * "Roles" (though it likely can be empty) containing the roles the user has.

     */

    @Override

    protected Group[] getRoleSets() throws LoginException {

        SimpleGroup group = new SimpleGroup("Roles");

        try {

        	System.out.println("Search here group for user: "+super.getUsername());

            group.addMember(new SimplePrincipal("Manager"));

        } catch (Exception e) {

            throw new LoginException("Failed to create group member for " + group);

        }

        return new Group[] { group };

    }

   

}

Here, the initialize method is used to gather some module options which can be included in your login module.

The most important parts are the validatePassword method and the getRoleSets.

  • validatePassword does the password checking job, as commented in the code
  • getRoleSets finds the role for the user, once that password checking was successfull. In this example we have granted the Manager role to the user.

Packaging the Module

Next, package the module in an archive say LoginModule.jar and deploy it as a module on the application server.

Here is the module.xml file which we will use for the module named basicloginmodule:

<module xmlns="urn:jboss:module:1.1" name="basicloginmodule">

  <resources>
    <resource-root path="LoginModule.jar"/>
  </resources>


  <dependencies>

    <module name="org.picketbox"/>

    <module name="javax.api"/>

  </dependencies>

</module>

Now last step will be including the security domain for this module in your server configuration:

<security-domain name="simple-auth" cache-type="default">

    <authentication>

        <login-module code="com.mastertheboss.BasicLoginModule" flag="required" module="login"/>

    </authentication>

</security-domain>

Configuring a MongoDB Login Module

Creating a Login Module with JBoss AS 7 or WildFly can be done by extending some of the available PicketBox Login modules. See this tutorial for a quick introduction to Custom Login modules: Creating a Custom JBoss Login Module. Here we will learn how to create a custom Login Module which used MongoDB for performing JAAS authentication.

First of all, start your MongoDB instance:

mongod

 Now connect with your Mongo shell:

mongo

 We will create a single document in a collection named “jbosslogin”. This document contains the username, the password and the group to which the user belongs:

db.jbosslogin.insert({name:'user1',password:'Password1',group:'Manager'})

 Expected outcome from the shell:

Ok, this will be enough for a simple test. Now download MongoDB JDBC driver which is available here. Once downloaded, start your favourite IDE and create the following Login Module class:

package com.mastertheboss;

import java.net.UnknownHostException;
import java.security.acl.Group;

import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;

import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

import com.mongodb.*;
/**
 * 
 * @author Francesco Marchioni
 * 
 */
public class MongoLoginModule extends UsernamePasswordLoginModule {
	String userGroup;

	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map sharedState, Map options) {
		super.initialize(subject, callbackHandler, sharedState, options);
	}

	/**
	 * (required) The UsernamePasswordLoginModule modules compares the result of
	 * this method with the actual password.
	 */
	@Override
	protected String getUsersPassword() throws LoginException {
		System.out.format("MyLoginModule: authenticating user '%s'\n",
				getUsername());
		String password = super.getUsername();
		password = password.toUpperCase();
		return password;
	}

	/**
	 * (optional) Override if you want to change how the password are compared
	 * or if you need to perform some conversion on them.
	 */
	@Override
	protected boolean validatePassword(String inputPassword,
			String expectedPassword) {

		String encryptedInputPassword = (inputPassword == null) ? null
				: inputPassword.toUpperCase();
		System.out.format("Validating that (encrypted) input psw '%s' equals to (encrypted) '%s'\n",
						encryptedInputPassword, expectedPassword);
		MongoClient mongoClient = null;
		try {
			mongoClient = new MongoClient("localhost");
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		DB db = mongoClient.getDB("test");
		DBCollection coll = db.getCollection("jbosslogin");
		BasicDBObject query = new BasicDBObject("name", getUsername());

		DBCursor cursor = coll.find(query);

		try {
			if (cursor.hasNext()) {
                            // User found in DB
   			    BasicDBObject obj = (BasicDBObject) cursor.next();

				String password = (String) obj.get("password");
				userGroup = (String) obj.get("group");
				if (inputPassword.equals(password)) {
					System.out.println("Password matching");
					return true;
				}
			} else {
				System.out.println("User not found!");
				return false;
			}
		} finally {
			cursor.close();
		}

		return false;
	}

	@Override
	protected Group[] getRoleSets() throws LoginException {
		SimpleGroup group = new SimpleGroup("Roles");
		try {
            // userGroup picked up by MongoDB Cursor earlier
			group.addMember(new SimplePrincipal(userGroup));
		} catch (Exception e) {
			throw new LoginException("Failed to create group member for "
					+ group);
		}
		return new Group[] { group };
	}

}

As you can see, the most interesting part is the validatePassword method which checks on the DB if the password entered by the user matches with the password found in the DB. After the password check, an Array of Group is returned, containing the group found in the BasicDBObject cursor.

The following libraries will be needed in order to compile this project:

Save this class and pack it in a JAR file, let’s say mongo-loginmodule.jar. We will now provide it to the application server as a module.

Creating the module on the application server

We will create the following structure under the modules folder, which includes the mongo-loginmodule.jar, the Mongo JDBC driver and a module.xml file:

C:\Wildfly-8.2.0.Final\modules
”œ”€”€”€mongologin
”‚   ”””€”€”€main
”‚           LoginModule.jar
”‚           module.xml
”‚           mongo-java-driver-2.13.0-rc2.jar

Within the module.xml we will declare the list of resources contained in the module and the module name as well:

<module xmlns="urn:jboss:module:1.1" name="mongologin">
  <resources>
    <resource-root path="LoginModule.jar"/>
    <resource-root path="mongo-java-driver-2.13.0-rc2.jar" />
  </resources>

  <dependencies>
    <module name="org.picketbox"/>
    <module name="javax.api"/>
  </dependencies>
</module>

Declaring the Security Domain in the application server configuration

Last step will be including a Security domain in the application server configuration which uses the module we have just installed:

<subsystem xmlns="urn:jboss:domain:security:1.2">
	<security-domains>
		<security-domain name="mongo-auth" cache-type="default">
			<authentication>
				<login-module code="com.mastertheboss.MongoLoginModule" flag="required" module="mongologin"/>
			</authentication>
		</security-domain>
. . . .
	</security-domains>
</subsystem>

That’s all. In order to use the login module in your application, include the security domain named “mongo-auth”. For example, in a Web application, include in your jboss-web.xml configuration file the following:

<jboss-web>
    <security-domain>mongo-auth</security-domain>
</jboss-web>

Further improvements

You can further improve the MongoDB Login module by specifying the Database and collection name as module options:

<subsystem xmlns="urn:jboss:domain:security:1.2">
	<security-domains>
		<security-domain name="mongo-auth" cache-type="default">
			<authentication>
				<login-module code="com.mastertheboss.MongoLoginModule" flag="required" module="mongologin">
                                     <module-option name="database" value="test"/>
                                     <module-option name="collection" value="jbosslogin"/>
                                </login-module>
			</authentication>
		</security-domain>
. . . .
	</security-domains>
</subsystem>

You will need to include two additional properties in your LoginModule named database and collection, which will replace the hardcoded values we have used so far:

public class MongoLoginModule extends UsernamePasswordLoginModule {
	String userGroup;

        String database;
        String collection;

. . .
	@Override
	protected boolean validatePassword(String inputPassword,
			String expectedPassword) {

		. . .
		DB db = mongoClient.getDB(database);
		DBCollection coll = db.getCollection(collection);
		BasicDBObject query = new BasicDBObject("name", getUsername());

		DBCursor cursor = coll.find(query);
        . . .
        }

}

Securing JBoss applications using the ApplicationRealm

JBoss AS 7 and the EAP 6 provide out of the box a Security Domain which can be used for securing your applications. Let’s see how to use it in a few simple steps.

What is JBoss Application Realm?

When applications are deployed to the application server they are associated with a security domain within the security subsystem. The “other” security domain is provided to work with the ApplicationRealm, this domain is defined with a pair of login modules named respectively: “Remoting” and “RealmDirect” (“RealmUsersRoles” for AS7 ).

  • The Remoting login module is used to check if the request currently being authenticated is a request received over a Remoting connection, if so the identity that was created during the authentication process is used and associated with the current request.
  • The RealmDirect when the request did not arrive over a Remoting connection, the application server makes use of this login module and then use the realm to load the users roles.

The advantage of this approach is that all of the backing store configuration can be left within the realm with the security domain just delegating to the realm.

Differences between JBoss AS 7.1.1 and EAP 6.1

The main difference between AS7 and the EAP 6 is that JBoss AS 7 uses the RealmUsersRoles as basic mechanism for the Application/Management realm and contains the user/roles file definitions as options:

<security-domain name="other" cache-type="default">

   . . . . .
    <login-module code="RealmUsersRoles" flag="required">
       <module-option name="usersProperties" value="${jboss.server.config.dir}/application-users.properties"/>
       <module-option name="rolesProperties" value="${jboss.server.config.dir}/application-roles.properties"/>
       <module-option name="unauthenticatedIdentity" value="guest"/>
       <module-option name="password-stacking" value="useFirstPass"/>
    </login-module>

</security-domain>

On the other hand, the EAP 6 uses the RealmDirect which delegates the file definitions to the ApplicationRealm:

<security-domain name="other" cache-type="default">

   . . . . . 
   <login-module code="RealmDirect" flag="required">
       <module-option name="password-stacking" value="useFirstPass"/>
   </login-module>

</security-domain>

That being said, let’s see how to secure a simple Web application using the ApplicationRealm. Start by creating an user using the add-user script:

What type of user do you wish to add?
 a) Management User (mgmt-users.properties)
 b) Application User (application-users.properties)
(a): b
Enter the details of the new user to add.
Realm (ApplicationRealm) :
Username : admin1234
Password :
Re-enter Password :
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: Manager
About to add user 'admin1234' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'admin1234' to file 'C:\jboss\jboss-eap-6.1\standalone\configuration\application-users.properties'
Added user 'admin1234' with roles Manager to file 'C:\jboss\jboss-eap-6.1\standalone\configuration\application-roles.properties'

As you can see, we have added an user named admin1234 as part of the Manager group. This information has been stored into the files application-user.properties and application-roles.properties.

Let’s configure a web application to use this security domain. Let’s start with web.xml:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>HtmlAuth</web-resource-name>
            <description>application security constraints
       </description>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Manager</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>ApplicationRealm</realm-name>
    </login-config>

    <security-role>
        <role-name>Manager</role-name>
    </security-role>

And here’s the jboss-web.xml section, which binds the application to the Security domain named “other”:

<jboss-web> 
      <security-domain>java:/jaas/other</security-domain>
</jboss-web>

As it is, the Web application will issue a BASIC authentication window once you try to access your Web application.

Configuring Single Signon on JBoss AS 7

This tutorial describes how to configure Single Signon for a JBoss AS 7 Web application (standalone and clustered).

 The Single Signon configuration allows a centralized login configuration for corporate sites that use different Web context. In order to cofigure single signon on JBoss AS 7 we need to operate on two configuration points:

  • The web subsystem where we are going to add a sso element in the virtual-server definition
  • The jboss-web.xml deployment file which defines the SingleSignOn Valve to be used

Here’s the configuration for a standalone non-clustered JBoss AS instance:

 <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
            <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
            <virtual-server name="default-host" enable-welcome-root="true">
                <alias name="localhost"/>
                <sso domain="localhost" reauthenticate="false"/>
            </virtual-server>
</subsystem>

Notice the sso stanza we have added and the reauthenticate attribute.

reauthenticate attribute is a flag to determine whether each request needs to be reauthenticated to the securityRealm. Setting to true can allow web applications with different security-domain configurations to share an SSO. Default isfalse.

Please note that you can add this information via CLI as well:
/subsystem=web/virtual-server=default-host/sso=configuration:add(reauthenticate="false")

Next declare in the jboss-web.xml the Valve which will be used to handle SSO. Every request will go through this valve and it will act according to what you specified in reauthenticate flag.

<jboss-web>
    <security-domain>sso</security-domain>
          <valve>
        <class-name>org.apache.catalina.authenticator.SingleSignOn</class-name>
    </valve>
</jboss-web>

Remember that you need to define the security domain mentioned in jboss-web.xml into your security subsystem. For example supposing you are using the file based security login module:

<security-domain name="sso" cache-type="default">
    <authentication>
        <login-module code="UsersRoles" flag="required">
           <module-option name="usersProperties" value="${jboss.server.config.dir}/users.properties"/>
           <module-option name="rolesProperties" value="${jboss.server.config.dir}/roles.properties"/>
        </login-module>
    </authentication>
</security-domain>

Now configure define security constraints in your web application, for example here we are securing the application so that just the Manager role can access it through a FORM login:

 
    <security-constraint>
        <web-resource-collection>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Manager</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>Sample Realm</realm-name>
        <form-login-config>
            <form-login-page>/jsp/login.jsp</form-login-page>
            <form-error-page>/jsp/login-error.jsp</form-error-page>
        </form-login-config>
    </login-config>

 
    <security-role>
        <role-name>Manager</role-name>
    </security-role>

    
And a corresponding login.jsp page:

    <form id="login_form" name="login_form" method="post"
            action="j_security_check" enctype="application/x-www-form-urlencoded">
            <center>
                 
                <p>Please login to proceed.</p>
            </center>

            <div style="margin-left: 15px;">
                <p>
                    <label for="username"> Username</label><br /> <input id="username"
                        type="text" name="j_username" size="20" />
                </p>
                <p>
                    <label for="password"> Password</label><br /> <input id="password"
                        type="password" name="j_password" value="" size="20" />
                </p>
                <center>
                    <input id="submit" type="submit" name="submit" value="Login"
                        class="buttonmed" />
                </center>
            </div>
        </form>

Now if you try to replicate the same web configuration in another Web application you should notice that once logged in the first application, you should be able to enter in the second web application without additional credentials.

 

Single Signon Domain configuration

 

If you are going to run your Single Signon application in a Domain configuration you need to provide some information about the domain name and (if clustered) the Infinispan cache you are using to store data.

<subsystem xmlns="urn:jboss:domain:web:1.1" native="true" default-virtual-server="default-host">

    <connector name="ajp" protocol="AJP/1.3" scheme="http" socket-binding="ajp" redirect-port="8443" enabled="true"/>
    <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>

    <virtual-server name="default-host" enable-welcome-root="true">
        <alias name="localhost"/>
        <sso cache-container="web" cache-name="sso" domain="myDomain" reauthenticate="true"/>
    </virtual-server>

</subsystem>

We then need to define your infinispan cache to be used for SingleSignon:

<subsystem xmlns="urn:jboss:domain:infinispan:1.1" default-cache-container="cluster">
    ...
    <replicated-cache name="sso" mode="SYNC" batching="true"/>
        <distributed-cache name="dist" mode="ASYNC" batching="true">
           <file-store/>
        </distributed-cache>
    ...
</subsystem>

Finally, here the Web server Valve needed for a clustered application:

<jboss-web>
    <security-domain>sso</security-domain>
       <valve>
           <class-name>org.jboss.as.web.sso.ClusteredSingleSignOn</class-name>
       </valve>
</jboss-web> 

Configuring JBoss with PicketBox LDAP Module

In this tutorial we will show how to connect WildFly / JBoss EAP to OpenLDAP directory service using PicketBox Security Framework. For a more recent configuration (using Elytron) we recommend checking this article: How to configure an Elytron LDAP Realm on WildFly

Installing LDAP

OpenLDAP is a free suite of client and server tools that implement the Lightweight Directory Access Protocol (LDAP) for Linux/Windows. Strictly speaking, though, LDAP isn’t a database at all, but a protocol used to access information stored in an information directory (also known as an LDAP directory).
OpenLDAP is available at http://www.openldap.org/software/download/
If you are looking for a Windows version, you can find it here: http://www.userbooster.de/en/download/openldap-for-windows.aspx

OpenLDAP installation guide for Linux can be found here: http://www.openldap.org/doc/admin24/quickstart.html. (Windows users can simply execute the OpenldapforWindows.exe which will guide you through an intuitive wizard.)

Once installed open the slapd.conf file and let’s customize the top level of the LDAP directory tree. In particular we will change the domain component (dc) to acme.com (replace it with your actual domain).

suffix        "dc=acme,dc=com"
rootdn        "cn=Manager,dc=acme,dc=com"

Once done with it, you can start LDAP using:

su root -c /usr/local/libexec/slapd  # Linux

slapd.exe # Windows

Now in order to connect LDAP with JBoss AS, you need to define one user and assign it to one role. For this purpose we will create one user named admin and assign it to the role Manager.

In this tutorial we have used the free tool JXExplorer to connect to OpenLDAP and load the ldif file.

dn: dc=acme,dc=com
objectclass: top
objectclass: dcObject
objectclass: organization
dc: acme
o: MCC

dn: ou=People,dc=acme,dc=com
objectclass: top
objectclass: organizationalUnit
ou: People

 
dn: uid=admin,ou=People,dc=acme,dc=com
objectclass: top
objectclass: uidObject
objectclass: person
uid: admin
cn: Manager
sn: Manager
userPassword: secret


dn: ou=Roles,dc=acme,dc=com
objectclass: top
objectclass: organizationalUnit
ou: Roles

 
dn: cn=Manager,ou=Roles,dc=acme,dc=com
objectClass: top
objectClass: groupOfNames
cn: Manager
description: the acmeAS7 group
member: uid=admin,ou=People,dc=acme,dc=com

Once loaded the LDIF file, try reconnecting to OpenLDAP using the following credentials:

You should see the following directory structure, containing one user (admin) and one role (Manager):

Configuring JBoss LDAPAuth

Next, to use LDAP for Authentication, you can use the LdapExtended Login module, entering the values of the bindDN and bindCredential contained in slapd.conf. You need to specify as well which organization unit contains the users, through the baseCtxDN option and as well the organization which contains the roles through the rolesCtxDN. Additionally you need to specify the following properties:
The baseFilter option is a search filter used to locate the context of the user to authenticate.
The roleFilter is as well a search filter used to locate the roles associated with the authenticated user.
The searchScope sets the search scope to one of the strings. ONELEVEL_SCOPE searches directly under the named roles context.
Finally the allowEmptyPasswords: It is a flag indicating if empty(length==0) passwords should be passed to the LDAP server.

Here’s the configuration to be added as security-domain for a JBoss AS 7 / JBoss EAP 6 installation:

<security-domain name="LDAPAuth">
    <authentication>
      <login-module code="LdapExtended" flag="required">
        <module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
        <module-option name="java.naming.provider.url" value="ldap://localhost:389"/>
        <module-option name="java.naming.security.authentication" value="simple"/>
        <module-option name="bindDN" value="uid=admin,dc=acme,dc=com"/>
        <module-option name="bindCredential" value="secret"/>
        <module-option name="baseCtxDN" value="ou=People,dc=acme,dc=com"/>
        <module-option name="baseFilter" value="(uid={0})"/>
        <module-option name="rolesCtxDN" value="ou=Roles,dc=acme,dc=com"/>
        <module-option name="roleFilter" value="(member={1})"/>
        <module-option name="roleAttributeID" value="cn"/>
        <module-option name="searchScope" value="ONELEVEL_SCOPE"/>
        <module-option name="allowEmptyPasswords" value="true"/>
      </login-module>
    </authentication>
</security-domain>

If you are running a JBoss 4/5/6 security domain, here’s the corresponding configuration:

<application-policy name="LDAPAuth">
    <authentication>
      <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required" >
         <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
         <module-option name="java.naming.provider.url">ldap://localhost:389</module-option>
         <module-option name="java.naming.security.authentication">simple</module-option>
         <module-option name="bindDN">uid=admin,dc=acme,dc=com</module-option>
         <module-option name="bindCredential">secret</module-option>
         <module-option name="baseCtxDN">ou=People,dc=acme,dc=com</module-option>
         <module-option name="baseFilter">(uid={0})</module-option>

         <module-option name="rolesCtxDN">ou=Roles,dc=acme,dc=com</module-option>
         <module-option name="roleFilter">(member={1})</module-option>
         <module-option name="roleAttributeID">cn</module-option>
         <module-option name="searchScope">ONELEVEL_SCOPE</module-option>
         <module-option name="allowEmptyPasswords">true</module-option>
      </login-module>
    </authentication>
</application-policy>    

Done with the application server configuration, last step will be enabling security at application level; supposing you are going to secure a web application, the first step will be declaring the protected resources in your web.xml file, which are allowed just to the Manager role:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 

http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>HtmlAuth</web-resource-name>
            <description>application security constraints
</description>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>Manager</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>LDAPAuth realm</realm-name>
    </login-config>
    <security-role>
        <role-name>Manager</role-name>
    </security-role>
</web-app>

And here the jboss-web.xml configuration file which links the application to the LDAPAuth security domain. This file needs to be placed into the WEB-INF folder of your web application:

<jboss-web>     
   <security-domain>java:/jaas/LDAPAuth</security-domain> 
</jboss-web>