Glassfish  provides support for Custom Realms and Custom Login Modules that are based on the JAAS framework. This post explains how to write a simple Realm class and its corresponding LoginModule, configure them with an illustration of a simple web application that uses this realm.

Custom Realm

The Custom Realm should extend com.sun.appserv.security.AppservRealm. The Realm class is basically meant to provide user and group-related information. The methods to be implemented are

i) public void init(Properties properties )throws BadRealmException, NoSuchRealmException

This method is invoked during server startup when the realm is initially loaded.  The realm can do any initialization it needs in this method. The Properties is a set of key-value pairs configured while creating the Realm and are present in domain.xml. Among the other custom properties, there is a property jaas-context (which is explained later in this post). This property should be set using the call setProperty method implemented in the parent class. If the method returns without throwing an exception, the Enterprise Server assumes that the realm is ready to service authentication requests. If an exception is thrown, the realm is disabled.

ii) public String getAuthType() - This method returns a descriptive string representing the type of authentication done by this realm.

iii) public Enumeration getGroupNames(String user) throws InvalidOperationException, NoSuchUserException -
This method returns the group names the user belongs to as an Enumeration of Strings.

Custom LoginModule

The Custom LoginModule should extend com.sun.appserv.security.AppservPasswordLoginModule. This class should override the method

abstract protected void authenticateUser() throws LoginException

This method performs the actual custom authentication, by either using a database, or LDAP or a file or even a simple Hashtable as illustrated in the attached sample code. The custom login module must not implement any of the other methods, such as login(), logout(), abort(), commit(), or initialize(). Default implementations are provided in AppservPasswordLoginModule which hook into the Enterprise Server infrastructure.

The custom login module can access the following protected object fields, which it inherits from AppservPasswordLoginModule. These contain the user name, password of the user to be authenticated and the currentRealm class.

protected String _username;

protected String _password;

protected com.sun.enterprise.security.auth.realm.Realm  _currentRealm;

The authenticateUser() method should end with a call to the commitUserAuthentication(String[] authenticatedGroupList) method where the authenticatedGroupList is the list of groups the user belongs to.

As can be observed, the realm class is isolated from the LoginModule. The Realm is capable of capturing arbitrary configuration information and can help in obtaining the Group information. The Group information from the Realm can be  populated into the authenticated JAAS subject during commit() phase following a  successful LoginContext.login() call on the  authentication module. This populated group information is then used by the container in its authorization policy decisions.

Attached here is the source code of a simple sample realm class and the custom module. In this example, the Realm class stores the user-group information in a hashtable. The LoginModule class stores the user-password information in a hashtable and performs authentication. It obtains the authenticatedGroupList from the Realm class' getGroups(username) method.

To test this sample(it works with both GF v2 and v3), download and install Glassfish v3 from here, drop the binaries of this realm and custom module in <GF-ROOT>/domains/domain1/lib/, start the server and create the realm using the Admin console. The realm classname should be specified as com.samplerealm.SampleRealm.

An additional realm property jaas-context should be specified to say sampleRealm. This value should refer to the SampleLoginModule class in the

<GF-ROOT>/domains/domain1/config/login.conf

file as follows:

sampleRealm {
       com.samplerealm.SampleLoginModule required;
};

where sampleRealm refers to the value defined in the jaas-context property.

As can be seen from the source files, the users configured in this realm are userA, userB whose corresponding passwords are abc123, xyz123. userA has been configured in the group devGroup, while userB belongs to testGroup. To test this realm, this web-application can be used.

Observe that the web.xml of the web-app contains the following :

<login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>SampleRealm</realm-name>
     </login-config>

where  SampleRealm in the <login-conf><realm-name> element points to the name of the configured Realm as can be seen in the server's domain.xml

        <auth-realm classname="com.samplerealm.SampleRealm" name="SampleRealm">
          <property name="jaas-context" value="sampleRealm"></property>
        </auth-realm>

To access the web-app using the group names of the user, the following mapping between the role and group is required in sun-web.xml:

  <security-role-mapping>
    <role-name>tester</role-name>
    <group-name>devgroup</group-name>
  </security-role-mapping>

where the role-name matches the configured role in the auth-constraint of web.xml

        <auth-constraint>
            <description/>
            <role-name>tester</role-name>
         </auth-constraint>

and group-name is the corresponding group the user belongs to as defined in the custom realm class. So if the application is accessed using the user/password: userA/abc123, the user is authorized to view the pages, since he belongs to the devGroup, but not userB/xyz123 (who belongs to testGroup).


Comments:

I am reading the AppservPasswordLoginModule, the protected String _currentRealm variable should be a type of Realm, not a String class name. Maybe something you can verify for us:

// should that be "protected Realm _currentRealm"?
protected String _currentRealm;

Posted by Jeff on December 29, 2008 at 09:06 PM SCT #

