Wednesday, 5 March 2008

Glassfish login module : a simple example

In this tutorial we will see how to write a very simple custom login module for the Glassfish application server.
Even if Glassfish ships with some useful login modules, there are not adapted to every situations, most notably with custom LDAP directories or strange authentication methods.
Chapter 5 of the Glassfish developer documentation is a must read on this subject, and reading it is recommended before trying to write your own custom login module.

Our custom authentication scheme
In this example we want to implement a very simple authentication method : a user is authenticated if is username start with a specific string. For example, if the string is "z", then the users zed and zorro can login.

A configurable login module
The login module must be fully configurable. The application server administrator can configure the string used in our authentication scheme.

The custom realm
A custom login module in glassfish is composed by two classes :
- a custom realm, which handle realm configuration
- a custom login module, which handle authentication based on the informations given by the custom realm
A custom realm must extends AppservRealm :
public class MyRealm extends AppservRealm {
private String jaasCtxName;

private String startWith;

@Override
protected void init(Properties props) throws BadRealmException, NoSuchRealmException { _
logger.info("X-TECH MyRealm: init()");

jaasCtxName = props.getProperty("jaas-context", "xtechMyRealm");
startWith = props.getProperty("startWith", "z");
}

@Override public String getJAASContext() { return jaasCtxName; }

public Enumeration getGroupNames(String string) throws InvalidOperationException, NoSuchUserException {
List groupNames = new LinkedList();
return (Enumeration) groupNames;
}

public String getAuthType() { return "X-TECH MyRealm"; }
public String getStartWith() {
return startWith; }

}


The custom login module
A custom login must extends AppservPasswordLoginModule and override authenticateUser():
public class MyLoginModule extends AppservPasswordLoginModule {

protected void authenticateUser() throws LoginException {
_logger.info("X-TECH MyLoginModule : authenticateUser for " + _username);
MyRealm realm = (MyRealm) _currentRealm;


if (!_username.startsWith(realm.getStartWith())) {
_logger.info("Invalid credentials.");
throw new LoginException ("Invalid credentials.");
}

_logger.info("User authenticated");
Set principals = _subject.getPrincipals();

principals.add(new PrincipalImpl(_username));

String grpList[] = new String[1];
grpList[0] = "User";

this.commitUserAuthentication(grpList);

}

}


Installing the custom login module
Installing and activating the customs realm and login module involve three steps :
- Configuring a realm using the glassfish administration console
- Linking the custom realm with the custom login module
- Make the classes avaible to glassfish
The first step is illustrated in the screenshot below :
The second step is done by editing the login.conf file located in the config directory of the domain. Add a line like this one to the end of the login.conf file:
xtechMyRealm { org.xtech.examples.gfloginmodule.MyLoginModule required; };
For the last step just copy the .jar file containing the customs realm and login module classes in the lib directory of glassfish (you must restart glassfish).

Writing an example application
To try the new login module create a web application with only a index.jsp page. Put this line to display the authenticated user name :
Welcome ${pageContext.request.userPrincipal.name}
Add security to the web.xml file:


and link the web application security roles with the group name used in the custom login module by adding the folowing lines to the sun-web.xml file:

Deploy your application and give it a try !

Tuesday, 3 April 2007

JBoss custom login module : a simple example

In this tutorial we will see how to write a very simple custom login module for the JBoss application server.
Even if JBoss ships with some useful login modules, there are not adapted to every situations, most notably with custom LDAP directories or strange authentication methods.
Chapter 8 of the JBoss documentation is a must read on this subject, and reading it is recommended before trying to write your own custom login module.

Our custom authentication scheme
In this example we want to implement a very stange authentication method :
- dynamic user login names in the form userX where X is a decimal number
- dynamic administrators login names in the form adminY where Y is what you want and is not very important
- a guest user name for anonymous connections
- dynamic user passphrase in the form userX loves you
- dynamic administrator passphrase in the form adminY is the boss
- empty password for the guest user


A configurable login module
The login module must be fully configurable.
We want :
- a regular expression to indentify guest users (optional, "anonymous" if not configured)
- a regular expression to indentify normal users
- a regular expression to indentify administrators
- a model for the users passphrases
- a model for the administrator passphrases

JBoss configuration
Default JBoss login modules configuration file is ${JBOSS_HOME}/server/default/conf/login-config.xml.
Every JBoss login module must be given a JAAS security domain name. We will call our security domain "MyLoginModule".
Add the following to the configuration file just before the "other" security domain :

com.blogger.xtechteam.examples.jboss.loginmodule.CustomJBossLoginModule
is the name of our custom login module class.

