1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.security.spi.impl.ldap;
18
19 import java.util.Hashtable;
20
21 import javax.naming.AuthenticationException;
22 import javax.naming.Context;
23 import javax.naming.InitialContext;
24 import javax.naming.NamingEnumeration;
25 import javax.naming.NamingException;
26 import javax.naming.directory.Attribute;
27 import javax.naming.directory.Attributes;
28 import javax.naming.directory.BasicAttributes;
29 import javax.naming.directory.DirContext;
30 import javax.naming.directory.SearchControls;
31 import javax.naming.directory.SearchResult;
32
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.jetspeed.i18n.KeyedMessage;
37 import org.apache.jetspeed.security.SecurityException;
38
39 /***
40 * @see org.apache.jetspeed.security.spi.impl.ldap.LdapUserCredentialDao
41 * @author <a href="mailto:mike.long@dataline.com">Mike Long </a>, <a href="mailto:dlestrat@apache.org">David Le Strat</a>
42 */
43 public class LdapUserCredentialDaoImpl extends AbstractLdapDao implements LdapUserCredentialDao
44 {
45 /*** The logger. */
46 private static final Log logger = LogFactory.getLog(LdapUserCredentialDaoImpl.class);
47
48 /*** The password attribute. */
49
50 /***
51 * <p>
52 * Default constructor.
53 * </p>
54 *
55 * @throws SecurityException A {@link SecurityException}.
56 */
57 public LdapUserCredentialDaoImpl() throws SecurityException
58 {
59 super();
60 }
61
62 /***
63 * <p>
64 * Initializes the dao.
65 * </p>
66 *
67 * @param ldapConfig Holds the ldap binding configuration.
68 *
69 * @throws SecurityException A {@link SecurityException}.
70 */
71 public LdapUserCredentialDaoImpl(LdapBindingConfig ldapConfig) throws SecurityException
72 {
73 super(ldapConfig);
74 }
75
76 /***
77 * <p>
78 * Updates the password for the specified user.
79 * </p>
80 */
81 public void changePassword(final String uid, final String password) throws SecurityException
82 {
83 validateUid(uid);
84 validatePassword(password);
85 logger.debug("changePassword for " + uid + " with " + password);
86 String userDn = lookupByUid(uid);
87 logger.debug("userDn = " + userDn);
88 try
89 {
90 setPassword(userDn, password);
91 }
92 catch (NamingException e)
93 {
94 throw new SecurityException(e);
95 }
96 }
97
98 /***
99 * <p>
100 * Looks up the user by the UID attribute. If this lookup succeeds, this
101 * method then attempts to authenticate the user using the password,
102 * throwing an AuthenticationException if the password is incorrect or an
103 * OperationNotSupportedException if the password is empty.
104 * </p>
105 *
106 * @param uid The uid.
107 * @param password The password.
108 * @throws SecurityException Throws a {@link SecurityException}.
109 */
110 public boolean authenticate(final String uid, final String password) throws SecurityException
111 {
112 validateUid(uid);
113 validatePassword(password);
114 try
115 {
116 Hashtable env = this.ctx.getEnvironment();
117
118 String oldCredential = (String)env.get(Context.SECURITY_CREDENTIALS);
119 String oldUsername = (String)env.get(Context.SECURITY_PRINCIPAL);
120
121 String dn = lookupByUid(uid);
122 if ( dn == null )
123 throw new SecurityException(new KeyedMessage("User " + uid + " not found"));
124
125
126
127
128 if (!StringUtils.isEmpty(getRootContext()))
129 dn +="," + getRootContext();
130
131 env.put(Context.SECURITY_PRINCIPAL,dn);
132 env.put(Context.SECURITY_CREDENTIALS,password);
133 new InitialContext(env);
134 env.put(Context.SECURITY_PRINCIPAL,oldUsername);
135 env.put(Context.SECURITY_CREDENTIALS,oldCredential);
136 return true;
137 }
138 catch (AuthenticationException e)
139 {
140 return false;
141 }
142 catch (NamingException e)
143 {
144 throw new SecurityException(e);
145 }
146 }
147
148 /***
149 * @see org.apache.jetspeed.security.spi.impl.ldap.LdapUserCredentialDao#getPassword(java.lang.String)
150 */
151 public char[] getPassword(final String uid) throws SecurityException
152 {
153 validateUid(uid);
154 try
155 {
156 SearchControls cons = setSearchControls();
157 NamingEnumeration results = searchByWildcardedUid(uid, cons);
158
159 return getPassword(results, uid);
160 }
161 catch (NamingException e)
162 {
163 throw new SecurityException(e);
164 }
165 }
166
167 /***
168 * <p>
169 * Set the user's password.
170 * </p>
171 *
172 * @param userDn The user.
173 * @param password The password.
174 * @throws NamingException Throws a {@link NamingException}.
175 */
176 private void setPassword(final String userDn, final String password) throws NamingException
177 {
178 logger.debug("setPassword userDn = " + userDn);
179 String rdn = getSubcontextName(userDn);
180
181
182 logger.debug("setPassword rdn = " + rdn);
183 Attributes attrs = new BasicAttributes(false);
184
185 attrs.put(getUserPasswordAttribute(), password);
186 ctx.modifyAttributes(rdn, DirContext.REPLACE_ATTRIBUTE, attrs);
187 }
188
189 /***
190 * <p>
191 * Get the password.
192 * </p>
193 *
194 * @param results The {@link NamingEnumeration}.
195 * @param uid The uid.
196 * @return The password as an array of char.
197 * @throws NamingException Throws a {@link NamingException}.
198 */
199 private char[] getPassword(final NamingEnumeration results, final String uid) throws NamingException
200 {
201 if (!results.hasMore())
202 {
203 throw new NamingException("Could not find any user with uid[" + uid + "]");
204 }
205
206 Attributes userAttributes = getFirstUser(results);
207
208 char[] rawPassword = convertRawPassword(getAttribute(getUserPasswordAttribute(), userAttributes));
209 return rawPassword;
210 }
211
212 /***
213 * <p>
214 * Get the attribute.
215 * </p>
216 *
217 * @param attributeName The attribute name.
218 * @param userAttributes The user {@link Attributes}.
219 * @return The {@link Attribute}
220 * @throws NamingException Throws a {@link NamingException}.
221 */
222 private Attribute getAttribute(String attributeName, Attributes userAttributes) throws NamingException
223 {
224 for (NamingEnumeration ae = userAttributes.getAll(); ae.hasMore();)
225 {
226 Attribute attr = (Attribute) ae.next();
227
228 if (attr.getID().equalsIgnoreCase(attributeName))
229 {
230 return attr;
231 }
232 }
233 return null;
234 }
235
236 /***
237 * <p>
238 * This method converts an ascii password to a char array. It needs to be
239 * improved to do proper unicode conversion.
240 * </p>
241 *
242 * @param attr The {@link Attribute}.
243 */
244 private char[] convertRawPassword(Attribute attr) throws NamingException
245 {
246 char[] charPass = null;
247
248 if ( attr != null )
249 {
250 byte[] rawPass = (byte[]) attr.getAll().next();
251 charPass = new char[rawPass.length];
252
253 for (int i = 0; i < rawPass.length; i++)
254 {
255
256 charPass[i] = (char) rawPass[i];
257 }
258 }
259 else
260 {
261 charPass = new char[0];
262 }
263 return charPass;
264 }
265
266 /***
267 * <p>
268 * Gets the first matching user.
269 * </p>
270 *
271 * @param results The results to find the user in.
272 * @return The Attributes.
273 * @throws NamingException Throws a {@link NamingException}.
274 */
275 private Attributes getFirstUser(NamingEnumeration results) throws NamingException
276 {
277 SearchResult result = (SearchResult) results.next();
278 Attributes answer = result.getAttributes();
279
280 return answer;
281 }
282
283 protected String getEntryPrefix() {
284 return this.getUserIdAttribute();
285 }
286
287 protected String getSearchSuffix() {
288 return this.getUserFilter();
289 }
290
291 protected String getSearchDomain() {
292 return this.getUserFilterBase();
293 }
294
295 protected String[] getObjectClasses() {
296 return this.getUserObjectClasses();
297 }
298
299 protected String[] getAttributes() {
300 return this.getUserAttributes();
301 }
302
303 }