Yes, it should be a Realm class instance. This has been corrected in the original post. Thanks a lot!!

Posted by Nithya Subramanian on December 30, 2008 at 11:21 AM SCT #

The link to the sourcecode is not correct (gives a 404). You're missing an 'e' in the link, it should be http://blogs.sun.com/nithya/resource/samplerealm.zip.

Posted by Johan on January 02, 2009 at 12:31 PM SCT #

Yes, changed it, thanks!!

Posted by Nithya Subramanian on January 04, 2009 at 08:42 AM SCT #

Does this work for v3 prelude? If I follow the instruction I get ClassNotFoundException for com.samplerealm.SampleReam.

Posted by paulbrickell on May 26, 2009 at 04:47 PM SCT #

Hello...
Thanks for the great post. I am having an issue, however, because glassfish(2) is not finding my custom classes. i have tried jarring them up and putting them in the /lib of both the root install dir and the domain lib. i also set the classpath-suffix in the domain.xml. i just keep getting the BadRealmException.
any words of wisdom? please? thanks ahead of time.

Posted by Paul on May 28, 2009 at 07:30 PM SCT #

I did get it to initialize putting the jar of the Login Module & the Realm in the glassfish install lib dir - not the domain lib. My problem was I wasnt jarring the classes correctly - jar them at the dir below the com.etc.etc ... duh. Thanks for the blog.

Posted by Paul on May 29, 2009 at 07:40 AM SCT #

I can't get this to work. I believe I have everything configured properly. This is the error I see:

[#|2009-10-08T13:39:54.545-0600|INFO|sun-appserver2.1|javax.enterprise.system.core.security|_ThreadID=31;_ThreadName=httpSSLWorkerThread-8080-1;userA;|SEC5046: Audit: Authentication refused for [userA].|#]

[#|2009-10-08T13:39:54.545-0600|WARNING|sun-appserver2.1|javax.enterprise.system.container.web|_ThreadID=31;_ThreadName=httpSSLWorkerThread-8080-1;_RequestID=2ecce6d2-b426-4c10-a6b3-7fe20ddbe48d;|Web login failed: Login failed: javax.security.auth.login.LoginException: Invalid null input: name|#]

Posted by Jim on October 08, 2009 at 11:41 PM SCT #

Oops. I forgot to add the additional "jaas-context" property. Now it works.

Posted by Jim on October 08, 2009 at 11:52 PM SCT #

The SampleRealms does not work with GlassFish v3 (build 74.2).

I can not add the realm in the Admin console (Message=An Error occurred).

The server log file does not help much:

org.jvnet.hk2.config.TransactionFailure: Unexpected exception during isValid call
Can't create children

Caused by: java.lang.NullPointerException
at com.sun.enterprise.config.serverbeans.customvalidators.FileRealmPropertyCheckValidator.isValid(FileRealmPropertyCheckValidator.java:61)
at com.sun.enterprise.config.serverbeans.customvalidators.FileRealmPropertyCheckValidator.isValid(FileRealmPropertyCheckValidator.java:49)
at org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:141)
... 74 more

Posted by Sascha on January 05, 2010 at 05:34 PM SCT #

I just wanted to add that I tried to reuse my self developed Realm/LoginModule currently working under Glassfish 2.1 under Glassfish 3.

But with Glassfish 3 I get the same stacktrace as Sascha - nowhere in the stacktrace is mentioned one of my classes so I think its a bug in Glassfish v3 ....:

[#|2010-02-03T21:03:07.002+0100|INFO|glassfishv3.0|javax.enterprise.system.tools.admin.org.glassfish.server|_ThreadID=25;_ThreadName=Thread-1;|Can't create children
org.jvnet.hk2.config.TransactionFailure: Unexpected exception during isValid call
at org.jvnet.hk2.config.ConfigSupport._apply(ConfigSupport.java:194)
at org.jvnet.hk2.config.ConfigSupport.apply(ConfigSupport.java:130)
at org.glassfish.admin.amx.impl.config.AMXConfigImpl.createChildren(AMXConfigImpl.java:518)
at org.glassfish.admin.amx.impl.config.AMXConfigImpl.createChild(AMXConfigImpl.java:620)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Posted by Marcel Pokrandt on February 04, 2010 at 12:31 AM SCT #

@Marcel Pokrandt

It seems there is an issue with the glassfish admin console, if you use the asadmin create-auth-realm it should create without problems.

Posted by Federico Vela on February 04, 2010 at 01:35 AM SCT #

@Federico

Thanks for your help. You were right. Even configuring additional parameters for an existant realm doesn´t work in the web interface. Manually configuring the Realm worked for me.
This really, really helped me a lot because I wanted to start using EJB3.1 and need this Realm.

Posted by Marcel on February 06, 2010 at 02:06 AM SCT #

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2010 by Nithya Subramanian