Author: remy.maucherat(a)jboss.com
Date: 2008-09-04 07:00:08 -0400 (Thu, 04 Sep 2008)
New Revision: 769
Added:
trunk/java/org/apache/catalina/realm/CombinedRealm.java
trunk/java/org/apache/catalina/startup/RealmRuleSet.java
Modified:
trunk/java/org/apache/catalina/realm/LocalStrings.properties
trunk/java/org/apache/catalina/startup/ContextRuleSet.java
trunk/java/org/apache/catalina/startup/EngineRuleSet.java
trunk/java/org/apache/catalina/startup/HostRuleSet.java
trunk/java/org/apache/juli/ClassLoaderLogManager.java
trunk/webapps/docs/changelog.xml
Log:
- Add combined realm.
- Port logging improvement in juli.
Added: trunk/java/org/apache/catalina/realm/CombinedRealm.java
===================================================================
--- trunk/java/org/apache/catalina/realm/CombinedRealm.java (rev
0)
+++ trunk/java/org/apache/catalina/realm/CombinedRealm.java 2008-09-04 11:00:08 UTC (rev
769)
@@ -0,0 +1,297 @@
+/*
+ * 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.jboss.logging.Logger;
+
+/**
+ * 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 Logger log = Logger.getLogger(CombinedRealm.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<Realm> realms = new LinkedList<Realm>();
+
+
+ /**
+ * 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 <code>null</code>.
+ *
+ * @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 <code>null</code>.
+ *
+ * @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 <code>null</code>.
+ *
+ * @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 <code>null</code>.
+ *
+ * @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;
+ }
+
+}
Modified: trunk/java/org/apache/catalina/realm/LocalStrings.properties
===================================================================
--- trunk/java/org/apache/catalina/realm/LocalStrings.properties 2008-09-03 18:48:04 UTC
(rev 768)
+++ trunk/java/org/apache/catalina/realm/LocalStrings.properties 2008-09-04 11:00:08 UTC
(rev 769)
@@ -70,3 +70,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
Modified: trunk/java/org/apache/catalina/startup/ContextRuleSet.java
===================================================================
--- trunk/java/org/apache/catalina/startup/ContextRuleSet.java 2008-09-03 18:48:04 UTC
(rev 768)
+++ trunk/java/org/apache/catalina/startup/ContextRuleSet.java 2008-09-04 11:00:08 UTC
(rev 769)
@@ -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: trunk/java/org/apache/catalina/startup/EngineRuleSet.java
===================================================================
--- trunk/java/org/apache/catalina/startup/EngineRuleSet.java 2008-09-03 18:48:04 UTC (rev
768)
+++ trunk/java/org/apache/catalina/startup/EngineRuleSet.java 2008-09-04 11:00:08 UTC (rev
769)
@@ -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: trunk/java/org/apache/catalina/startup/HostRuleSet.java
===================================================================
--- trunk/java/org/apache/catalina/startup/HostRuleSet.java 2008-09-03 18:48:04 UTC (rev
768)
+++ trunk/java/org/apache/catalina/startup/HostRuleSet.java 2008-09-04 11:00:08 UTC (rev
769)
@@ -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: trunk/java/org/apache/catalina/startup/RealmRuleSet.java
===================================================================
--- trunk/java/org/apache/catalina/startup/RealmRuleSet.java (rev
0)
+++ trunk/java/org/apache/catalina/startup/RealmRuleSet.java 2008-09-04 11:00:08 UTC (rev
769)
@@ -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;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a Realm
definition
+ * element. This <code>RuleSet</code> supports Realms such as the
+ * <code>CombinedRealm</code> that used nested Realms.</p>
+ *
+ * @version $Revision: 691805 $ $Date: 2008-09-04 00:18:39 +0200 (Thu, 04 Sep 2008) $
+ */
+
+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 <code>RuleSet</code> with the default
+ * matching pattern prefix.
+ */
+ public RealmRuleSet() {
+
+ this("");
+
+ }
+
+
+ /**
+ * Construct an instance of this <code>RuleSet</code> 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
+
+
+ /**
+ * <p>Add the set of Rule instances defined in this RuleSet to the
+ * specified <code>Digester</code> instance, associating them with
+ * our namespace URI (if any). This method should only be called
+ * by a Digester instance.</p>
+ *
+ * @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");
+
+ }
+
+
+}
Modified: trunk/java/org/apache/juli/ClassLoaderLogManager.java
===================================================================
--- trunk/java/org/apache/juli/ClassLoaderLogManager.java 2008-09-03 18:48:04 UTC (rev
768)
+++ trunk/java/org/apache/juli/ClassLoaderLogManager.java 2008-09-04 11:00:08 UTC (rev
769)
@@ -19,11 +19,13 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLClassLoader;
import java.security.AccessControlException;
import java.security.AccessController;
+import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Enumeration;
@@ -303,7 +305,21 @@
}
} catch (AccessControlException ace) {
// No permission to configure logging in context
- // Ignore and carry on
+ // Log and carry on
+ ClassLoaderLogInfo info =
classLoaderLoggers.get(ClassLoader.getSystemClassLoader());
+ if (info != null) {
+ Logger log = info.loggers.get("");
+ if (log != null) {
+ Permission perm = ace.getPermission();
+ if (perm instanceof FilePermission &&
perm.getActions().equals("read")) {
+ log.warning("Reading " + perm.getName() + " is not
permitted. See \"per context logging\" in the default catalina.policy
file.");
+ }
+ else {
+ log.warning("Reading logging.properties is not permitted in
some context. See \"per context logging\" in the default catalina.policy
file.");
+ log.warning("Original error was: " +
ace.getMessage());
+ }
+ }
+ }
}
if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader()))
{
String configFileStr =
System.getProperty("java.util.logging.config.file");
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2008-09-03 18:48:04 UTC (rev 768)
+++ trunk/webapps/docs/changelog.xml 2008-09-04 11:00:08 UTC (rev 769)
@@ -29,6 +29,12 @@
<fix>
<bug>45735</bug>: More consistent getETag. (remm)
</fix>
+ <fix>
+ Better logging of security exceptions reading logging configuration. (rjung)
+ </fix>
+ <add>
+ Add a special CombinedRealm which can aggregate authentication from multiple
realms. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">