FileDocCategorySizeDatePackage
JAASRealm.javaAPI DocGlassfish v2 API16979Fri May 04 22:32:16 BST 2007org.apache.catalina.realm

JAASRealm

public class JAASRealm extends RealmBase

Implmentation of Realm that authenticates users via the Java Authentication and Authorization Service (JAAS). JAAS support requires either JDK 1.4 (which includes it as part of the standard platform) or JDK 1.3 (with the plug-in jaas.jar file).

The value configured for the appName property is passed to the javax.security.auth.login.LoginContext constructor, to specify the application name used to select the set of relevant LoginModules required.

The JAAS Specification describes the result of a successful login as a javax.security.auth.Subject instance, which can contain zero or more java.security.Principal objects in the return value of the Subject.getPrincipals() method. However, it provides no guidance on how to distinguish Principals that describe the individual user (and are thus appropriate to return as the value of request.getUserPrincipal() in a web application) from the Principal(s) that describe the authorized roles for this user. To maintain as much independence as possible from the underlying LoginMethod implementation executed by JAAS, the following policy is implemented by this Realm:

  • The JAAS LoginModule is assumed to return a Subject with at least one Principal instance representing the user himself or herself, and zero or more separate Principals representing the security roles authorized for this user.
  • On the Principal representing the user, the Principal name is an appropriate value to return via the Servlet API method HttpServletRequest.getRemoteUser().
  • On the Principals representing the security roles, the name is the name of the authorized security role.
  • This Realm will be configured with two lists of fully qualified Java class names of classes that implement java.security.Principal - one that identifies class(es) representing a user, and one that identifies class(es) representing a security role.
  • As this Realm iterates over the Principals returned by Subject.getPrincipals(), it will identify the first Principal that matches the "user classes" list as the Principal for this user.
  • As this Realm iterates over the Princpals returned by Subject.getPrincipals(), it will accumulate the set of all Principals matching the "role classes" list as identifying the security roles for this user.
  • It is a configuration error for the JAAS login method to return a validated Subject without a Principal that matches the "user classes" list.
author
Craig R. McClanahan
version
$Revision: 1.4 $ $Date: 2007/05/05 05:32:16 $