The custom login module
Our custom login module is a class derived from org.jboss.security.auth.spi.UsernamePasswordLoginModule.
By derivating UsernamePasswordLoginModule we only need to implements the following methods :
- initialize() : called by JBoss after creating a new instance of our login module
- validatePassword() : called by JBoss each time a user try to login
- getRoleSets() : called by JBoss after a user have been successfully authenticated to get the list of roles for this user
This is the code of our login module :

public class CustomJBossLoginModule extends UsernamePasswordLoginModule {

/* Regular expressions used for user login name */
private Pattern guestRegEx;
private Pattern userRegEx;
private Pattern adminRegEx;
/* Magic phrases used in place of user password */
private String userMagic;
private String adminMagic;

/* Currently authenticated user roles */
private boolean isGuest;
private boolean hasUserRole;
private boolean hasAdminRole;

/* called by JBoss when loading the login module */
@Override
public void initialize (
Subject subject,
CallbackHandler callbackHandler,
Map sharedState,
Map options) {
super.initialize (subject, callbackHandler, sharedState, options);
log.info ("CustomJBossLoginModule : initialize");

/* load the optional login module option */
String guestParam = (String)options.get ("guestRegEx");
if (guestParam == null) {
guestParam = "anonymous";
}

/* load the mandatory login module options */
guestRegEx = Pattern.compile (guestParam);
userRegEx = Pattern.compile ((String)options.get ("userRegEx"));
adminRegEx = Pattern.compile ((String)options.get ("adminRegEx"));
userMagic = (String)options.get ("userMagic");
adminMagic = (String)options.get ("adminMagic");
}

public CustomJBossLoginModule () {
super ();
}


/* called by JBoss to validate a user password */
@SuppressWarnings ("unchecked")
@Override
protected boolean validatePassword (String inputPassword, String expectedPassword) {
log.info ("CustomJBossLoginModule : validatePassword for " + getUsername ());

/* validate the user name */
isGuest = guestRegEx.matcher (getUsername ()).matches ();
if (!isGuest) {
hasUserRole = userRegEx.matcher (getUsername ()).matches ();
if (!hasUserRole) {
hasAdminRole = adminRegEx.matcher (getUsername ()).matches ();
}
}

/* validate the password */
boolean validPassword = isGuest;
if (hasUserRole) {
validPassword = inputPassword.equals (
String.format (userMagic, getUsername ()));
} else if (hasAdminRole) {
validPassword = inputPassword.equals (
String.format (adminMagic, getUsername ()));
}

/* save some informations for the application */
if (validPassword) {
Set principals = subject.getPrincipals ();
principals.add (new SimplePrincipal (getUsername ()));
principals.add (new SimplePrincipal (inputPassword));
principals.add (new SimplePrincipal ("Hello from CustomJBossLoginModule"));
principals.add (new SimplePrincipal (Boolean.toString (isGuest)));
principals.add (new SimplePrincipal (Boolean.toString (hasUserRole)));
principals.add (new SimplePrincipal (Boolean.toString (hasAdminRole)));
log.info (String.format ("CustomJBossLoginModule : user %s authenticated (guest : %b, user : %b, admin : %b)",
getUsername (), isGuest, hasUserRole, hasAdminRole));
} else {
log.info ("CustomJBossLoginModule : Invalid credentials");
}
return validPassword;
}

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

/* fill the role collection */
@Override
protected Group[] getRoleSets () throws LoginException {
SimpleGroup userRoles = new SimpleGroup ("Roles");
if (isGuest) {
userRoles.addMember (new SimplePrincipal ("Guest"));
}
if (hasUserRole) {
userRoles.addMember (new SimplePrincipal ("User"));
}
if (hasAdminRole) {
userRoles.addMember (new SimplePrincipal ("Admin"));
}
Group[] roleSets = { userRoles };
return roleSets;
}

}

Compilation
To compile this login module you need two .jar files from JBoss : jboss-common.jar and jbosssx.jar.

Deployment
Put the login module class in a .jar file and copy this archive to ${JBOSS_HOME}/server/default/lib.


Getting informations about the user from the application
You (normally) have noticed that our login module save some informations in the collection of principals for the subject (the authenticated user).
This is needed to have this informations from our web applications or from our EJBs in a application-server independant way.
This is an example of a class that can be used to get this informations :

