Return-Path: Delivered-To: apmail-tomcat-dev-archive@www.apache.org Received: (qmail 37950 invoked from network); 3 Sep 2008 22:19:40 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 3 Sep 2008 22:19:40 -0000 Received: (qmail 86855 invoked by uid 500); 3 Sep 2008 22:19:31 -0000 Delivered-To: apmail-tomcat-dev-archive@tomcat.apache.org Received: (qmail 86807 invoked by uid 500); 3 Sep 2008 22:19:31 -0000 Mailing-List: contact dev-help@tomcat.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: "Tomcat Developers List" Delivered-To: mailing list dev@tomcat.apache.org Received: (qmail 86795 invoked by uid 99); 3 Sep 2008 22:19:31 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Sep 2008 15:19:31 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Sep 2008 22:18:41 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 5E899238899B; Wed, 3 Sep 2008 15:18:42 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r691805 - in /tomcat/trunk: java/org/apache/catalina/realm/ java/org/apache/catalina/startup/ webapps/docs/ webapps/docs/config/ Date: Wed, 03 Sep 2008 22:18:41 -0000 To: dev@tomcat.apache.org From: markt@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080903221842.5E899238899B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: markt Date: Wed Sep 3 15:18:39 2008 New Revision: 691805 URL: http://svn.apache.org/viewvc?rev=691805&view=rev Log: Add a new combined Realm that can be used to try authenticating against multiple realms. Added: tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java (with props) tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/startup/ContextRuleSet.java tomcat/trunk/java/org/apache/catalina/startup/EngineRuleSet.java tomcat/trunk/java/org/apache/catalina/startup/HostRuleSet.java tomcat/trunk/webapps/docs/config/realm.xml tomcat/trunk/webapps/docs/realm-howto.xml Added: tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java?rev=691805&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java (added) +++ tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java Wed Sep 3 15:18:39 2008 @@ -0,0 +1,298 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.cert.X509Certificate; +import java.util.LinkedList; +import java.util.List; + +import org.apache.catalina.Container; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.Realm; +import org.apache.catalina.util.StringManager; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * Realm implementation that contains one or more realms. Authentication is + * attempted for each realm in the order they were configured. If any realm + * authenticates the user then the authentication succeeds. + */ +public class CombinedRealm extends RealmBase { + + private static Log log = LogFactory.getLog(RealmBase.class); + + /** + * The string manager for this package. + */ + protected static StringManager sm = + StringManager.getManager(Constants.Package); + + /** + * The list of Realms contained by this Realm. + */ + protected List realms = new LinkedList(); + + + /** + * Add a realm to the list of realms that will be used to authenticate + * users. + */ + public void addRealm(Realm theRealm) { + realms.add(theRealm); + + if (log.isDebugEnabled()) { + sm.getString("combinedRealm.addRealm", theRealm.getInfo(), + Integer.toString(realms.size())); + } + } + + + /** + * Return the Principal associated with the specified username and + * credentials, if there is one; otherwise return null. + * + * @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, byte[] credentials) { + Principal authenticatedUser = null; + + for (Realm realm : realms) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authStart", username, realm.getInfo())); + } + + authenticatedUser = realm.authenticate(username, credentials); + + if (authenticatedUser == null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authFail", username, realm.getInfo())); + } + } else { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authSucess", username, realm.getInfo())); + } + break; + } + } + return authenticatedUser; + } + + + /** + * Return the Principal associated with the specified username, which + * matches the digest calculated using the given parameters using the + * method described in RFC 2069; otherwise return null. + * + * @param username Username of the Principal to look up + * @param clientDigest Digest which has been submitted by the client + * @param nOnce Unique (or supposedly unique) token which has been used + * for this request + * @param realm Realm name + * @param md5a2 Second MD5 digest used to calculate the digest : + * MD5(Method + ":" + uri) + */ + public Principal authenticate(String username, String clientDigest, + String once, String nc, String cnonce, String qop, + String realmName, String md5a2) { + Principal authenticatedUser = null; + + for (Realm realm : realms) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authStart", username, realm.getInfo())); + } + + authenticatedUser = realm.authenticate(username, clientDigest, once, + nc, cnonce, qop, realmName, md5a2); + + if (authenticatedUser == null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authFail", username, realm.getInfo())); + } + } else { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authSucess", username, realm.getInfo())); + } + break; + } + } + return authenticatedUser; + } + + + /** + * Return the Principal associated with the specified username and + * credentials, if there is one; otherwise return null. + * + * @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) { + Principal authenticatedUser = null; + + for (Realm realm : realms) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authStart", username, realm.getInfo())); + } + + authenticatedUser = realm.authenticate(username, credentials); + + if (authenticatedUser == null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authFail", username, realm.getInfo())); + } + } else { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authSucess", username, realm.getInfo())); + } + break; + } + } + return authenticatedUser; + } + + + /** + * Set the Container with which this Realm has been associated. + * + * @param container The associated Container + */ + public void setContainer(Container container) { + // Set the container for sub-realms. Mainly so logging works. + for(Realm realm : realms) { + realm.setContainer(container); + } + super.setContainer(container); + } + + + /** + * Prepare for the beginning of active use of the public methods of this + * component. This method should be called before any of the public + * methods of this component are utilized. It should also send a + * LifecycleEvent of type START_EVENT to any registered listeners. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + public void start() throws LifecycleException { + // Start 'sub-realms' then this one + for (Realm realm : realms) { + if (realm instanceof Lifecycle) { + ((Lifecycle) realm).start(); + } + } + super.start(); + } + + + /** + * Gracefully terminate the active use of the public methods of this + * component. This method should be the last one called on a given + * instance of this component. It should also send a LifecycleEvent + * of type STOP_EVENT to any registered listeners. + * + * @exception LifecycleException if this component detects a fatal error + * that needs to be reported + */ + public void stop() throws LifecycleException { + // Stop this realm, then the sub-realms (reverse order to start) + super.stop(); + for (Realm realm : realms) { + if (realm instanceof Lifecycle) { + ((Lifecycle) realm).stop(); + } + } + } + + + /** + * Return the Principal associated with the specified chain of X509 + * client certificates. If there is none, return null. + * + * @param certs Array of client certificates, with the first one in + * the array being the certificate of the client itself. + */ + public Principal authenticate(X509Certificate[] certs) { + Principal authenticatedUser = null; + String username = null; + if (certs != null && certs.length >0) { + username = certs[0].getSubjectDN().getName(); + } + + for (Realm realm : realms) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authStart", username, realm.getInfo())); + } + + authenticatedUser = realm.authenticate(certs); + + if (authenticatedUser == null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authFail", username, realm.getInfo())); + } + } else { + if (log.isDebugEnabled()) { + log.debug(sm.getString("combinedRealm.authSucess", username, realm.getInfo())); + } + break; + } + } + return authenticatedUser; + } + + @Override + protected String getName() { + // This method should never be called + // Stack trace will show where this was called from + UnsupportedOperationException uoe = + new UnsupportedOperationException( + sm.getString("combinedRealm.getName")); + log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe); + throw uoe; + } + + @Override + protected String getPassword(String username) { + // This method should never be called + // Stack trace will show where this was called from + UnsupportedOperationException uoe = + new UnsupportedOperationException( + sm.getString("combinedRealm.getPassword")); + log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe); + throw uoe; + } + + @Override + protected Principal getPrincipal(String username) { + // This method should never be called + // Stack trace will show where this was called from + UnsupportedOperationException uoe = + new UnsupportedOperationException( + sm.getString("combinedRealm.getPrincipal")); + log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe); + throw uoe; + } + +} Propchange: tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/java/org/apache/catalina/realm/CombinedRealm.java ------------------------------------------------------------------------------ svn:keywords = Date Revision Author Id Modified: tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties Wed Sep 3 15:18:39 2008 @@ -85,3 +85,11 @@ dataSourceRealm.getPassword.exception=Exception retrieving password for "{0}" dataSourceRealm.getRoles.exception=Exception retrieving roles for "{0}" dataSourceRealm.open=Exception opening database connection +combinedRealm.unexpectedMethod=An unexpected call was made to a method on the combined realm +combinedRealm.getName=The getName() method should never be called +combinedRealm.getPassword=The getPassword() method should never be called +combinedRealm.getPrincipal=The getPrincipal() method should never be called +combinedRealm.authStart=Attempting to authenticate user "{0}" with realm "{1}" +combinedRealm.authFailed=Failed to authenticate user "{0}" with realm "{1}" +combinedRealm.authSucess=Authenticated user "{0}" with realm "{1}" +combinedRealm.addRealm=Add "{0}" realm, making a total of "{1}" realms \ No newline at end of file Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextRuleSet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextRuleSet.java?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/ContextRuleSet.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/ContextRuleSet.java Wed Sep 3 15:18:39 2008 @@ -170,13 +170,7 @@ "addApplicationParameter", "org.apache.catalina.deploy.ApplicationParameter"); - digester.addObjectCreate(prefix + "Context/Realm", - null, // MUST be specified in the element - "className"); - digester.addSetProperties(prefix + "Context/Realm"); - digester.addSetNext(prefix + "Context/Realm", - "setRealm", - "org.apache.catalina.Realm"); + digester.addRuleSet(new RealmRuleSet(prefix + "Context/")); digester.addObjectCreate(prefix + "Context/Resources", "org.apache.naming.resources.FileDirContext", Modified: tomcat/trunk/java/org/apache/catalina/startup/EngineRuleSet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/EngineRuleSet.java?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/EngineRuleSet.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/EngineRuleSet.java Wed Sep 3 15:18:39 2008 @@ -121,13 +121,7 @@ "org.apache.catalina.LifecycleListener"); - digester.addObjectCreate(prefix + "Engine/Realm", - null, // MUST be specified in the element - "className"); - digester.addSetProperties(prefix + "Engine/Realm"); - digester.addSetNext(prefix + "Engine/Realm", - "setRealm", - "org.apache.catalina.Realm"); + digester.addRuleSet(new RealmRuleSet(prefix + "Engine/")); digester.addObjectCreate(prefix + "Engine/Valve", null, // MUST be specified in the element Modified: tomcat/trunk/java/org/apache/catalina/startup/HostRuleSet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/HostRuleSet.java?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/HostRuleSet.java (original) +++ tomcat/trunk/java/org/apache/catalina/startup/HostRuleSet.java Wed Sep 3 15:18:39 2008 @@ -124,13 +124,7 @@ "addLifecycleListener", "org.apache.catalina.LifecycleListener"); - digester.addObjectCreate(prefix + "Host/Realm", - null, // MUST be specified in the element - "className"); - digester.addSetProperties(prefix + "Host/Realm"); - digester.addSetNext(prefix + "Host/Realm", - "setRealm", - "org.apache.catalina.Realm"); + digester.addRuleSet(new RealmRuleSet(prefix + "Host/")); digester.addObjectCreate(prefix + "Host/Valve", null, // MUST be specified in the element Added: tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java?rev=691805&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java (added) +++ tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java Wed Sep 3 15:18:39 2008 @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.startup; + + +import org.apache.tomcat.util.digester.Digester; +import org.apache.tomcat.util.digester.RuleSetBase; + + +/** + *

