Index: maven.xml =================================================================== RCS file: /home/cvspublic/jakarta-jetspeed-2/maven.xml,v retrieving revision 1.61 diff -u -r1.61 maven.xml --- maven.xml 23 May 2004 21:26:18 -0000 1.61 +++ maven.xml 26 May 2004 12:42:52 -0000 @@ -52,6 +52,7 @@ + @@ -191,6 +192,7 @@ ignoreFailures="false"/> + @@ -489,4 +491,29 @@ + + + + + + + + + + + + + + + + + + + + + + + Index: project.xml =================================================================== RCS file: /home/cvspublic/jakarta-jetspeed-2/project.xml,v retrieving revision 1.13 diff -u -r1.13 project.xml --- project.xml 3 Apr 2004 04:31:19 -0000 1.13 +++ project.xml 26 May 2004 12:42:52 -0000 @@ -145,6 +145,22 @@ 3.2 plugin + + + commons-logging + 1.0.3 + + false + + + + mx4j + mx4j-jmx + 1.1.1 + + false + + Index: site/xdocs/getting-started.xml =================================================================== RCS file: /home/cvspublic/jakarta-jetspeed-2/site/xdocs/getting-started.xml,v retrieving revision 1.1 diff -u -r1.1 getting-started.xml --- site/xdocs/getting-started.xml 6 May 2004 00:04:54 -0000 1.1 +++ site/xdocs/getting-started.xml 26 May 2004 12:43:08 -0000 @@ -33,9 +33,9 @@
  • Maven 1.0-beta-10 or higher
  • Java 1.4 or higher
  • Servlet 2.3:
    - Tomcat 4.1.18-LE w/JDK 1.4
    + Tomcat 4.1.x
    or
    - Tomcat 4.1.24 w/ JDK 1.3
  • + Tomcat 5.x
    @@ -50,6 +50,16 @@ what is it? + catalina.home + ${CATALINA_HOME}/ + The root of your Tomcat server installation. + + + catalina.version.major + 4 + Are you using Tomcat 4.1.x or 5.x? + + catalina.shared.lib ${CATALINA_HOME}/shared/lib/ The location of the jars in your Tomcat installation. @@ -60,6 +70,14 @@ The location to deploy web application in Tomcat. +

    +If you are using Tomcat 5.x, the allBuild goal described below will install a +patched version of the JAASRealm class in ${catalina.home}/server/classes to +enable Jetspeed JAAS based security. +For more information about this patch see: + +Issue 55: JAAS Authentication on Tomcat 5. +

    To develop or deploy Jetspeed with another database (not the default HSQL), see the property configuration in the Database Configuration documentation. Index: src/tomcat5-jaas-patch/org/apache/catalina/realm/JAASRealm.java =================================================================== RCS file: src/tomcat5-jaas-patch/org/apache/catalina/realm/JAASRealm.java diff -N src/tomcat5-jaas-patch/org/apache/catalina/realm/JAASRealm.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tomcat5-jaas-patch/org/apache/catalina/realm/JAASRealm.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,457 @@ +/* + * Copyright 2001-2002,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package org.apache.catalina.realm; + + +import java.security.Principal; +import java.security.acl.Group; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; + +import javax.security.auth.Subject; +import javax.security.auth.login.AccountExpiredException; +import javax.security.auth.login.CredentialExpiredException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.catalina.Container; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.util.StringManager; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + *

    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.6 $ $Date: 2004/02/27 14:58:45 $ + */ + +public class JAASRealm + extends RealmBase + { + private static Log log = LogFactory.getLog(JAASRealm.class); + + // ----------------------------------------------------- Instance Variables + + + /** + * The application name passed to the JAAS LoginContext, + * which uses it to select the set of relevant LoginModules. + */ + protected String appName = null; + + + /** + * Descriptive information about this Realm implementation. + */ + protected static final String info = + "org.apache.catalina.realm.JAASRealm/1.0"; + + + /** + * Descriptive information about this Realm implementation. + */ + protected static final String name = "JAASRealm"; + + + /** + * The list of role class names, split out for easy processing. + */ + protected ArrayList roleClasses = new ArrayList(); + + + /** + * The string manager for this package. + */ + protected static final StringManager sm = + StringManager.getManager(Constants.Package); + + + /** + * The set of user class names, split out for easy processing. + */ + protected ArrayList userClasses = new ArrayList(); + + + // ------------------------------------------------------------- Properties + + + /** + * setter for the appName member variable + * @deprecated JAAS should use the Engine ( domain ) name and webpp/host overrides + */ + public void setAppName(String name) { + appName = name; + } + + /** + * getter for the appName member variable + */ + public String getAppName() { + return appName; + } + + public void setContainer(Container container) { + super.setContainer(container); + String name=container.getName(); + if( appName==null ) { + appName=name; + log.info("Setting JAAS app name " + appName); + } + } + + /** + * Comma-delimited list of javax.security.Principal classes + * that represent security roles. + */ + protected String roleClassNames = null; + + public String getRoleClassNames() { + return (this.roleClassNames); + } + + public void setRoleClassNames(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); + } + } + + + /** + * Comma-delimited list of javax.security.Principal classes + * that represent individual users. + */ + protected String userClassNames = null; + + public String getUserClassNames() { + return (this.userClassNames); + } + + public void setUserClassNames(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 Methods + + + /** + * 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 + */ + public Principal authenticate(String username, String credentials) { + + // 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 ? + // + // 2004-05-25 Ate Douma: + // Disabling new Tomcat5 logic of setting the ContextClassLoader + // to the server classloader before the LoginModule is loaded. + // See: http://nagoya.apache.org/jira/browse/JS2-55 +// 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.error(sm.getString("jaasRealm.unexpectedError"), 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.warn(sm.getString("jaasRealm.loginException", username), e); + return (null); + } catch (Throwable e) { + log.error(sm.getString("jaasRealm.unexpectedError"), 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; + } + } + + + // -------------------------------------------------------- Package Methods + + + // ------------------------------------------------------ Protected Methods + + + /** + * Return a short name for this Realm implementation. + */ + protected String getName() { + + return (name); + + } + + + /** + * Return the password associated with the given principal's user name. + */ + protected String getPassword(String username) { + + return (null); + + } + + + /** + * Return the Principal associated with the given user name. + */ + protected Principal getPrincipal(String username) { + + return (null); + + } + + + /** + * 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 + */ + protected Principal createPrincipal(String username, Subject subject) { + // 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); + } + } + + + // ------------------------------------------------------ Lifecycle Methods + + + /** + * + * 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 + */ + public void start() throws LifecycleException { + + // Perform normal superclass initialization + super.start(); + + } + + + /** + * 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 + */ + public void stop() throws LifecycleException { + + // Perform normal superclass finalization + super.stop(); + + } + + +}