Index: org/apache/catalina/realm/X509SubjectDnRetriever.java
===================================================================
--- org/apache/catalina/realm/X509SubjectDnRetriever.java (revision 0)
+++ org/apache/catalina/realm/X509SubjectDnRetriever.java (revision 0)
@@ -0,0 +1,198 @@
+/*
+ * 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.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+
+/**
+ * Implementation of X509UserNameRetriever that takes a user name from the Subject Field of the X509Certificate.
+ * The user name is the unique part of information from the client certificate that used to identify the identity of the user.
+ * The Subject field (also called Subject Distinguish Name or SubjectDN) identifies the entity associated with the public key.
+ * The Subject field contains the following relevant attributes (it can also contain other attributes).
+ *
+ * Subject Attribute Subject Attribute Description Example
+ * CN Common Name CN=Bob BobFamily
+ * emailAddress Email Address emailAddress=bob@example.com
+ * C Country Name C=US
+ * ST State or Province Name ST=NY
+ * L Locality Name L=New York
+ * O Organization Name O=Work Organization
+ * OU Organizational Unit Name OU=Managers
+ *
+ * To retrieve the user name from the subject, you can use the entire SubjectDN field or the SubjectDN attribute.
+ * To retrieve the user name from entire SubjectDN field use the default constructor
+ * To retrieve the user name from the SubjectDN attribute, please provide the retrieve attribute name
+ * The the retrieve attribute name is a code letter based on a legend defined in the certificate itself.
+ *
+ * For example, the Email attribute is used to hold the User Name.
+ * Please provide "e" or "emailAddress" for the constructor.
+ *
+ * For example, the Common Name attribute is used to hold the User Name.
+ * Please provide "CN" for the constructor.
+ *
+ *
+ */
+public class X509SubjectDnRetriever implements X509UserNameRetriever {
+ /**
+ * Logger for this class
+ */
+ protected final Log log = LogFactory.getLog(getClass());
+
+ private static final String EMAIL_SUBJECT_ATTR = "emailAddress";
+
+
+ /**
+ * The value is based on a legend defined in the certificate itself.
+ * It tested with the following browsers: IE, FF and Chrome
+ * For new browsers may be will required to add a new legend value to the list.
+ */
+ private static final List EmailOptions = Arrays.asList(EMAIL_SUBJECT_ATTR.toLowerCase(), "e") ;
+
+
+ private String subjectDnAttribute = null;
+ private String subjectDnAttributeConfiguration = null;
+
+ protected X509SubjectDnRetriever() {
+ setSubjectDnAttribute(null);
+ }
+
+ protected X509SubjectDnRetriever(String retrieveAttr) {
+ setSubjectDnAttribute(retrieveAttr);
+ }
+
+ public void setX509UserNameRetrieveConfiguration(String userNameRetrieveConfiguration) {
+ setSubjectDnAttribute(userNameRetrieveConfiguration);
+
+ }
+ /* (non-Javadoc)
+ * @see org.apache.catalina.realm.X509UserNameRetriever#getUserName(java.security.cert.X509Certificate)
+ */
+ public String getUserName(X509Certificate clientCert) {
+ if (log.isDebugEnabled()) {
+ log.debug("getUserName(X509Certificate) - start");
+ }
+ String subject = getSubjectDN(clientCert);
+ String userName = null;
+
+ if (subject != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Subject is [" + subject + "].");
+ }
+ if (subjectDnAttribute == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("subjectDnAttribute is null, so return the whole subject.");
+ }
+ userName = subject;
+ } else {
+ boolean foundUserName = false;
+ try {
+ LdapName ldapName = new LdapName(subject);
+ List list = ldapName.getRdns();
+ if (list != null) {
+ for (Rdn rdn : list) {
+ String type = rdn.getType();
+ if (subjectDnAttribute.equalsIgnoreCase(type.toString())) {
+ Object value = rdn.getValue();
+ if (value instanceof String) {
+ userName = (String) value;
+ foundUserName = true;
+ if (log.isDebugEnabled()) {
+ log.debug("Success to retreive userName [" + userName + "].");
+ }
+ break;
+ }
+ }
+ }
+ }
+ } catch (InvalidNameException e) {
+ log.info("subject [" + subject + "] is not valid name : [" + e.getMessage() + "].");
+ }
+ if (!foundUserName) {
+ log.info("subject [" + subject + "] does not contain the required attribute [" + subjectDnAttributeConfiguration + "]. Return the whole subject.");
+ userName = subject;
+ }
+ }
+
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("getUserName(X509Certificate) - end; Ret is [" + userName + "].");
+ }
+ return userName;
+ }
+
+ private void setSubjectDnAttribute(String subjectDnAttributeConfiguration) {
+ this.subjectDnAttributeConfiguration = subjectDnAttributeConfiguration;
+ subjectDnAttribute = mapSubjectDnAttribute(subjectDnAttributeConfiguration);
+ if (log.isDebugEnabled()) {
+ log.debug("setSubjectDnAttribute(String) - end; subjectDnAttribute [" + subjectDnAttribute + "]; subjectDnAttributeConfiguration [" + subjectDnAttributeConfiguration + "]");
+ }
+ }
+
+
+
+ private String mapSubjectDnAttribute(String subjectDnAttributeConfiguration) {
+ String ret = null;
+ if (subjectDnAttributeConfiguration != null) {
+ if (EmailOptions.contains(subjectDnAttributeConfiguration.toLowerCase())) {
+ ret = EMAIL_SUBJECT_ATTR;
+ } else {
+ ret = subjectDnAttributeConfiguration;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * @param clientCert
+ * @return the whole SubjectSN of the X509Certificate
+ */
+ protected String getSubjectDN(X509Certificate clientCert) {
+ String subject = null;
+ if (clientCert != null) {
+ if ((clientCert.getSubjectDN()!= null)
+ && (clientCert.getSubjectDN().getName() != null)) {
+ subject = clientCert.getSubjectDN().getName();
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Can not getSubjectDN, SubjectDN is null");
+ }
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Can not getSubjectDN, clientCert is null");
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("getSubjectDN(X509Certificate) - end; Ret is [" + subject + "].");
+ }
+ return subject;
+
+ }
+}
Index: org/apache/catalina/realm/RealmBase.java
===================================================================
--- org/apache/catalina/realm/RealmBase.java (revision 1245889)
+++ org/apache/catalina/realm/RealmBase.java (working copy)
@@ -150,6 +150,26 @@
protected boolean stripRealmForGss = true;
+ /**
+ * The X509UserNameRetriever is used to get the user name during the client certificate authentication
+ */
+ private X509UserNameRetriever x509UserNameRetriever = null;
+
+
+ /**
+ * The value indicates which part of constitutes the username.
+ * The value is a code letter based on a legend defined in the certificate itself.
+ */
+
+ private String x509UserNameRetrieveConfiguration = null;
+
+ /**
+ * @param className - the class that implements X509UserNameRetriever
+ * If the value is provided a realm will create X509UserNameRetriever.
+ *
+ */
+ private String x509UserNameRetrieverClassName = null;
+
// ------------------------------------------------------------- Properties
@@ -1034,6 +1054,8 @@
if (container != null) {
this.containerLog = container.getLogger();
}
+ // Create x509UserNameRetriever for the client certificate authentication.
+ createUserNameRetriever(x509UserNameRetrieveConfiguration, x509UserNameRetrieverClassName);
}
/**
@@ -1191,7 +1213,12 @@
* Return the Principal associated with the given certificate.
*/
protected Principal getPrincipal(X509Certificate usercert) {
- return(getPrincipal(usercert.getSubjectDN().getName()));
+ // get user name using x509UserNameRetriever
+ String userName = x509UserNameRetriever.getUserName(usercert);
+
+ if (log.isDebugEnabled())
+ log.debug("Get principal for [" + userName + "]");
+ return(getPrincipal(userName));
}
@@ -1390,5 +1417,104 @@
return name;
}
}
+
+ /**
+ * @param userNameRetrieveConfiguration - The value is used to configure how to get the user name during the client certificate authentication.
+ *
+ * The user name is the unique part of information from the client certificate that used to identify the identity of the user.
+ * The Subject field (also called Subject Distinguish Name or SubjectDN) identifies the entity associated with the public key.
+ * The Subject field contains the following relevant attributes (it can also contain other attributes).
+ *
+ * Subject Attribute Subject Attribute Description Example
+ * CN Common Name CN=Bob BobFamily
+ * emailAddress Email Address emailAddress=bob@example.com
+ * C Country Name C=US
+ * ST State or Province Name ST=NY
+ * L Locality Name L=New York
+ * O Organization Name O=Work Organization
+ * OU Organizational Unit Name OU=Managers
+ *
+ * To retrieve the user name from the subject, you can use the entire SubjectDN field or the SubjectDN attribute.
+ * To retrieve the user name from entire SubjectDN field leave the value empty.
+ * To retrieve the user name from the SubjectDN attribute, please provide the retrieve attribute name.
+ * The the retrieve attribute name is a code letter based on a legend defined in the certificate itself.
+ *
+ * For example, the Email attribute is used to hold the User Name.
+ * Please provide "e" or "emailAddress" for the constructor.
+ *
+ * For example, the Common Name attribute is used to hold the User Name.
+ * Please provide "CN" for the constructor.
+ *
+ */
+ public void setX509UserNameRetrieveConfiguration(String userNameRetrieveConfiguration) {
+ x509UserNameRetrieveConfiguration = userNameRetrieveConfiguration;
+ }
+ /**
+ * @param className - The Java class name that is used to override the default X509UserNameRetriever.
+ * The X509UserNameRetriever is used to get the user name during the client certificate authentication.
+ * If the value is provided a realm will create X509UserNameRetriever from the provided class.
+ * This class must implement the org.apache.catalina.realm.X509UserNameRetriever
interface.
+ */
+
+ public void setX509UserNameRetrieverClassName(String className) {
+ x509UserNameRetrieverClassName = className;
+ }
+
+
+ private void createUserNameRetriever(String x509UserNameRetrieveConfiguration, String x509UserNameRetrieverClassName) {
+
+ boolean createdFromClassName = createUserNameRetrieverFromClassName(x509UserNameRetrieverClassName);
+ if (createdFromClassName) {
+ if (x509UserNameRetriever instanceof X509SubjectDnRetriever) {
+ // set the configuration to x509UserNameRetriever
+ ((X509SubjectDnRetriever) x509UserNameRetriever).setX509UserNameRetrieveConfiguration(x509UserNameRetrieveConfiguration);
+ }
+ } else {
+ if (x509UserNameRetrieveConfiguration == null) {
+ x509UserNameRetriever = new X509SubjectDnRetriever();
+ } else {
+ x509UserNameRetriever = new X509SubjectDnRetriever(x509UserNameRetrieveConfiguration);
+ }
+ }
+ }
+
+
+
+ @SuppressWarnings("unchecked")
+ private boolean createUserNameRetrieverFromClassName(String x509UserNameRetrieverClassName) {
+
+ boolean created = false;
+ if ((x509UserNameRetrieverClassName != null)
+ && (x509UserNameRetrieverClassName != "")) {
+ Class extends X509UserNameRetriever> x509UserNameRetrieverClass = null;
+ try {
+ x509UserNameRetrieverClass = (Class extends X509UserNameRetriever>) Class.forName(x509UserNameRetrieverClassName);
+ x509UserNameRetriever = x509UserNameRetrieverClass.newInstance();
+ created = true;
+ if (log.isDebugEnabled()) {
+ log.debug("Success to create x509UserNameRetriever [" + x509UserNameRetriever + "].");
+ }
+ } catch (ClassCastException e) {
+ String warnString = "Class [" + x509UserNameRetrieverClassName + "] is not instance of [" + X509UserNameRetriever.class.getSimpleName() + "].";
+ log.warn(warnString);
+ } catch (ClassNotFoundException e) {
+ String warnString = "Class [" + x509UserNameRetrieverClassName + "] was not found.";
+ log.warn(warnString, e);
+ } catch (InstantiationException e) {
+ String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
+ log.warn(warnString);
+ } catch (IllegalAccessException e) {
+ String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
+ log.warn(warnString);
+ }
+ }
+ return created;
+ }
+
+
+
+
+
+
}
Index: org/apache/catalina/realm/X509UserNameRetriever.java
===================================================================
--- org/apache/catalina/realm/X509UserNameRetriever.java (revision 0)
+++ org/apache/catalina/realm/X509UserNameRetriever.java (revision 0)
@@ -0,0 +1,30 @@
+/*
+ * 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.cert.X509Certificate;
+
+/**
+ * The interface X509UserNameRetriever defines how to retrieve a user name from X509Certificate.
+ * The user name is the unique part of information from the client certificate
+ * that used to identify the identity of the user.
+ * The interface is used to get the user name during the client certificate authentication
+ */
+public interface X509UserNameRetriever {
+ String getUserName(X509Certificate clientCert);
+}