View | Details | Raw Unified | Return to bug 52500
Collapse All | Expand All

(-)org/apache/catalina/realm/UserNameRetrieverConfiguration.java (+31 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
package org.apache.catalina.realm;
19
20
21
/**
22
 * The interface <b>UserNameRetrieverConfiguration</b> allows to configure <b>UserNameRetriever</b>
23
 * 
24
 * @author Michael Furman
25
 */
26
public interface UserNameRetrieverConfiguration {
27
	 /**
28
     * @param userNameRetrieveConfiguration - the parameter for the <b>UserNameRetriever</b> configuration
29
     */
30
    public void setX509UserNameRetrieveConfiguration(String userNameRetrieveConfiguration);
31
}
(-)org/apache/catalina/realm/UserNameRetriever.java (+31 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
package org.apache.catalina.realm;
19
20
import java.security.cert.X509Certificate;
21
22
/**
23
 * The interface <b>UserNameRetriever</b> defines how to retrieve a user name from X509Certificate.
24
 * The user name is the unique part of information from the client certificate 
25
 * that used to identify the identity of the user. 
26
 * The interface is used to get the user name during the client certificate authentication
27
 * @author Michael Furman
28
 */
29
public interface UserNameRetriever {
30
	String getUserName(X509Certificate clientCert);
31
}
(-)org/apache/catalina/realm/SubjectDnRetriever.java (+188 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
19
package org.apache.catalina.realm;
20
21
import java.security.cert.X509Certificate;
22
import java.util.Arrays;
23
import java.util.List;
24
25
import javax.naming.InvalidNameException;
26
import javax.naming.ldap.LdapName;
27
import javax.naming.ldap.Rdn;
28
29
import org.apache.juli.logging.Log;
30
import org.apache.juli.logging.LogFactory;
31
32
33
/**
34
 * Implementation of <b>UserNameRetriever</b> that takes a user name from the Subject Field of the X509Certificate.
35
 * The user name is the unique part of information from the client certificate that used to identify the identity of the user. 
36
 * The Subject field (also called Subject Distinguish Name or SubjectDN) identifies the entity associated with the public key.
37
 * The Subject field contains the following relevant attributes (it can also contain other attributes).
38
 * 
39
 * Subject Attribute	Subject Attribute Description		Example
40
 * CN					Common Name							CN=Bob BobFamily
41
 * emailAddress			Email Address						emailAddress=bob@example.com
42
 * C					Country Name						C=US
43
 * ST					State or Province Name				ST=NY
44
 * L					Locality Name						L=New York
45
 * O					Organization Name					O=Work Organization
46
 * OU					Organizational Unit Name			OU=Managers
47
 * 
48
 * To retrieve the user name from the subject, you can use the entire SubjectDN field or the SubjectDN attribute.
49
 * To retrieve the user name from entire SubjectDN field use the default constructor
50
 * To retrieve the user name from the SubjectDN attribute, please provide the retrieve attribute name
51
 * The the retrieve attribute name is a code letter based on a legend defined in the certificate itself.
52
 * 
53
 * For example, the Email attribute is used to hold the User Name.
54
 * Please provide "e" or "emailAddress" for the constructor.
55
 * 
56
 * For example, the Common Name attribute is used to hold the User Name.
57
 * Please provide "CN" for the constructor.
58
 * 
59
 * @author Michael Furman
60
 *
61
 */
62
public class SubjectDnRetriever implements UserNameRetriever {
63
	/**
64
	 * Logger for this class
65
	 */
66
	protected final Log log = LogFactory.getLog(getClass());	
67
	 
68
	private static final String EMAIL_SUBJECT_ATTR = "emailAddress";
69
70
	
71
	/**
72
	 * The value is based on a legend defined in the certificate itself
73
	 * It tested with the following browsers: IE, FF and Chrome
74
	 * For new browsers may be will required to add a new legend value to the list.
75
	 */
76
	private static final List<String> EmailOptions = Arrays.asList(EMAIL_SUBJECT_ATTR.toLowerCase(), "e") ;
77
78
	
79
	private String subjectDnAttribute = null;
80
	private String subjectDnAttributeConfiguration = null;
81
	
82
    protected SubjectDnRetriever() {
83
        setSubjectDnAttribute(null);
84
    }
85
86
    protected SubjectDnRetriever(String retrieveAttr) {
87
        setSubjectDnAttribute(retrieveAttr);
88
    }
89
    
90
    public String getUserName(X509Certificate clientCert) {
91
		if (log.isDebugEnabled()) {
92
			log.debug("getUserName(X509Certificate) - start");
93
		}
94
		String subject = getSubjectDN(clientCert);
95
		String userName = null;
96
		
97
		if (subject != null) {
98
			if (log.isDebugEnabled()) {
99
				log.debug("Subject is [" + subject + "].");
100
			}
101
			if (subjectDnAttribute == null) {
102
				if (log.isDebugEnabled()) {
103
					log.debug("subjectDnAttribute is null, so return the whole subject.");
104
				}
105
				userName = subject;
106
			} else {
107
				boolean foundUserName = false;
108
				try {					
109
					LdapName ldapName = new LdapName(subject);
110
					List<Rdn> list = ldapName.getRdns();
111
					if (list != null) {
112
						for (Rdn rdn : list) {
113
							String type = rdn.getType();
114
							if (subjectDnAttribute.equalsIgnoreCase(type.toString()))  {
115
								Object value = rdn.getValue();
116
								if (value instanceof String) {
117
									userName = (String) value;
118
									foundUserName = true;									
119
									if (log.isDebugEnabled()) {
120
										log.debug("Success to retreive userName [" + userName + "].");
121
									}
122
									break;
123
								}		
124
							}
125
						}
126
					}					
127
				} catch (InvalidNameException e) {					
128
					log.info("subject [" + subject + "] is not valid name : [" + e.getMessage() + "].");
129
				}
130
				if (!foundUserName) {
131
					log.info("subject [" + subject + "] does not contain the required attribute [" + subjectDnAttributeConfiguration + "]. Return the whole subject.");
132
					userName = subject;
133
				}					
134
			}
135
			
136
		}
137
			
138
		if (log.isDebugEnabled()) {
139
			log.debug("getUserName(X509Certificate) - end; Ret is [" + userName + "].");
140
		}
141
        return userName;
142
    }
143
144
    private void setSubjectDnAttribute(String subjectDnAttributeConfiguration) {
145
		this.subjectDnAttributeConfiguration = subjectDnAttributeConfiguration;
146
		subjectDnAttribute = mapSubjectDnAttribute(subjectDnAttributeConfiguration);
147
		if (log.isDebugEnabled()) {
148
			log.debug("setSubjectDnAttribute(String) - end; subjectDnAttribute [" +  subjectDnAttribute + "]; subjectDnAttributeConfiguration [" +  subjectDnAttributeConfiguration + "]");
149
		}
150
    }
151
    
152
    
153
154
	private String  mapSubjectDnAttribute(String subjectDnAttributeConfiguration) {
155
		String ret = null;
156
		if (subjectDnAttributeConfiguration != null) {
157
			if (EmailOptions.contains(subjectDnAttributeConfiguration.toLowerCase()))  {
158
				ret = EMAIL_SUBJECT_ATTR;				
159
			} else {
160
				ret = subjectDnAttributeConfiguration;
161
			}
162
		}
163
		return ret;
164
	}
165
166
	protected String getSubjectDN(X509Certificate clientCert) {
167
		String subject = null;
168
		if (clientCert != null) {
169
			if ((clientCert.getSubjectDN()!= null) 
170
					&& (clientCert.getSubjectDN().getName() != null)) {
171
					subject = clientCert.getSubjectDN().getName();
172
			}  else {
173
				if (log.isDebugEnabled()) {
174
					log.debug("Can not getSubjectDN, SubjectDN is null");
175
				}
176
			}
177
		} else {
178
			if (log.isDebugEnabled()) {
179
				log.debug("Can not getSubjectDN, clientCert is null");
180
			}
181
		}			
182
		if (log.isDebugEnabled()) {
183
			log.debug("getSubjectDN(X509Certificate) - end; Ret is [" + subject + "].");
184
		}
185
        return subject;
186
        
187
	}
188
}
(-)org/apache/catalina/realm/UserNameRetrieverDecorator.java (+118 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
19
package org.apache.catalina.realm;
20
21
import java.security.cert.X509Certificate;
22
23
import org.apache.juli.logging.Log;
24
import org.apache.juli.logging.LogFactory;
25
26
/**
27
 * Implementation of <b>UserNameRetriever</b> that allows to create an implementation from the class name.
28
 * 
29
 * If the created implementation of <b>UserNameRetriever</b> not returns the user name, 
30
 * the UserNameRetrieverDecorator class returns the the entire SubjectDN field by defaultUserNameRetriever.
31
 * 
32
 * @author Michael Furman
33
 *
34
 */
35
public class UserNameRetrieverDecorator implements UserNameRetriever {
36
	private static final Log log = LogFactory
37
			.getLog(UserNameRetrieverDecorator.class);
38
39
	private UserNameRetriever userNameRetriever;
40
41
	private UserNameRetriever defaultUserNameRetriever;
42
43
	private boolean createdFromClassName = false;
44
45
	/**
46
	 * @param x509UserNameRetrieveConfiguration - the parameter for the configuration of UserNameRetriever. 
47
	 * @param x509UserNameRetrieverClassName - the class that implements UserNameRetriever
48
     * If the x509UserNameRetrieverClassName class implements <b>UserNameRetrieverConfiguration</b>, the userNameRetrieveConfiguration value 
49
     * will be passed to the UserNameRetriever during the creation.
50
     * The <b>UserNameRetriever</b> is used to get the user name during the client certificate authentication
51
	 */
52
	public UserNameRetrieverDecorator(String x509UserNameRetrieveConfiguration, String x509UserNameRetrieverClassName) {
53
		super();
54
		createdFromClassName = createUserNameRetrieverFromClassName(x509UserNameRetrieverClassName);
55
		if (createdFromClassName) {
56
			if (userNameRetriever instanceof UserNameRetrieverConfiguration) {
57
				// set the configuration to userNameRetriever
58
				((UserNameRetrieverConfiguration) userNameRetriever).setX509UserNameRetrieveConfiguration(x509UserNameRetrieveConfiguration);
59
			}
60
			// the defaultUserNameRetriever will be used if the created UserNameRetriever will return an empty value.
61
			defaultUserNameRetriever = new SubjectDnRetriever();
62
		} else {
63
			if (x509UserNameRetrieveConfiguration == null) {
64
				userNameRetriever = new SubjectDnRetriever();
65
			} else {
66
				userNameRetriever = new SubjectDnRetriever(x509UserNameRetrieveConfiguration);
67
			}
68
		}
69
	}
70
71
	public String getUserName(X509Certificate clientCert) {
72
		String userName = null;
73
		if (createdFromClassName) {
74
			userName = userNameRetriever.getUserName(clientCert);
75
			if ((userName == null) || ("".equals(userName))) {
76
				if (log.isDebugEnabled()) {
77
					log.debug("Created userNameRetriever returns the empty user name. Returnts the user name by defaultUserNameRetriever.");
78
				}
79
				userName = defaultUserNameRetriever.getUserName(clientCert);
80
			}
81
		} else {
82
			userName = userNameRetriever.getUserName(clientCert);
83
		}
84
		return userName;
85
	}
86
87
	@SuppressWarnings("unchecked")
88
	private boolean createUserNameRetrieverFromClassName(String x509UserNameRetrieverClassName) {
89
90
		boolean created = false;
91
		if ((x509UserNameRetrieverClassName != null)
92
				&& (x509UserNameRetrieverClassName != "")) {
93
			Class<? extends UserNameRetriever> x509UserNameRetrieverClass = null;
94
			try {
95
				x509UserNameRetrieverClass = (Class<? extends UserNameRetriever>) Class.forName(x509UserNameRetrieverClassName);
96
				userNameRetriever = x509UserNameRetrieverClass.newInstance();
97
				created = true;
98
				if (log.isDebugEnabled()) {
99
					log.debug("Success to create userNameRetriever [" + userNameRetriever + "].");
100
				}
101
			} catch (ClassCastException e) {
102
				String warnString = "Class [" + x509UserNameRetrieverClassName + "] is not instance of [" + UserNameRetriever.class.getSimpleName() + "].";
103
				log.warn(warnString);
104
			} catch (ClassNotFoundException e) {
105
				String warnString = "Class [" + x509UserNameRetrieverClassName + "] was not found.";
106
				log.warn(warnString, e);
107
			} catch (InstantiationException e) {
108
				String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
109
				log.warn(warnString);
110
			} catch (IllegalAccessException e) {
111
				String warnString = "Cannot instantiate class [" + x509UserNameRetrieverClassName + "].";
112
				log.warn(warnString);
113
			}
114
		}
115
		return created;
116
	}
117
118
}
(-)org/apache/catalina/realm/RealmBase.java (-1 / +45 lines)
Lines 150-155 Link Here
150
    protected boolean stripRealmForGss = true;
150
    protected boolean stripRealmForGss = true;
151
151
152
152
153
    /**
154
     * The <b>UserNameRetriever</b> is used to get the user name during the client certificate authentication
155
     */
156
    private UserNameRetriever userNameRetriever = null;
157
158
    
159
	/**
160
	 * The value indicates which part of constitutes the username. 
161
     * The value is a code letter based on a legend defined in the certificate itself.
162
     */
163
	 
164
	private String x509UserNameRetrieveConfiguration = null;
165
    
166
	/**
167
     * @param className - the class that implements <b>UserNameRetriever</b>
168
     * If the value is provided a realm will create UserNameRetriever.
169
	 * 
170
	 */
171
	private String x509UserNameRetrieverClassName = null;
172
    
153
    // ------------------------------------------------------------- Properties
173
    // ------------------------------------------------------------- Properties
154
174
155
175
Lines 1034-1039 Link Here
1034
        if (container != null) {
1054
        if (container != null) {
1035
            this.containerLog = container.getLogger();
1055
            this.containerLog = container.getLogger();
1036
        }
1056
        }
1057
        // Create userNameRetriever for the client certificate authentication.
1058
		userNameRetriever = new UserNameRetrieverDecorator(x509UserNameRetrieveConfiguration, x509UserNameRetrieverClassName);
1037
    }
1059
    }
1038
1060
1039
    /**
1061
    /**
Lines 1191-1197 Link Here
1191
     * Return the Principal associated with the given certificate.
1213
     * Return the Principal associated with the given certificate.
1192
     */
1214
     */
1193
    protected Principal getPrincipal(X509Certificate usercert) {
1215
    protected Principal getPrincipal(X509Certificate usercert) {
1194
        return(getPrincipal(usercert.getSubjectDN().getName()));
1216
    	// get user name using userNameRetriever
1217
		String userName = userNameRetriever.getUserName(usercert);
1218
1219
		if (log.isDebugEnabled())
1220
            log.debug("Get principal for [" + userName + "]");		
1221
        return(getPrincipal(userName));
1195
    }
1222
    }
1196
1223
1197
1224
Lines 1390-1394 Link Here
1390
            return name;
1417
            return name;
1391
        }
1418
        }
