|
@@ -1,17 +1,17 @@
|
|
|
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
|
|
- *
|
|
|
- * 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.
|
|
|
- */
|
|
|
+*
|
|
|
+* 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 net.sf.acegisecurity.providers.dao.ldap;
|
|
|
|
|
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
|
|
import org.springframework.dao.DataAccessException;
|
|
|
import org.springframework.dao.DataAccessResourceFailureException;
|
|
|
|
|
|
+import java.text.MessageFormat;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
import java.util.Hashtable;
|
|
@@ -44,365 +45,857 @@ import javax.naming.directory.BasicAttribute;
|
|
|
import javax.naming.directory.BasicAttributes;
|
|
|
import javax.naming.directory.DirContext;
|
|
|
import javax.naming.directory.InitialDirContext;
|
|
|
+import javax.naming.directory.SearchControls;
|
|
|
import javax.naming.directory.SearchResult;
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * This is an example <code>PasswordAuthenticationDao</code> implementation
|
|
|
- * using LDAP service for user authentication.
|
|
|
- *
|
|
|
-* <p>Example use: <br/>
|
|
|
-* <bean id="ldapDaoImpl" class="net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao"> <br/>
|
|
|
-* <property name="host"><value>sydney.ipov.info</value></property> <br/>
|
|
|
-* <property name="rootContext"><value>dc=ipov,dc=info</value></property> <br/>
|
|
|
-* <property name="userContext"><alue>ou=Users</value></property> <br/>
|
|
|
-* <property name="userAttribute"><value>uid</value></property> <br/>
|
|
|
-* </bean> <br/>
|
|
|
-* ...<br/>
|
|
|
-* <bean id="authenticationProvider" class="net.sf.acegisecurity.providers.dao.PasswordDaoAuthenticationProvider"> <br/>
|
|
|
-* <property name="passwordAuthenticationDao"><ref local="ldapDaoImpl"/></property> <br/>
|
|
|
-* </bean> <br/>
|
|
|
+* <p>
|
|
|
+* LdapPasswordAuthenticationDao allows you to authenticate user's against LDAP Directories via JNDI.
|
|
|
+* LDAP administrators have a wide variety of options available to them when configuring a server,
|
|
|
+* so the LdapPasswordAuthenticationDao has a wide variety of ways that it can be configured. </p>
|
|
|
+*
|
|
|
+* <STYLE TYPE="text/css"><!--
|
|
|
+* .syntax0 {
|
|
|
+* color: #000000;
|
|
|
+* }
|
|
|
+* .syntax1 {
|
|
|
+* color: #cc0000;
|
|
|
+* }
|
|
|
+* .syntax2 {
|
|
|
+* color: #ff8400;
|
|
|
+* }
|
|
|
+* .syntax3 {
|
|
|
+* color: #6600cc;
|
|
|
+* }
|
|
|
+* .syntax4 {
|
|
|
+* color: #cc6600;
|
|
|
+* }
|
|
|
+* .syntax5 {
|
|
|
+* color: #ff0000;
|
|
|
+* }
|
|
|
+* .syntax6 {
|
|
|
+* color: #9966ff;
|
|
|
+* }
|
|
|
+* .syntax7 {
|
|
|
+* background: #ffffcc;
|
|
|
+* color: #ff0066;
|
|
|
+* }
|
|
|
+* .syntax8 {
|
|
|
+* color: #006699;
|
|
|
+* font-weight: bold;
|
|
|
+* }
|
|
|
+* .syntax9 {
|
|
|
+* color: #009966;
|
|
|
+* font-weight: bold;
|
|
|
+* }
|
|
|
+* .syntax10 {
|
|
|
+* color: #0099ff;
|
|
|
+* font-weight: bold;
|
|
|
+* }
|
|
|
+* .syntax11 {
|
|
|
+* color: #66ccff;
|
|
|
+* font-weight: bold;
|
|
|
+* }
|
|
|
+* .syntax12 {
|
|
|
+* color: #02b902;
|
|
|
+* }
|
|
|
+* .syntax13 {
|
|
|
+* color: #ff00cc;
|
|
|
+* }
|
|
|
+* .syntax14 {
|
|
|
+* color: #cc00cc;
|
|
|
+* }
|
|
|
+* .syntax15 {
|
|
|
+* color: #9900cc;
|
|
|
+* }
|
|
|
+* .syntax16 {
|
|
|
+* color: #6600cc;
|
|
|
+* }
|
|
|
+* .syntax17 {
|
|
|
+* color: #0000ff;
|
|
|
+* }
|
|
|
+* .syntax18 {
|
|
|
+* color: #000000;
|
|
|
+* font-weight: bold;
|
|
|
+* }
|
|
|
+* -->
|
|
|
+* </STYLE>
|
|
|
+* <p>
|
|
|
+* Currently LdapPasswordAuthenticationDao authenticates a username/password pair by
|
|
|
+* 'logging in to' the LDAP server via a JNDI bind() operation.
|
|
|
+* There is some flexibility in that multiple userContexts can be set; the
|
|
|
+* LdapPasswordAuthenticationDao will attempt to bind() against each until either a bind()
|
|
|
+* operation succeeds or all userContexts have been tried. </p>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* LdapPasswordAuthenticationDao offers 3 modes for determining the roles assigned to a user
|
|
|
+* (these can be used in combination). </p>
|
|
|
+* <ul>
|
|
|
+* <li>The simplest method is to use the defaultRolename property.
|
|
|
+* If set, and no other roles are found for an authenticated user,
|
|
|
+* the value of defaultRolename is assigned as the sole role of the user.
|
|
|
+* </li>
|
|
|
+* <li>Roles can be retrieved from the context created by the user login
|
|
|
+* (the userContext against which the username/password for the user resulted in a successful bind()).
|
|
|
+* The attribute names from which roles will be retrieved in this mode are specified by the
|
|
|
+* userRolesAttributes property.
|
|
|
+* </li>
|
|
|
+* <li>Roles can be searched for within the LDAP directory.
|
|
|
+* This option requires three properties to be set:
|
|
|
+* the roleContexts property determines the context(s) which will be searched;
|
|
|
+* the roleAttributesSearchFilter property specifies an LDAP search filter
|
|
|
+* (with placeholders for the username and/or DN of the user);
|
|
|
+* while the roleNameAttributes specifies the attributes which (may) contain role information.
|
|
|
+* </li>
|
|
|
+* </ul>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* If the both the userRolesAttributes method and the roleContexts search method are used,
|
|
|
+* and if both return results, then the final list of roles will be determined by combining the two results.
|
|
|
* </p>
|
|
|
- *
|
|
|
- * @author Karel Miarka
|
|
|
- * @author Daniel Miller
|
|
|
- * @author Robert Sanders
|
|
|
- */
|
|
|
+* <p>
|
|
|
+* One final operation is performed before returning the list of GrantedAuthority
|
|
|
+* objects associated with the user: if the the upperCaseRoleNames property is set to
|
|
|
+* true the user's role names are capitalized; then the values of the rolePrefix and roleSuffix
|
|
|
+* are used to wrap any role names.
|
|
|
+* </p>
|
|
|
+* <p>
|
|
|
+* At this point a few examples will probably help clear up the confusion
|
|
|
+* that the abstract description above may have created.
|
|
|
+* Unless otherwise noted, all examples will use the following base set of assumptions:
|
|
|
+* An LDAP server reachable at the url ldap://ldap.mycompany.com:389/
|
|
|
+* and a rootContext of dc=mycompany,dc=com. The following would be you AuthenticationProvider:
|
|
|
+* <PRE>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">id</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">authenticationProvider</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">class</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">net.sf.acegisecurity.providers.dao.PasswordDaoAuthenticationProvider</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">passwordAuthenticationDao</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">ref</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">local</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">ldapDaoImpl</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* </PRE>
|
|
|
+* </p>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* First example: your users are stored under the rootContext as cn=USERNAME,ou=Users;
|
|
|
+* user objects have the attribute memberOf which contains the names of any roles they
|
|
|
+* have been granted. You would use the following bean configuration:
|
|
|
+* <PRE>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">id</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">ldapDaoImpl</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">class</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">url</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ldap://ldap.mycompany.com:389/<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">rootContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">alue</SPAN><SPAN CLASS="syntax17">></SPAN>cn={0},ou=Users,dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userRolesAttribute</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>memberOf<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* </PRE>
|
|
|
+* </p>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* Second example: users are stored under the rootContext as uid=USERNAME,ou=Users;
|
|
|
+* user object have no role information. Groups (aka roles) are stored as objects
|
|
|
+* under the context ou=Groups and have an attribute memberUid which contains the
|
|
|
+* full distinguished name of the user. You would use the following bean configuration:
|
|
|
+* <PRE>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">id</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">ldapDaoImpl</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">class</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">url</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ldap://ldap.mycompany.com:389/<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">rootContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax1"><!--</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">here</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{0}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">username</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">--></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>uid={0},ou=Users,dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ou=Groups<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax1"><!--</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">here</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{0}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">distinguished</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">name</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">(which</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">would</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">be</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">uid=USERNAME,ou=Users,dc=mycompany,cd=com</SPAN>
|
|
|
+* <SPAN CLASS="syntax1">and</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{1}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">username.</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">--></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleAttributesSearchFilter</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>(memberUid={0})<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleNameAttribute</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>memberUid<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* </PRE>
|
|
|
+* </p>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* Third example: under the rootContext your users are stored as uid=USERNAME,ou=Users.
|
|
|
+* You don't care about the roles stored in the LDAP, all you want to know is if the user
|
|
|
+* can login via LDAP. You would use the following bean configuration:
|
|
|
+* <PRE>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">id</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">ldapDaoImpl</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">class</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">url</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ldap://ldap.mycompany.com:389/<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">rootContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">alue</SPAN><SPAN CLASS="syntax17">></SPAN>cn={0},ou=Users,dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">defaultRolename</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>USER<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* </PRE>
|
|
|
+* </p>
|
|
|
+*
|
|
|
+* <p>
|
|
|
+* Forth example (something more complex): under the rootContext your users are stored in to seperate subContexts.
|
|
|
+* Your internal users are under uid=USERNAME,ou=Users; you also have client logins stored
|
|
|
+* under the context uid=USERNAME,ou=Clients. For internal users role information is stored
|
|
|
+* under the context ou=Groups and have an attribute memberUid which contains the
|
|
|
+* full distinguished name of the user. For clients, role information is stored as an attribute
|
|
|
+* memberOf as part of their user object. You could split the definitions up into two separate
|
|
|
+* LdapPasswordAuthenticationDao beans, but you could also use:
|
|
|
+* <PRE>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">id</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">ldapDaoImpl</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">class</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">net.sf.acegisecurity.providers.dao.ldap.LdapPasswordAuthenticationDao</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">url</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ldap://ldap.mycompany.com:389/<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">rootContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax1"><!--</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">here</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{0}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">username</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">--></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userContexts</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">list</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>uid={0},ou=Users,dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>uid={0},ou=Clients,dc=mycompany,dc=com<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">list</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">userRolesAttribute</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>memberOf<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleContext</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>ou=Groups<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax1"><!--</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">here</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{0}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">distinguished</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">name</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">(which</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">would</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">be</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">uid=USERNAME,ou=Users,dc=mycompany,cd=com</SPAN>
|
|
|
+* <SPAN CLASS="syntax1">and</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">{1}</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">is</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">the</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">username.</SPAN><SPAN CLASS="syntax1"> </SPAN><SPAN CLASS="syntax1">--></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleAttributesSearchFilter</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>(memberUid={0})<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17"> </SPAN><SPAN CLASS="syntax17">name</SPAN><SPAN CLASS="syntax17">=</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax13">roleNameAttributes</SPAN><SPAN CLASS="syntax13">"</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN>memberUid<SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">value</SPAN><SPAN CLASS="syntax17">></SPAN><SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">property</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* <SPAN CLASS="syntax17"><</SPAN><SPAN CLASS="syntax17">/</SPAN><SPAN CLASS="syntax17">bean</SPAN><SPAN CLASS="syntax17">></SPAN>
|
|
|
+* </PRE>
|
|
|
+* </p>
|
|
|
+*
|
|
|
+* @author Karel Miarka
|
|
|
+* @author Daniel Miller
|
|
|
+* @author Robert Sanders
|
|
|
+*/
|
|
|
public class LdapPasswordAuthenticationDao implements PasswordAuthenticationDao {
|
|
|
- //~ Static fields/initializers =============================================
|
|
|
-
|
|
|
- public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
|
|
|
- private static final transient Log log = LogFactory.getLog(LdapPasswordAuthenticationDao.class);
|
|
|
-
|
|
|
- //~ Instance fields ========================================================
|
|
|
-
|
|
|
- private String host;
|
|
|
-
|
|
|
- /** The INITIAL_CONTEXT_FACTORY for use with JNDI. */
|
|
|
- private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
|
|
|
- private String rootContext;
|
|
|
- private String userAttribute = "CN"; // ??? is this the right code??
|
|
|
- private String userContext = "CN=Users";
|
|
|
- private String[] rolesAttributes = {"memberOf"};
|
|
|
- private int port = 389;
|
|
|
-
|
|
|
- //~ Methods ================================================================
|
|
|
-
|
|
|
- /**
|
|
|
- * Set hostname or IP address of the host running LDAP server.
|
|
|
- *
|
|
|
- * @param hostname DOCUMENT ME!
|
|
|
- */
|
|
|
- public void setHost(String hostname) {
|
|
|
- this.host = hostname;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return Returns the host.
|
|
|
- */
|
|
|
- public String getHost() {
|
|
|
- return host;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @param initialContextFactory The initialContextFactory to set.
|
|
|
- */
|
|
|
- public void setInitialContextFactory(String initialContextFactory) {
|
|
|
- this.initialContextFactory = initialContextFactory;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return Returns the initialContextFactory.
|
|
|
- */
|
|
|
- public String getInitialContextFactory() {
|
|
|
- return initialContextFactory;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the port on which is running the LDAP server. <br>Default value: 389
|
|
|
- *
|
|
|
- * @param port DOCUMENT ME!
|
|
|
- */
|
|
|
- public void setPort(int port) {
|
|
|
- this.port = port;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return Returns the port.
|
|
|
- */
|
|
|
- public int getPort() {
|
|
|
- return port;
|
|
|
- }
|
|
|
-
|
|
|
- public String getProviderURL() {
|
|
|
- StringBuffer providerUrl = new StringBuffer();
|
|
|
- providerUrl.append("ldap://");
|
|
|
- providerUrl.append(this.host);
|
|
|
- providerUrl.append(":");
|
|
|
- providerUrl.append(this.port);
|
|
|
- providerUrl.append("/");
|
|
|
- providerUrl.append(this.rootContext);
|
|
|
-
|
|
|
- return providerUrl.toString();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the name of user object's attribute(s) which contains the list of
|
|
|
- * user's role names. The role is converted to upper case and a "ROLE_"
|
|
|
- * prefix is added when <code>GrantedAuthority</code> is created. Default
|
|
|
- * value: { "memberOf" }.
|
|
|
- *
|
|
|
- * @param rolesAttributes DOCUMENT ME!
|
|
|
- */
|
|
|
- public void setRolesAttributes(String[] rolesAttributes) {
|
|
|
- this.rolesAttributes = rolesAttributes;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the root context to which you attempt to log in. <br>
|
|
|
- * For example: DC=yourdomain,DC=com
|
|
|
- *
|
|
|
- * @param rootContext DOCUMENT ME!
|
|
|
- */
|
|
|
- public void setRootContext(String rootContext) {
|
|
|
- this.rootContext = rootContext;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @param userAttribute The userAttribute to set.
|
|
|
- */
|
|
|
- public void setUserAttribute(String userAttribute) {
|
|
|
- this.userAttribute = userAttribute;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return Returns the userAttribute.
|
|
|
- */
|
|
|
- public String getUserAttribute() {
|
|
|
- return userAttribute;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the context in which all users reside relative to the root context. <br>
|
|
|
- * Defalut value: "CN=Users"
|
|
|
- *
|
|
|
- * @param userContext DOCUMENT ME!
|
|
|
- */
|
|
|
- public void setUserContext(String userContext) {
|
|
|
- this.userContext = userContext;
|
|
|
- }
|
|
|
-
|
|
|
- public UserDetails loadUserByUsernameAndPassword(String username,
|
|
|
- String password) throws DataAccessException, BadCredentialsException {
|
|
|
- if ((password == null) || (password.length() == 0)) {
|
|
|
- throw new BadCredentialsException("Empty password");
|
|
|
- }
|
|
|
-
|
|
|
- Hashtable env = new Hashtable(11);
|
|
|
-
|
|
|
- env.put(Context.INITIAL_CONTEXT_FACTORY,
|
|
|
- "com.sun.jndi.ldap.LdapCtxFactory");
|
|
|
- env.put(Context.PROVIDER_URL, getProviderURL());
|
|
|
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
|
|
- env.put(Context.SECURITY_PRINCIPAL, getUserPrincipal(username));
|
|
|
- env.put(Context.SECURITY_CREDENTIALS, password);
|
|
|
-
|
|
|
- try {
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- log.debug("Connecting to " + getProviderURL() + " as "
|
|
|
- + getUserPrincipal(username));
|
|
|
- }
|
|
|
-
|
|
|
- DirContext ctx = new InitialDirContext(env);
|
|
|
-
|
|
|
- String[] attrIDs = getRolesAttributeNames();
|
|
|
- Collection roles = getRolesFromContext(ctx, userContext, username,
|
|
|
- attrIDs);
|
|
|
- ctx.close();
|
|
|
-
|
|
|
- if (roles.isEmpty()) {
|
|
|
- throw new BadCredentialsException("The user has no granted "
|
|
|
- + "authorities or the rolesAttribute is invalid");
|
|
|
- }
|
|
|
-
|
|
|
- String[] ldapRoles = (String[]) roles.toArray(new String[] {});
|
|
|
-
|
|
|
- return new User(username, password, true, true, true,
|
|
|
- getGrantedAuthorities(ldapRoles));
|
|
|
- } catch (AuthenticationException ex) {
|
|
|
- throw new BadCredentialsException(BAD_CREDENTIALS_EXCEPTION_MESSAGE,
|
|
|
- ex);
|
|
|
- } catch (CommunicationException ex) {
|
|
|
- throw new DataAccessResourceFailureException(ex.getRootCause()
|
|
|
- .getMessage(), ex);
|
|
|
- } catch (NamingException ex) {
|
|
|
- throw new DataAccessResourceFailureException(ex.getMessage(), ex);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get an array <code>GrantedAuthorities</code> given the list of roles
|
|
|
- * obtained from the LDAP context. Delegates to
|
|
|
- * <code>getGrantedAuthority(String ldapRole)</code>. This function may be
|
|
|
- * overridden in a subclass.
|
|
|
- *
|
|
|
- * @param ldapRoles DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return DOCUMENT ME!
|
|
|
- */
|
|
|
- protected GrantedAuthority[] getGrantedAuthorities(String[] ldapRoles) {
|
|
|
- GrantedAuthority[] grantedAuthorities = new GrantedAuthority[ldapRoles.length];
|
|
|
-
|
|
|
- for (int i = 0; i < ldapRoles.length; i++) {
|
|
|
- grantedAuthorities[i] = getGrantedAuthority(ldapRoles[i]);
|
|
|
- }
|
|
|
-
|
|
|
- return grantedAuthorities;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get a <code>GrantedAuthority</code> given a role obtained from the LDAP
|
|
|
- * context. If found in the LDAP role, the following characters are
|
|
|
- * converted to underscore: ',' (comma), '=' (equals), ' ' (space) This
|
|
|
- * function may be overridden in a subclass.
|
|
|
- *
|
|
|
- * @param ldapRole DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return DOCUMENT ME!
|
|
|
- */
|
|
|
- protected GrantedAuthority getGrantedAuthority(String ldapRole) {
|
|
|
- GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_"
|
|
|
- + ldapRole.toUpperCase());
|
|
|
-
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- log.debug("GrantedAuthority: " + ga);
|
|
|
- }
|
|
|
-
|
|
|
- return ga;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * DOCUMENT ME!
|
|
|
- *
|
|
|
- * @param name DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return Return true if the given name is a role attribute.
|
|
|
- */
|
|
|
- protected boolean isRoleAttribute(String name) {
|
|
|
- if (name != null) {
|
|
|
- for (int i = 0; i < rolesAttributes.length; i++) {
|
|
|
- if (name.equals(rolesAttributes[i])) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the attributes to that contain role information. This function may
|
|
|
- * be overridden in a subclass.
|
|
|
- *
|
|
|
- * @return DOCUMENT ME!
|
|
|
- */
|
|
|
- protected String[] getRolesAttributeNames() {
|
|
|
- return rolesAttributes;
|
|
|
- }
|
|
|
-
|
|
|
- protected Collection getRolesFromContext(DirContext ctx,
|
|
|
- String userContext, String username, String[] roleAttributes)
|
|
|
- throws NamingException {
|
|
|
- List roles = new ArrayList();
|
|
|
-
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- String rolesString = "";
|
|
|
-
|
|
|
- for (int i = 0; i < roleAttributes.length; i++) {
|
|
|
- rolesString += (", " + roleAttributes[i]);
|
|
|
- }
|
|
|
-
|
|
|
- log.debug("Searching user context '" + userContext + "' for roles "
|
|
|
- + "attributes: " + rolesString.substring(1));
|
|
|
- }
|
|
|
-
|
|
|
- NamingEnumeration answer = ctx.search(userContext,
|
|
|
- getUsernameAttributes(username), roleAttributes);
|
|
|
-
|
|
|
- while (answer.hasMore()) {
|
|
|
- SearchResult sr = (SearchResult) answer.next();
|
|
|
- NamingEnumeration attrs = sr.getAttributes().getAll();
|
|
|
-
|
|
|
- while (attrs.hasMore()) {
|
|
|
- Attribute attr = (Attribute) attrs.next();
|
|
|
-
|
|
|
- if (isRoleAttribute(attr.getID())) {
|
|
|
- NamingEnumeration rolesAttr = attr.getAll();
|
|
|
-
|
|
|
- while (rolesAttr.hasMore()) {
|
|
|
- String role = (String) rolesAttr.next();
|
|
|
- roles.add(role);
|
|
|
-
|
|
|
- if (log.isDebugEnabled()) {
|
|
|
- log.debug("Role read: " + attr.getID() + "=" + role);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return roles;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the <code>Context.SECURITY_PRINCIPAL</code> for the given username
|
|
|
- * string. This implementation returns the userBase for JNDI / LDAP
|
|
|
- * lookup.
|
|
|
- *
|
|
|
- * @param username DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return DOCUMENT ME!
|
|
|
- */
|
|
|
- protected String getUserPrincipal(String username) {
|
|
|
- StringBuffer principal = new StringBuffer(userAttribute);
|
|
|
- principal.append("=");
|
|
|
- principal.append(username);
|
|
|
- principal.append(",");
|
|
|
- principal.append(this.userContext);
|
|
|
- principal.append(",");
|
|
|
- principal.append(this.rootContext);
|
|
|
-
|
|
|
- return principal.toString();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the attribute(s) to match when searching for the user object. This
|
|
|
- * implementation returns a "distinguishedName" attribute with the value
|
|
|
- * returned by <code>getUserPrincipal(username)</code>. A subclass may
|
|
|
- * customize this behavior by overriding <code>getUserPrincipal</code>
|
|
|
- * and/or <code>getUsernameAttributes</code>.
|
|
|
- *
|
|
|
- * @param username DOCUMENT ME!
|
|
|
- *
|
|
|
- * @return DOCUMENT ME!
|
|
|
- */
|
|
|
- protected Attributes getUsernameAttributes(String username) {
|
|
|
- Attributes matchAttrs = new BasicAttributes(true); // ignore case
|
|
|
- matchAttrs.put(new BasicAttribute("distinguishedName",
|
|
|
- getUserPrincipal(username)));
|
|
|
-
|
|
|
- return matchAttrs;
|
|
|
- }
|
|
|
+
|
|
|
+ /** InnerClass used to keep context variable together. */
|
|
|
+ private class UserContext {
|
|
|
+ public DirContext dirContext;
|
|
|
+ public String userPrincipal;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the attribute(s) to match when searching for the user object. This
|
|
|
+ * implementation returns a "distinguishedName" attribute with the value
|
|
|
+ * returned by <code>getUserPrincipal(username)</code>. A subclass may
|
|
|
+ * customize this behavior by overriding <code>getUserPrincipal</code>
|
|
|
+ * and/or <code>getUsernameAttributes</code>.
|
|
|
+ *
|
|
|
+ * @param username
|
|
|
+ * DOCUMENT ME!
|
|
|
+ *
|
|
|
+ * @return DOCUMENT ME!
|
|
|
+ */
|
|
|
+ public Attributes getUsernameAttributes() {
|
|
|
+ Attributes matchAttrs = new BasicAttributes(true); // ignore case
|
|
|
+ matchAttrs.put(new BasicAttribute("distinguishedName", userPrincipal));
|
|
|
+ return matchAttrs;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
|
|
|
+
|
|
|
+ private static final transient Log log = LogFactory
|
|
|
+ .getLog(LdapPasswordAuthenticationDao.class);
|
|
|
+
|
|
|
+ /** Type of authentication within LDAP; default is simple. */
|
|
|
+ private String authenticationType = "simple";
|
|
|
+
|
|
|
+ /** If set to a non-null value, and a user can be bound to a LDAP Conext,
|
|
|
+ * but no role information is found then this role is automatically added.
|
|
|
+ * If null (the default) then a BadCredentialsException is thrown
|
|
|
+ *
|
|
|
+ * <p>For example; if you have an LDAP directory with no role information
|
|
|
+ * stored, you might simply want to give any user who can login a role of "USER".</p>
|
|
|
+ */
|
|
|
+ private String defaultRole = null;
|
|
|
+
|
|
|
+ /** The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory.
|
|
|
+ * Default is "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not</b>
|
|
|
+ * need to set this unless you have unusual needs.
|
|
|
+ **/
|
|
|
+ private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
|
|
|
+
|
|
|
+ /** Internal variable, concatenation */
|
|
|
+ private String providerUrl;
|
|
|
+
|
|
|
+ /** Used to build LDAP Search Filter for finding roles (in the roleContexts)
|
|
|
+ * pointing to a user. Uses MessageFormat like tokens; {0} is the
|
|
|
+ * user's DistiguishedName, {1} is the user's username.
|
|
|
+ * For more information on syntax see
|
|
|
+ * javax.naming.directory.DirContext.search(), or RFC 2254.
|
|
|
+ *
|
|
|
+ * <p>Example: if each group has an attribute 'memberUid' with values being
|
|
|
+ * the usernames of the user's in that group, then the value of this property
|
|
|
+ * would be <b>(memberUid={1})</b> </p>
|
|
|
+ **/
|
|
|
+ private String roleAttributesSearchFilter;
|
|
|
+
|
|
|
+ /** Contexts to search for role's (which point to the user id).
|
|
|
+ * <p>Example, if you have a Groups object containing Groups of users then
|
|
|
+ * the expression: <b>ou=Groups,dc=mycompany,dc=com</b> might be used;
|
|
|
+ * alternatively, if rootContext="dc=mycompany,dc=com" then simply use "ou=Groups" here.
|
|
|
+ **/
|
|
|
+ private String[] roleContexts;
|
|
|
+
|
|
|
+ /** Attribute(s) of any role object returned from the roleContexts to use as role-names.
|
|
|
+ * <b>Warning: </b> if you do role lookups using the roleContexts and
|
|
|
+ * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes
|
|
|
+ * will be returned.
|
|
|
+ *
|
|
|
+ **/
|
|
|
+ private String[] roleNameAttributes;
|
|
|
+
|
|
|
+ /** Prefix to be associated with any roles found for a user,
|
|
|
+ * defaults to an empty string.
|
|
|
+ * Older versions of this class used "ROLE_" for this value. */
|
|
|
+ private String rolePrefix = "";
|
|
|
+
|
|
|
+ /** Suffix to be associated with any roles found for a user,
|
|
|
+ * defaults to an empty string. */
|
|
|
+ private String roleSuffix = "";
|
|
|
+
|
|
|
+ /** Root context of the LDAP Connection, if any is needed.
|
|
|
+ * <p> Example: <b>dc=mycompany,dc=com</b> </p>
|
|
|
+ * <p><strong>Note: </strong> It is usually preferable to add this data as part of the
|
|
|
+ * userContexts and/or roleContexts attributes. </p>
|
|
|
+ **/
|
|
|
+ private String rootContext = "";
|
|
|
+
|
|
|
+ /** If true then all role name values returned from the directory
|
|
|
+ * will be converted to uppercase.
|
|
|
+ */
|
|
|
+ private boolean upperCaseRoleNames = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * LDAP URL (without the port) of the LDAP server to connect to; example
|
|
|
+ * <b>ldap://dir.mycompany.com:389/</b> (port 389 is the standard LDAP port).
|
|
|
+ */
|
|
|
+ private String url;
|
|
|
+
|
|
|
+ /** One or more LDAP Contexts which contain user account information, use the
|
|
|
+ * MessageFormat key {0} to denote location where the user's username should
|
|
|
+ * be inserted into the expression to create a DistiguishedName.
|
|
|
+ * <p>Example: <p>cn={0},ou=Users,dc=mycompnay,dc=com</b> </p>
|
|
|
+ * <p>Alternatively, if you had set rootContext="dc=mycompany,dc=com" then
|
|
|
+ * the first example would be rewritten as <b>cn={0},ou=Users</b>. </p>
|
|
|
+ **/
|
|
|
+ private MessageFormat[] userContexts;
|
|
|
+
|
|
|
+ /** Name(s) of the attribute(s) for a user account object
|
|
|
+ * contaning role names assigned to the user. Leave unset if there are none.
|
|
|
+ * Consult your LDAP server administrator to determine these value(s).
|
|
|
+ *
|
|
|
+ **/
|
|
|
+ private String[] userRolesAttributes;
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param results Result of searching on of the roleContexts for matches against the current user.
|
|
|
+ * @param roles List of roles the user has already been assigned.
|
|
|
+ * @throws NamingException
|
|
|
+ */
|
|
|
+ protected void addAnyRolesFound(NamingEnumeration results, Collection roles) throws NamingException {
|
|
|
+ while (results.hasMore()) {
|
|
|
+ SearchResult result = (SearchResult)results.next();
|
|
|
+ Attributes attrs = result.getAttributes();
|
|
|
+ if (attrs == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // Here we loop over the attributes returned in the SearchResult
|
|
|
+ // TODO replace with Utility method call:
|
|
|
+ NamingEnumeration e = attrs.getAll();
|
|
|
+ while (e.hasMore()) {
|
|
|
+ Attribute a = (Attribute)e.next();
|
|
|
+ for (int i = 0; i < a.size(); i++) {
|
|
|
+ roles.add( (String)a.get(i) );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the defaultRole.
|
|
|
+ */
|
|
|
+ public String getDefaultRole() {
|
|
|
+ return defaultRole;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get an array <code>GrantedAuthorities</code> given the list of roles
|
|
|
+ * obtained from the LDAP context. Delegates to
|
|
|
+ * <code>getGrantedAuthority(String ldapRole)</code>. This function may
|
|
|
+ * be overridden in a subclass.
|
|
|
+ *
|
|
|
+ * @param ldapRoles
|
|
|
+ * DOCUMENT ME!
|
|
|
+ *
|
|
|
+ * @return DOCUMENT ME!
|
|
|
+ */
|
|
|
+ protected GrantedAuthority[] getGrantedAuthorities(String[] ldapRoles) {
|
|
|
+ GrantedAuthority[] grantedAuthorities = new GrantedAuthority[ldapRoles.length];
|
|
|
+
|
|
|
+ for (int i = 0; i < ldapRoles.length; i++) {
|
|
|
+ grantedAuthorities[i] = getGrantedAuthority(ldapRoles[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return grantedAuthorities;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a <code>GrantedAuthority</code> given a role obtained from the LDAP
|
|
|
+ * context. If found in the LDAP role, the following characters are
|
|
|
+ * converted to underscore: ',' (comma), '=' (equals), ' ' (space) This
|
|
|
+ * function may be overridden in a subclass.
|
|
|
+ *
|
|
|
+ * @param ldapRole
|
|
|
+ * DOCUMENT ME!
|
|
|
+ *
|
|
|
+ * @return DOCUMENT ME!
|
|
|
+ */
|
|
|
+ protected GrantedAuthority getGrantedAuthority(String ldapRole) {
|
|
|
+ String roleName = rolePrefix + ldapRole.toUpperCase() + roleSuffix;
|
|
|
+ if (upperCaseRoleNames) {
|
|
|
+ roleName = roleName.toUpperCase();
|
|
|
+ }
|
|
|
+ GrantedAuthority ga = new GrantedAuthorityImpl(roleName);
|
|
|
+
|
|
|
+ if (log.isDebugEnabled()) {
|
|
|
+ log.debug("GrantedAuthority: " + ga);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ga;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return The InitialContextFactory for creating the root JNDI context; defaults to "com.sun.jndi.ldap.LdapCtxFactory"
|
|
|
+ */
|
|
|
+ public String getInitialContextFactory() {
|
|
|
+ return initialContextFactory;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ~ Methods
|
|
|
+ // ================================================================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Given a password, construct the Hashtable of JNDI values for a bind attempt.
|
|
|
+ */
|
|
|
+ protected Hashtable getJdniEnvironment(String password) {
|
|
|
+ Hashtable env = new Hashtable(11);
|
|
|
+ env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
|
|
|
+ env.put(Context.PROVIDER_URL, getProviderURL());
|
|
|
+ env.put(Context.SECURITY_AUTHENTICATION, authenticationType);
|
|
|
+ env.put(Context.SECURITY_CREDENTIALS, password);
|
|
|
+ return env;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return The full "Provuder" URL for the LDAP source; it should look
|
|
|
+ * something like: ldap://www.mycompany.com:389/
|
|
|
+ */
|
|
|
+ public synchronized String getProviderURL() {
|
|
|
+ if (null == this.providerUrl) {
|
|
|
+ StringBuffer providerUrl = new StringBuffer( this.url );
|
|
|
+ if (!this.url.endsWith("/")) {
|
|
|
+ providerUrl.append("/");
|
|
|
+ }
|
|
|
+ providerUrl.append(this.rootContext);
|
|
|
+ this.providerUrl = providerUrl.toString();
|
|
|
+ }
|
|
|
+ return this.providerUrl;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the roleUserAttributes.
|
|
|
+ */
|
|
|
+ public String getRoleAttributesSearchFilter() {
|
|
|
+ return roleAttributesSearchFilter;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Array of MessageFormat String's for Contexts that store role information for users.
|
|
|
+ */
|
|
|
+ public String[] getRoleContexts() {
|
|
|
+ return roleContexts;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the roleNameAttributes.
|
|
|
+ */
|
|
|
+ public String[] getRoleNameAttributes() {
|
|
|
+ return roleNameAttributes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the rolePrefix.
|
|
|
+ */
|
|
|
+ public String getRolePrefix() {
|
|
|
+ return rolePrefix;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ protected Collection getRolesFromRoleSearch(UserContext userContext, String username, String[] roleAttributes) {
|
|
|
+ if ((null == roleContexts) || (roleContexts.length == 0)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String[] searchFilterVars = new String[] {userContext.userPrincipal, username};
|
|
|
+
|
|
|
+ SearchControls controls = new SearchControls();
|
|
|
+ controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
|
|
+ controls.setReturningAttributes(roleAttributes);
|
|
|
+
|
|
|
+ List roles = new ArrayList();
|
|
|
+ for (int i = 0; i < roleContexts.length; i++) {
|
|
|
+ try {
|
|
|
+ NamingEnumeration results = userContext.dirContext.search(
|
|
|
+ roleContexts[i], roleAttributesSearchFilter, searchFilterVars, controls);
|
|
|
+ addAnyRolesFound(results, roles);
|
|
|
+ } catch (NamingException e) {
|
|
|
+ if (log.isInfoEnabled()) {
|
|
|
+ log.info("Unable to find user-role match in context = " + roleContexts[i], e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return roles;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Looksup any roleAttributes associated with the user's DN within the DirContext.
|
|
|
+ *
|
|
|
+ * @param userContext
|
|
|
+ * UserContext Object containing DirContext in which to operate, and the user's DistinguishedName.
|
|
|
+ * @param roleAttributes
|
|
|
+ * Names of all attributes to search for role name information.
|
|
|
+ * @return Collection of roles granted to the user within the JNDI Context.
|
|
|
+ * @throws NamingException
|
|
|
+ */
|
|
|
+ protected Collection getRolesFromUserContext(UserContext userContext, String[] roleAttributes)
|
|
|
+ throws NamingException {
|
|
|
+ List roles = new ArrayList();
|
|
|
+ if (roleAttributes != null) {
|
|
|
+ if (log.isDebugEnabled()) {
|
|
|
+ StringBuffer rolesString = new StringBuffer();
|
|
|
+
|
|
|
+ for (int i = 0; i < roleAttributes.length; i++) {
|
|
|
+ rolesString.append(", ");
|
|
|
+ rolesString.append(roleAttributes[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.debug("Searching user context '" + userContext.userPrincipal + "' for roles "
|
|
|
+ + "attributes: " + rolesString.substring(1));
|
|
|
+ }
|
|
|
+ Attributes attrs = userContext.dirContext.getAttributes(userContext.userPrincipal, roleAttributes);
|
|
|
+ NamingEnumeration roleEnum = attrs.getAll();
|
|
|
+ while (roleEnum.hasMore()) {
|
|
|
+ Attribute roleAttr = (Attribute)roleEnum.next();
|
|
|
+ for (int i = 0; i < roleAttr.size(); i++) {
|
|
|
+ roles.add( roleAttr.get(i) );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return roles;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the roleSuffix.
|
|
|
+ */
|
|
|
+ public String getRoleSuffix() {
|
|
|
+ return roleSuffix;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the rootContext which to connect to;
|
|
|
+ * typically it could look something like: dc=mycompany,dc=com.
|
|
|
+ */
|
|
|
+ public String getRootContext() {
|
|
|
+ return rootContext;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return The LDAP URL to conntect to; example: ldap://ldap.mycompany.com:389/
|
|
|
+ */
|
|
|
+ public String getURL() {
|
|
|
+ return url;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Attempts to bind to the userContexts; returning on the first successful bind;
|
|
|
+ * or failing with a BadCredentialsException.
|
|
|
+ * @param username
|
|
|
+ * @param password
|
|
|
+ * @return UserContext, an innerclass holding the DirContext, and the user's LDAP Principal String.
|
|
|
+ * @throws NamingException
|
|
|
+ * @throws BadCredentialsException
|
|
|
+ */
|
|
|
+ protected UserContext getUserContext(String username, String password) throws NamingException, BadCredentialsException {
|
|
|
+ Hashtable env = getJdniEnvironment(password);
|
|
|
+ UserContext userContext = new UserContext();
|
|
|
+ for (int i = 0; i < userContexts.length; i++) {
|
|
|
+ env.remove(Context.SECURITY_PRINCIPAL);
|
|
|
+ userContext.userPrincipal = userContexts[i].format(new String[]{username});
|
|
|
+ env.put(Context.SECURITY_PRINCIPAL, userContext.userPrincipal);
|
|
|
+ try {
|
|
|
+ userContext.dirContext = new InitialDirContext(env);
|
|
|
+ if (userContext.dirContext != null) {
|
|
|
+ return userContext;
|
|
|
+ }
|
|
|
+ } catch (AuthenticationException ax) {
|
|
|
+ if (log.isInfoEnabled()) {
|
|
|
+ log.info("Authentication exception for user.", ax);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new BadCredentialsException(BAD_CREDENTIALS_EXCEPTION_MESSAGE);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the userContexts.
|
|
|
+ */
|
|
|
+ public String[] getUserContexts() {
|
|
|
+ String[] formats = new String[userContexts.length];
|
|
|
+ for (int i = 0; i < userContexts.length; i++) {
|
|
|
+ formats[i] = userContexts[i].toPattern();
|
|
|
+ }
|
|
|
+ return formats;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the userRolesAttributes.
|
|
|
+ */
|
|
|
+ public String[] getUserRolesAttributes() {
|
|
|
+ return userRolesAttributes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @FIXME When using a search (see getRolesFromContext()) I don't think this
|
|
|
+ * extra check is needed; JNDI should be responible for returning
|
|
|
+ * only the attributes requested (or maybe I don't understand JNDI
|
|
|
+ * well enough).
|
|
|
+ *
|
|
|
+ * @param Name/Id
|
|
|
+ * of the JNDI Attribute.
|
|
|
+ *
|
|
|
+ * @return Return true if the given name is a role attribute.
|
|
|
+ */
|
|
|
+ protected boolean isRoleAttribute(String name) {
|
|
|
+ log.info("Checking rolename: " + name);
|
|
|
+ if (name != null) {
|
|
|
+ for (int i = 0; i < userRolesAttributes.length; i++) {
|
|
|
+ if (name.equals(userRolesAttributes[i])) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return Returns the upperCaseRoleNames.
|
|
|
+ */
|
|
|
+ public boolean isUpperCaseRoleNames() {
|
|
|
+ return upperCaseRoleNames;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public UserDetails loadUserByUsernameAndPassword(String username,
|
|
|
+ String password) throws DataAccessException,
|
|
|
+ BadCredentialsException {
|
|
|
+ if ((password == null) || (password.length() == 0)) {
|
|
|
+ throw new BadCredentialsException("Empty password");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (log.isDebugEnabled()) {
|
|
|
+ log.debug("Connecting to " + getProviderURL() + " as " + username);
|
|
|
+ }
|
|
|
+
|
|
|
+ UserContext userContext = getUserContext(username, password);
|
|
|
+
|
|
|
+ Collection roles = getRolesFromUserContext(userContext, getUserRolesAttributes());
|
|
|
+ Collection roles2 = getRolesFromRoleSearch(userContext, username, getRoleNameAttributes());
|
|
|
+ if (null != roles2) {
|
|
|
+ roles.addAll(roles2);
|
|
|
+ }
|
|
|
+
|
|
|
+ userContext.dirContext.close();
|
|
|
+
|
|
|
+
|
|
|
+ if (roles.isEmpty()) {
|
|
|
+ if (null == defaultRole) {
|
|
|
+ throw new BadCredentialsException("The user has no granted "
|
|
|
+ + "authorities or the rolesAttribute is invalid");
|
|
|
+ } else {
|
|
|
+ roles.add(defaultRole);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ String[] ldapRoles = (String[]) roles.toArray(new String[] {});
|
|
|
+
|
|
|
+ return new User(username, password, true, true, true,
|
|
|
+ getGrantedAuthorities(ldapRoles));
|
|
|
+ } catch (AuthenticationException ex) {
|
|
|
+ throw new BadCredentialsException(
|
|
|
+ BAD_CREDENTIALS_EXCEPTION_MESSAGE, ex);
|
|
|
+ } catch (CommunicationException ex) {
|
|
|
+ throw new DataAccessResourceFailureException(ex.getRootCause()
|
|
|
+ .getMessage(), ex);
|
|
|
+ } catch (NamingException ex) {
|
|
|
+ throw new DataAccessResourceFailureException(ex.getMessage(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** If set to a non-null value, and a user can be bound to a LDAP Conext,
|
|
|
+ * but no role information is found then this role is automatically added.
|
|
|
+ * If null (the default) then a BadCredentialsException is thrown
|
|
|
+ *
|
|
|
+ * <p>For example; if you have an LDAP directory with no role information
|
|
|
+ * stored, you might simply want to give any user who can login a role of "USER".</p>
|
|
|
+ *
|
|
|
+ * @param defaultRole The defaultRole to set.
|
|
|
+ */
|
|
|
+ public void setDefaultRole(String defaultRole) {
|
|
|
+ this.defaultRole = defaultRole;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory.
|
|
|
+ * Default is "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not</b>
|
|
|
+ * need to set this unless you have unusual needs.
|
|
|
+ *
|
|
|
+ * @param initialContextFactory The InitialContextFactory for creating the root JNDI context;
|
|
|
+ * defaults to "com.sun.jndi.ldap.LdapCtxFactory"
|
|
|
+ */
|
|
|
+ public void setInitialContextFactory(String initialContextFactory) {
|
|
|
+ this.initialContextFactory = initialContextFactory;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Name(s) of the attribute(s) for a user account object
|
|
|
+ * contaning role names assigned to the user. Leave unset if there are none.
|
|
|
+ * Consult your LDAP server administrator to determine these value(s).
|
|
|
+ *
|
|
|
+ * @param roleUserAttributes
|
|
|
+ * The roleUserAttributes to set.
|
|
|
+ */
|
|
|
+ public void setRoleAttributesSearchFilter(String roleAttributesSearchArgs) {
|
|
|
+ this.roleAttributesSearchFilter = roleAttributesSearchArgs;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Shortcut for setRoleContexts( new String[]{roleContext} ); */
|
|
|
+ public void setRoleContext(String roleContext) {
|
|
|
+ setRoleContexts( new String[]{roleContext} );
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Contexts to search for role's (which point to the user id).
|
|
|
+ * <p>Example, if you have a Groups object containing Groups of users then
|
|
|
+ * the expression: <b>ou=Groups,dc=mycompany,dc=com</b> might be used;
|
|
|
+ * alternatively, if rootContext="dc=mycompany,dc=com" then simply use "ou=Groups" here.
|
|
|
+ *
|
|
|
+ * @param roleContexts Array of MessageFormat String's for Contexts that store role information for users.
|
|
|
+ */
|
|
|
+ public void setRoleContexts(String[] roleContexts) {
|
|
|
+ this.roleContexts = roleContexts;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Used to build LDAP Search Filter for finding roles (in the roleContexts)
|
|
|
+ * pointing to a user. Uses MessageFormat like tokens; {0} is the
|
|
|
+ * user's DistiguishedName, {1} is the user's username.
|
|
|
+ * For more information on syntax see
|
|
|
+ * javax.naming.directory.DirContext.search(), or RFC 2254.
|
|
|
+ *
|
|
|
+ * <p>Example: if each group has an attribute 'memberUid' with values being
|
|
|
+ * the usernames of the user's in that group, then the value of this property
|
|
|
+ * would be <b>(memberUid={1})</b> </p>
|
|
|
+ *
|
|
|
+ * @param roleNameAttributes The roleNameAttributes to set.
|
|
|
+ */
|
|
|
+ public void setRoleNameAttribute(String roleNameAttribute) {
|
|
|
+ setRoleNameAttributes( new String[] {roleNameAttribute} );
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Attribute(s) of any role object returned from the roleContexts to use as role-names.
|
|
|
+ * <b>Warning: </b> if you do role lookups using the roleContexts and
|
|
|
+ * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes
|
|
|
+ * will be returned.
|
|
|
+ *
|
|
|
+ * @param roleNameAttributes The roleNameAttributes to set.
|
|
|
+ */
|
|
|
+ public void setRoleNameAttributes(String[] roleNameAttributes) {
|
|
|
+ this.roleNameAttributes = roleNameAttributes;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Prefix to be associated with any roles found for a user,
|
|
|
+ * defaults to an empty string.
|
|
|
+ * Older versions of this class used "ROLE_" for this value.
|
|
|
+ *
|
|
|
+ * @param rolePrefix The rolePrefix to set.
|
|
|
+ */
|
|
|
+ public void setRolePrefix(String rolePrefix) {
|
|
|
+ this.rolePrefix = rolePrefix;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Suffix to be associated with any roles found for a user,
|
|
|
+ * defaults to an empty string.
|
|
|
+ *
|
|
|
+ * @param roleSuffix The roleSuffix to set.
|
|
|
+ */
|
|
|
+ public void setRoleSuffix(String roleSuffix) {
|
|
|
+ this.roleSuffix = roleSuffix;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Root context of the LDAP Connection, if any is needed.
|
|
|
+ * <p> Example: <b>dc=mycompany,dc=com</b> </p>
|
|
|
+ * <p><strong>Note: </strong> It is usually preferable to add this data as part of the
|
|
|
+ * userContexts and/or roleContexts attributes. </p>
|
|
|
+ *
|
|
|
+ * @param rootContext The rootContext which to connect to;
|
|
|
+ * typically it could look something like: dc=mycompany,dc=com.
|
|
|
+ */
|
|
|
+ public void setRootContext(String rootContext) {
|
|
|
+ this.rootContext = rootContext;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** If true then all role name values returned from the directory
|
|
|
+ * will be converted to uppercase.
|
|
|
+ *
|
|
|
+ * @param upperCaseRoleNames The upperCaseRoleNames to set.
|
|
|
+ */
|
|
|
+ public void setUpperCaseRoleNames(boolean upperCaseRoleNames) {
|
|
|
+ this.upperCaseRoleNames = upperCaseRoleNames;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param host The LDAP URL to conntect to; example: ldap://ldap.mycompany.com:389/
|
|
|
+ */
|
|
|
+ public void setURL(String url) {
|
|
|
+ this.url = url;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Shortcut for setUserContexts( new String[]{userContext} ); */
|
|
|
+ public void setUserContext(String userContext) {
|
|
|
+ setUserContexts( new String[]{userContext} );
|
|
|
+ }
|
|
|
+
|
|
|
+ /** One or more LDAP Contexts which contain user account information, use the
|
|
|
+ * MessageFormat key {0} to denote location where the user's username should
|
|
|
+ * be inserted into the expression to create a DistiguishedName.
|
|
|
+ * <p>Example: <p>cn={0},ou=Users,dc=mycompnay,dc=com</b> </p>
|
|
|
+ * <p>Alternatively, if you had set rootContext="dc=mycompany,dc=com" then
|
|
|
+ * the first example would be rewritten as <b>cn={0},ou=Users</b>. </p>
|
|
|
+ *
|
|
|
+ * @param userContexts
|
|
|
+ * The userContexts to set.
|
|
|
+ */
|
|
|
+ public void setUserContexts(String[] userContexts) {
|
|
|
+ this.userContexts = new MessageFormat[userContexts.length];
|
|
|
+ for (int i = 0; i < userContexts.length; i++) {
|
|
|
+ this.userContexts[i] = new MessageFormat(userContexts[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Shortcut for setUserRolesAttributes(new String[]{userRolesAttribute}); */
|
|
|
+ public void setUserRolesAttribute(String userRolesAttribute) {
|
|
|
+ this.userRolesAttributes = new String[]{userRolesAttribute};
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Attribute(s) of any role object returned from the roleContexts to use as role-names.
|
|
|
+ * <b>Warning: </b> if you do role lookups using the roleContexts and
|
|
|
+ * roleAttributesSearchFilter then you need to set roleNameAttributes or ALL attributes
|
|
|
+ * will be returned.
|
|
|
+ *
|
|
|
+ * @param userRolesAttributes
|
|
|
+ * The userRolesAttributes to set.
|
|
|
+ */
|
|
|
+ public void setUserRolesAttributes(String[] userRolesAttributes) {
|
|
|
+ this.userRolesAttributes = userRolesAttributes;
|
|
|
+ }
|
|
|
+
|
|
|
}
|