public class AuthenticatedUserInformations {

public class InvalidUserException extends Exception {};

/** The JACC PolicyContext key for the current Subject */
private static final String SUBJECT_CONTEXT_KEY =
"javax.security.auth.Subject.container";

private String username;
private String password;
private String hello;
private boolean isGuest;
private boolean isAdmin;
private boolean isUser;

public AuthenticatedUserInformations () throws InvalidUserException {
Subject caller;
try {
caller = (Subject) PolicyContext.getContext (SUBJECT_CONTEXT_KEY);
if (caller == null) {
throw new InvalidUserException ();
}
Iterator it = caller.getPrincipals ().iterator ();
username = it.next ().getName ();
password = it.next ().getName ();
hello = it.next ().getName ();
isGuest = Boolean.valueOf (it.next ().getName ());
isUser = Boolean.valueOf (it.next ().getName ());
isAdmin = Boolean.valueOf (it.next ().getName ());
} catch (PolicyContextException e) {
throw new InvalidUserException ();
}
}

public String getUsername () {
return username;
}

public String getPassword () {
return password;
}

public boolean isGuest () {
return isGuest;
}

public boolean isAdmin () {
return isAdmin;
}

public boolean isUser () {
return isUser;
}

}


Web application example
Finally, write a web application example to test our login module.
First, the web.xml file must contain the classic security configuration elements :
Second, the jboss-web.xml must declare the security domain :


Third, a simple JSP page to demonstrate the use of the AuthenticatedUserInformations class.
In an empty JSP page add the following lines before the HTML body :

and some JSP and HTML code in the body :
Deploy your application and give it a try !

Friday, 1 December 2006

Eclipse+JBoss Error : "transport error 202"

I get the following error when starting JBoss 4.0.5 from Eclipse/JBossIDE :


ERROR: transport error 202: gethostbyname: unknown host ["transport.c",L41]
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510) ["debugInit.c",L500]
JDWP exit error JVMTI_ERROR_INTERNAL(113): No transports initializedFATAL ERROR in native method: JDWP No transports initialized, jvmtiError=JVMTI_ERROR_INTERNAL(113)

Since this error only appears when starting JBoss when connected to my home network and not at work, I suspected a DNS/Resolver problem.
To correct the problem, just add the following line to /etc/hosts :

127.0.0.1 localhost

Apparently the problem appears only when launching the Eclipse debugger and is not linked with JBoss.

Thursday, 30 November 2006

WMWare : "End of error message"

A great error message from VMWare server :


"End of error message".... very usefull !
To view the real error message, just check the /var/log/vmware/vmware-serverd.log file :


Msg_Post: Could not open /dev/vmmon: No such file or directory.
Nov 30 12:59:53: app| Please make sure that the kernel module `vmmon' is loaded.
Nov 30 12:59:53: app|
Nov 30 12:59:53: app| Msg_Post: Error
Nov 30 12:59:53: app| [msg.vmmonPosix.initFailed] Failed to initialize monitor device.
Nov 30 12:59:53: app| [localized] Failed to initialize monitor device.

Ooops, the VMWare service (which create /dev/vmmon and other devices) was not started :

# sudo service vmware start

Monday, 27 November 2006

Introducing PgWorksheet

PgWorksheet is a simple GUI frontend to PostgreSQL for executing SQL queries and psql commands without using the psql command line tool.

Development of this tool have started at the E-CML Computer Science school for SQL and PL/SQL courses when Oracle was replaced by PostgreSQL for this courses.
PostgreSQL was choosen in place of Oracle for several reasons :

  • Minimal server administration compared to the mammoth-style administration tasks requiered by Oracle
  • Minimal CPU & memory footprint : Oracle needed a single server only for the courses. With PostgreSQL, the database was installed on the school main NFS/Samba/DNS/DHCP/VPN/... server.
  • No need to install a mammoth-sized client to have a simple GUI client to execute simples SQL queries.
This last point is also an important problem with PostgreSQL at the time of the switch : the total lack of a simple, light, easy to install, open source, free GUI client.
While PostgreSQL offers some great administration tools (PgAdmin, phpPgAdmin) there are administration tools, not just SQL scratch pads.

Since the main GUI tool used during SQL courses with Oracle was SQLWorksheet, we started to explore the web to find an identical tool for PostgreSQL. We found only one application fullfiling our requirements, pgst. But pgst have an important problem : despite the fact that is was written in Python, pgst was not portable and needed some important modifications to run under Microsoft Windows.

PgWorksheet started as a re-implementation of some ideas of pgst, written from scratch, but with portability in mind since we have 3 different operating system used during the courses : FreeBSD, Linux Fedora and Microsoft Windows.

Now PgWorksheet version 1.8 offers several, but limited to provide easy of use, fonctionnalities :
  • portability (runs on *BSD, all Linux distribs and Microsoft Windows XP)
  • syntax coloring
  • load/save SQL queries
  • SQL queries history
  • unlimited undo/redo
  • display of SQL queries results separated (one tab for one query)
  • server connection history
The PgWorksheet project is hosted by PgFoundry and the PgWorksheet sources are hosted by SourceForge (you can browse source code)