1392
    }
1419
    }
1420
    
1421
	/* (non-Javadoc)
1422
	 * @see org.apache.catalina.Realm#setX509UserNameRetrieveConfiguration(java.lang.String)
1423
	 */
1424
	public void setX509UserNameRetrieveConfiguration(String userNameRetrieveConfiguration) {
1425
		x509UserNameRetrieveConfiguration = userNameRetrieveConfiguration;
1426
	}
1393
1427
1428
	/* (non-Javadoc)
1429
	 * @see org.apache.catalina.Realm#setX509UserNameRetrieverClassName(java.lang.String)
1430
	 */
1431
	public void setX509UserNameRetrieverClassName(String className) {
1432
		 x509UserNameRetrieverClassName = className;
1433
	}
1434
	
1435
	
1436
    
1437
1394
}
1438
}
(-)org/apache/catalina/Realm.java (+16 lines)
Lines 191-194 Link Here
191
    public void removePropertyChangeListener(PropertyChangeListener listener);
191
    public void removePropertyChangeListener(PropertyChangeListener listener);
192
192
193
193
194
195
    /**
196
     * @param userNameRetrieveConfiguration - the parameter indicates which part of constitutes the username. 
197
     * The value is a code letter based on a legend defined in the certificate itself.
198
     */
199
    public void setX509UserNameRetrieveConfiguration(String userNameRetrieveConfiguration);
200
    
201
    /**
202
     * @param className - the class that implements <b>UserNameRetriever</b>
203
     * If the value is provided a realm will create UserNameRetriever.
204
     * If the class implements <b>UserNameRetrieverConfiguration</b>, the userNameRetrieveConfiguration value 
205
     * will be passed to the UserNameRetriever during the creation.
206
     * The <b>UserNameRetriever</b> is used to get the user name during the client certificate authentication
207
     */
208
    public void setX509UserNameRetrieverClassName(String className);
209
194
}
210
}

Return to bug 52500