RuleSet for processing the contents of a Realm definition + * element. This RuleSet supports Realms such as the + * CombinedRealm that used nested Realms.

+ * + * @version $Revision$ $Date$ + */ + +public class RealmRuleSet extends RuleSetBase { + + + // ----------------------------------------------------- Instance Variables + + + /** + * The matching pattern prefix to use for recognizing our elements. + */ + protected String prefix = null; + + + // ------------------------------------------------------------ Constructor + + + /** + * Construct an instance of this RuleSet with the default + * matching pattern prefix. + */ + public RealmRuleSet() { + + this(""); + + } + + + /** + * Construct an instance of this RuleSet with the specified + * matching pattern prefix. + * + * @param prefix Prefix for matching pattern rules (including the + * trailing slash character) + */ + public RealmRuleSet(String prefix) { + + super(); + this.namespaceURI = null; + this.prefix = prefix; + + } + + + // --------------------------------------------------------- Public Methods + + + /** + *

Add the set of Rule instances defined in this RuleSet to the + * specified Digester instance, associating them with + * our namespace URI (if any). This method should only be called + * by a Digester instance.

+ * + * @param digester Digester instance to which the new Rule instances + * should be added. + */ + public void addRuleInstances(Digester digester) { + + digester.addObjectCreate(prefix + "Realm", + null, // MUST be specified in the element, + "className"); + digester.addSetProperties(prefix + "Realm"); + digester.addSetNext(prefix + "Realm", + "setRealm", + "org.apache.catalina.Realm"); + + digester.addObjectCreate(prefix + "Realm/Realm", + null, // MUST be specified in the element + "className"); + digester.addSetProperties(prefix + "Realm/Realm"); + digester.addSetNext(prefix + "Realm/Realm", + "addRealm", + "org.apache.catalina.Realm"); + + } + + +} Propchange: tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/java/org/apache/catalina/startup/RealmRuleSet.java ------------------------------------------------------------------------------ svn:keywords = Date Revision Author Id Modified: tomcat/trunk/webapps/docs/config/realm.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/realm.xml?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/realm.xml (original) +++ tomcat/trunk/webapps/docs/config/realm.xml Wed Sep 3 15:18:39 2008 @@ -565,6 +565,30 @@ Guide for more information on setting up container managed security using the JAAS Realm component.

+

Combined Realm (org.apache.catalina.realm.CombinedRealm)

+ +

CombinedRealm is an implementation of the Tomcat 6 + Realm interface that authenticates users through one or more + sub-Realms.

+ +

Using CombinedRealm gives the developer the ability to combine multiple + Realms of the same or different types. This can be used to authenticate + against different sources, provide fall back in case one Realm fails or for + any other purpose that requires multiple Realms.

+ +

Sub-realms are defined by nesting Realm elements inside the + Realm element that defines the CombinedRealm. Authentication + will be attempted against each Realm in the order they are + listed. Authentication against any Realm will be sufficient to authenticate + the user.

+ +

The Combined Realm implementation does not support any additional + attributes.

+ +

See the Container-Managed Security + Guide for more information on setting up container managed security + using the Combined Realm component.

+ @@ -573,7 +597,14 @@
-

No components may be nested inside a Realm element.

+

Combined Realm Implementation

+ +

If you are using the Combined Realm Implementation + <Realm> elements may be nested inside it.

+ +

Other Realm Implementations

+ +

No other Realm implementation supports nested components.

Modified: tomcat/trunk/webapps/docs/realm-howto.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/realm-howto.xml?rev=691805&r1=691804&r2=691805&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/realm-howto.xml (original) +++ tomcat/trunk/webapps/docs/realm-howto.xml Wed Sep 3 15:18:39 2008 @@ -1440,6 +1440,61 @@ + + +

Introduction

+ +

CombinedRealm is an implementation of the Tomcat 6 + Realm interface that authenticates users through one or more + sub-Realms.

+ +

Using CombinedRealm gives the developer the ability to combine multiple + Realms of the same or different types. This can be used to authenticate + against different sources, provide fall back in case one Realm fails or for + any other purpose that requires multiple Realms.

+ +

Sub-realms are defined by nesting Realm elements inside the + Realm element that defines the CombinedRealm. Authentication + will be attempted against each Realm in the order they are + listed. Authentication against any Realm will be sufficient to authenticate + the user.

+ +

Realm Element Attributes

+

To configure CombinedRealm, you create a <Realm> + element and nest it in your $CATALINA_BASE/conf/server.xml + file within your <Engine> or <Host>. + You can also nest inside a <Context> node in a + context.xml file. The following attributes are supported by + this implementation:

+ + + + +

The fully qualified Java class name of this Realm implementation. + You MUST specify the value + "org.apache.catalina.realm.CombinedRealm" here.

+
+ +
+ +

Example

+ +

Here is an example of how your server.xml snippet should look to use a +UserDatabase Realm and a DataSource Realm.

+ + +<Realm className="org.apache.catalina.realm.CombinedRealm" > + <Realm className="org.apache.catalina.realm.UserDatabaseRealm" + resourceName="UserDatabase"/> + <Realm className="org.apache.catalina.realm.DataSourceRealm" debug="99" + dataSourceName="jdbc/authority" + userTable="users" userNameCol="user_name" userCredCol="user_pass" + userRoleTable="user_roles" roleNameCol="role_name"/> +<Realm/> + + +
+ --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org For additional commands, e-mail: dev-help@tomcat.apache.org