Fields Summary
private static com.sun.org.apache.commons.logging.Log
log
protected String
appName
The application name passed to the JAAS LoginContext, which uses it to select the set of relevant LoginModules.
protected static final String
info
Descriptive information about this Realm implementation.
protected static final String
name
Descriptive information about this Realm implementation.
protected ArrayList
roleClasses
The list of role class names, split out for easy processing.
protected static final org.apache.catalina.util.StringManager
sm
The string manager for this package.
protected ArrayList
userClasses
The set of user class names, split out for easy processing.
protected String
roleClassNames
Comma-delimited list of javax.security.Principal classes that represent security roles.
protected String
userClassNames
Comma-delimited list of javax.security.Principal classes that represent individual users.
Constructors Summary
Methods Summary
public java.security.Principalauthenticate(java.lang.String username, java.lang.String credentials)
Return the Principal associated with the specified username and credentials, if there is one; otherwise return null. If there are any errors with the JDBC connection, executing the query or anything we return null (don't authenticate). This event is also logged, and the connection will be closed so that a subsequent request will automatically re-open it.

param
username Username of the Principal to look up
param
credentials Password or other credentials to use in authenticating this username


        // Establish a LoginContext to use for authentication
        try {
        LoginContext loginContext = null;
        if( appName==null ) appName="Tomcat";

        if( log.isDebugEnabled())
            log.debug("Authenticating " + appName + " " +  username);

        // What if the LoginModule is in the container class loader ?
        //
        ClassLoader ocl=Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
        try {
            loginContext = new LoginContext
                (appName, new JAASCallbackHandler(this, username,
                                                  credentials));
        } catch (Throwable e) {
            log.debug("Error initializing JAAS: " +  e.toString());

            log.debug(sm.getString("jaasRealm.loginException", username), e);
            return (null);
        } finally {
            Thread.currentThread().setContextClassLoader(ocl);
        }

        if( log.isDebugEnabled())
            log.debug("Login context created " + username);

        // Negotiate a login via this LoginContext
        Subject subject = null;
        try {
            loginContext.login();
            subject = loginContext.getSubject();
            if (subject == null) {
                if( log.isDebugEnabled())
                    log.debug(sm.getString("jaasRealm.failedLogin", username));
                return (null);
            }
        } catch (AccountExpiredException e) {
            if (log.isDebugEnabled())
                log.debug(sm.getString("jaasRealm.accountExpired", username));
            return (null);
        } catch (CredentialExpiredException e) {
            if (log.isDebugEnabled())
                log.debug(sm.getString("jaasRealm.credentialExpired", username));
            return (null);
        } catch (FailedLoginException e) {
            if (log.isDebugEnabled())
                log.debug(sm.getString("jaasRealm.failedLogin", username));
            return (null);
        } catch (LoginException e) {
            log.debug(sm.getString("jaasRealm.loginException", username), e);
            return (null);
        } catch (Throwable e) {
            log.debug("Unexpected error", e);
            return (null);
        }

        if( log.isDebugEnabled())
            log.debug("Getting principal " + subject);

        // Return the appropriate Principal for this authenticated Subject
        Principal principal = createPrincipal(username, subject);
        if (principal == null) {
            log.debug(sm.getString("jaasRealm.authenticateFailure", username));
            return (null);
        }
        if (log.isDebugEnabled()) {
            log.debug(sm.getString("jaasRealm.authenticateSuccess", username));
        }

        return (principal);
        } catch( Throwable t) {
            log.error( "error ", t);
            return null;
        }
    
protected java.security.PrincipalcreatePrincipal(java.lang.String username, javax.security.auth.Subject subject)
Construct and return a java.security.Principal instance representing the authenticated user for the specified Subject. If no such Principal can be constructed, return null.

param
subject The Subject representing the logged in user

        // Prepare to scan the Principals for this Subject
        String password = null; // Will not be carried forward
        ArrayList roles = new ArrayList();

        // Scan the Principals for this Subject
        Iterator principals = subject.getPrincipals().iterator();
        while (principals.hasNext()) {
            Principal principal = (Principal) principals.next();
            // No need to look further - that's our own stuff
            if( principal instanceof GenericPrincipal ) {
                if( log.isDebugEnabled() )
                    log.debug("Found old GenericPrincipal " + principal );
                return principal;
            }
            String principalClass = principal.getClass().getName();
            if( log.isDebugEnabled() )
                log.info("Principal: " + principalClass + " " + principal);

            if (userClasses.contains(principalClass)) {
                // Override the default - which is the original user, accepted by
                // the friendly LoginManager
                username = principal.getName();
            }
            if (roleClasses.contains(principalClass)) {
                roles.add(principal.getName());
            }
            // Same as Jboss - that's a pretty clean solution
            if( (principal instanceof Group) &&
                 "Roles".equals( principal.getName())) {
                Group grp=(Group)principal;
                Enumeration en=grp.members();
                while( en.hasMoreElements() ) {
                    Principal roleP=(Principal)en.nextElement();
                    roles.add( roleP.getName());
                }

            }
        }

        // Create the resulting Principal for our authenticated user
        if (username != null) {
            return (new GenericPrincipal(this, username, password, roles));
        } else {
            return (null);
        }
    
public java.lang.StringgetAppName()
getter for the appName member variable

        return appName;
    
protected java.lang.StringgetName()
Return a short name for this Realm implementation.


        return (this.name);

    
protected java.lang.StringgetPassword(java.lang.String username)
Return the password associated with the given principal's user name.


        return (null);

    
protected java.security.PrincipalgetPrincipal(java.lang.String username)
Return the Principal associated with the given user name.


        return (null);

    
public java.lang.StringgetRoleClassNames()


       
        return (this.roleClassNames);
    
public java.lang.StringgetUserClassNames()


       
        return (this.userClassNames);
    
public voidsetAppName(java.lang.String name)
setter for the appName member variable

deprecated
JAAS should use the Engine ( domain ) name and webpp/host overrides



    // ------------------------------------------------------------- Properties

    
                            
        
        appName = name;
    
public voidsetContainer(org.apache.catalina.Container container)

        super.setContainer(container);
        String name=container.getName();
        if( appName==null  ) {
            appName=name;
            log.info("Setting JAAS app name " + appName);
        }
    
public voidsetRoleClassNames(java.lang.String roleClassNames)

        this.roleClassNames = roleClassNames;
        roleClasses.clear();
        String temp = this.roleClassNames;
        if (temp == null) {
            return;
        }
        while (true) {
            int comma = temp.indexOf(',");
            if (comma < 0) {
                break;
            }
            roleClasses.add(temp.substring(0, comma).trim());
            temp = temp.substring(comma + 1);
        }
        temp = temp.trim();
        if (temp.length() > 0) {
            roleClasses.add(temp);
        }
    
public voidsetUserClassNames(java.lang.String userClassNames)

        this.userClassNames = userClassNames;
        userClasses.clear();
        String temp = this.userClassNames;
        if (temp == null) {
            return;
        }
        while (true) {
            int comma = temp.indexOf(',");
            if (comma < 0) {
                break;
            }
            userClasses.add(temp.substring(0, comma).trim());
            temp = temp.substring(comma + 1);
        }
        temp = temp.trim();
        if (temp.length() > 0) {
            userClasses.add(temp);
        }
    
public voidstart()
Prepare for active use of the public methods of this Component.

exception
LifecycleException if this component detects a fatal error that prevents it from being started


        // Perform normal superclass initialization
        super.start();

    
public voidstop()
Gracefully shut down active use of the public methods of this Component.

exception
LifecycleException if this component detects a fatal error that needs to be reported


        // Perform normal superclass finalization
        super.stop();