1 package org.apache.turbine.services.security.ldap;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Turbine" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache Turbine", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.util.Hashtable;
58 import java.util.StringTokenizer;
59 import java.util.Vector;
60 import javax.naming.Context;
61 import javax.naming.NamingEnumeration;
62 import javax.naming.NamingException;
63 import javax.naming.directory.Attributes;
64 import javax.naming.directory.DirContext;
65 import javax.naming.directory.SearchControls;
66 import javax.naming.directory.SearchResult;
67 import org.apache.torque.util.Criteria;
68 import org.apache.turbine.om.security.User;
69 import org.apache.turbine.services.resources.TurbineResources;
70 import org.apache.turbine.services.security.UserManager;
71 import org.apache.turbine.services.security.ldap.util.ParseExceptionMessage;
72 import org.apache.turbine.util.Log;
73 import org.apache.turbine.util.security.DataBackendException;
74 import org.apache.turbine.util.security.EntityExistsException;
75 import org.apache.turbine.util.security.PasswordMismatchException;
76 import org.apache.turbine.util.security.UnknownEntityException;
77
78 /***
79 * A UserManager performs {@link org.apache.turbine.om.security.User}
80 * object related tasks on behalf of the
81 * {@link org.apache.turbine.services.security.SecurityService}.
82 *
83 * This implementation uses ldap for retrieving user data. It
84 * expects that the User interface implementation will be castable to
85 * {@link org.apache.turbine.om.BaseObject}.
86 *
87 * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
88 * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
89 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
90 * @author <a href="mailto:cberry@gluecode.com">Craig D. Berry</a>
91 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
92 * @author <a href="mailto:tadewunmi@gluecode.com">Tracy M. Adewunmi</a>
93 * @author <a href="mailto:lflournoy@gluecode.com">Leonard J. Flournoy</a>
94 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
95 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
96 * @version $Id: LDAPUserManager.java,v 1.3 2002/07/11 16:53:24 mpoeschl Exp $
97 */
98 public class LDAPUserManager implements UserManager, LDAPSecurityConstants
99 {
100 /***
101 * Check wether a specified user's account exists.
102 *
103 * The login name is used for looking up the account.
104 *
105 * @param user The user to be checked.
106 * @return true if the specified account exists
107 * @throws DataBackendException Error accessing the data backend.
108 */
109 public boolean accountExists(User user) throws DataBackendException
110 {
111 return accountExists(user.getUserName());
112 }
113
114 /***
115 *
116 * Check wether a specified user's account exists.
117 * The login name is used for looking up the account.
118 *
119 * @param usename The name of the user to be checked.
120 * @return true if the specified account exists
121 * @throws DataBackendException Error accessing the data backend.
122 */
123 public boolean accountExists(String username)
124 throws DataBackendException
125 {
126 /*!
127 * Is it possible to retrieve more then one user?
128 * Possibly the check for multiple users that is
129 * employed in the DBUserManager can be employed
130 * here.
131 */
132
133 try
134 {
135 User ldapUser = retrieve(username);
136 }
137 catch (Exception e)
138 {
139 throw new DataBackendException(
140 "Failed to check account's presence", e);
141 }
142
143 return true;
144 }
145
146 /***
147 * Retrieve a user from persistent storage using username as the
148 * key.
149 *
150 * @param username the name of the user.
151 * @return an User object.
152 * @exception UnknownEntityException if the user's account does not
153 * exist in the database.
154 * @exception DataBackendException Error accessing the data backend.
155 */
156 public User retrieve(String username)
157 throws UnknownEntityException, DataBackendException
158 {
159 String dNAttribute = TurbineResources.getString(LDAP_DN_ATTR);
160 String filter = TurbineResources.getString(LDAP_SEARCH_FLTR);
161 String userBaseSearch = TurbineResources.getString(LDAP_BASE_SERACH);
162 String dN = null;
163 String adminUser = TurbineResources.getString(LDAP_ADMIN_USRNAME);
164 String adminPassword = TurbineResources.getString(LDAP_ADMIN_PASSWRD);
165
166 adminUser = adminUser.replace('/', '=');
167 adminUser = adminUser.replace('%', ',');
168
169 User ldapUser = null;
170
171 /*
172 * The userBaseSearch string contains some
173 * characters that need to be transformed.
174 */
175 userBaseSearch = userBaseSearch.replace('/', '=');
176 userBaseSearch = userBaseSearch.replace('%', ',');
177
178 try
179 {
180 DirContext ctx = bind(adminUser, adminPassword);
181
182 /*
183 * Create the default search controls.
184 */
185 SearchControls ctls = new SearchControls();
186
187 /*
188 * Create filter.
189 */
190 filter = "("+filter + "="+username + ")";
191
192 NamingEnumeration answer =
193 ctx.search(userBaseSearch, filter, ctls);
194
195 while (answer.hasMore())
196 {
197 SearchResult sr = (SearchResult) answer.next();
198 Attributes attribs = sr.getAttributes();
199 Log.debug("attribs: " + attribs.get(dNAttribute));
200 dN = attribs.get(dNAttribute).toString();
201 Log.debug("dN: " + dN);
202 }
203
204 if (dN == null)
205 {
206 throw new UnknownEntityException("The given user: " +
207 username + "\n does not exist.");
208 }
209
210 StringTokenizer sT = new StringTokenizer(dN, ":");
211
212 while (sT.hasMoreElements())
213 {
214 dN = sT.nextToken();
215 }
216
217 dN = dN.trim();
218
219 ldapUser = new LDAPUser();
220 ldapUser.setUserName(dN);
221
222 ldapUser.setTemp("turbine.user", ldapUser);
223
224 }
225 catch (NamingException nameEx)
226 {
227 throw new DataBackendException(
228 "The LDAP server specified is unavailable");
229 }
230 return ldapUser;
231 }
232
233 /***
234 * This is currently not implemented to behave as expected. It is
235 * just here to support the interface requirement.
236 *
237 * Retrieve a set of users that meet the specified criteria.
238 *
239 * As the keys for the criteria, you should use the constants that
240 * are defined in {@link User} interface, plus the the names
241 * of the custom attributes you added to your user representation
242 * in the data storage. Use verbatim names of the attributes -
243 * without table name prefix in case of DB implementation.
244 *
245 * @param criteria The criteria of selection.
246 * @return a List of users meeting the criteria.
247 * @throws DataBackendException Error accessing the data backend.
248 */
249 public User[] retrieve(Criteria criteria) throws DataBackendException
250 {
251 Vector users = new Vector(0);
252 return (User[]) users.toArray(new User[0]);
253 }
254
255 /***
256 * Retrieve a user from persistent storage using username as the
257 * key, and authenticate the user. The implementation may chose
258 * to authenticate to the server as the user whose data is being
259 * retrieved.
260 *
261 * @param username the name of the user.
262 * @param password the user supplied password.
263 * @return an User object.
264 * @exception PasswordMismatchException if the supplied password was
265 * incorrect.
266 * @exception UnknownEntityException if the user's account does not
267 * exist in the database.
268 * @exception DataBackendException Error accessing the data backend.
269 */
270 public User retrieve(String username, String password)
271 throws PasswordMismatchException,
272 UnknownEntityException,DataBackendException
273 {
274
275 User user = retrieve(username);
276 authenticate(user, password);
277 return user;
278 }
279
280 /***
281 * This method is not yet implemented
282 *
283 * Save a User object to persistent storage. User's account is
284 * required to exist in the storage.
285 *
286 * @param user an User object to store.
287 * @exception UnknownEntityException if the user's account does not
288 * exist in the database.
289 *
290 */
291 public void store(User user)
292 throws UnknownEntityException,DataBackendException
293 {
294 if (!accountExists(user))
295 {
296 throw new UnknownEntityException("The account '"+
297 user.getUserName() + "' does not exist");
298 }
299 }
300
301 /***
302 * Authenticate a User with the specified password. If authentication
303 * is successful the method returns nothing. If there are any problems,
304 * exception was thrown.
305 *
306 * @param user a User object to authenticate.
307 * @param password the user supplied password.
308 * @exception PasswordMismatchException if the supplied password was
309 * incorrect.
310 * @exception UnknownEntityException if the user's account does not
311 * exist in the database.
312 * @exception DataBackendException Error accessing the data backend.
313 */
314 public void authenticate(User user, String password)
315 throws PasswordMismatchException,
316 UnknownEntityException, DataBackendException
317 {
318 try
319 {
320 bind(user.getUserName(), password);
321 }
322 catch (NamingException authEx)
323 {
324 throw new PasswordMismatchException(
325 "The given password for: " +
326 user.getUserName() + " is invalid\n");
327 }
328 }
329
330 /***
331 * This method is not yet implemented
332 * Change the password for an User.
333 *
334 * @param user an User to change password for.
335 * @param password the new password.
336 * @exception PasswordMismatchException if the supplied password was
337 * incorrect.
338 * @exception UnknownEntityException if the user's account does not
339 * exist in the database.
340 * @exception DataBackendException Error accessing the data backend.
341 */
342 public void changePassword(User user, String oldPassword, String newPassword)
343 throws PasswordMismatchException,
344 UnknownEntityException, DataBackendException
345 {
346 throw new DataBackendException(
347 "The method changePassword has no implementation.");
348 }
349
350 /***
351 * This method is not yet implemented
352 * Forcibly sets new password for an User.
353 *
354 * This is supposed to be used by the administrator to change the forgotten or
355 * compromised passwords. Certain implementatations of this feature
356 * would require adminstrative level access to the authenticating
357 * server / program.
358 *
359 * @param user an User to change password for.
360 * @param password the new password.
361 * @exception UnknownEntityException if the user's record does not
362 * exist in the database.
363 * @exception DataBackendException Error accessing the data backend.
364 */
365 public void forcePassword(User user, String password)
366 throws UnknownEntityException,DataBackendException
367 {
368 throw new DataBackendException(
369 "The method forcePassword has no implementation.");
370 }
371
372 /***
373 * This method is not yet implemented
374 * Creates new user account with specified attributes.
375 *
376 * @param user the object describing account to be created.
377 * @throws DataBackendException Error accessing the data backend.
378 * @throws EntityExistsException if the user account already exists.
379 */
380 public void createAccount(User user, String initialPassword)
381 throws EntityExistsException,DataBackendException
382 {
383 throw new DataBackendException(
384 "The method createAccount has no implementation.");
385 }
386
387 /***
388 * This method is not yet implemented
389 * Removes an user account from the system.
390 *
391 * @param user the object describing the account to be removed.
392 * @throws DataBackendException Error accessing the data backend.
393 * @throws UnknownEntityException if the user account is not present.
394 */
395 public void removeAccount(User user)
396 throws UnknownEntityException,DataBackendException
397 {
398 throw new DataBackendException(
399 "The method removeAccount has no implementation.");
400 }
401
402
403 /***
404 * Creats an initial context.
405 *
406 * @param ldap admin username supplied in TRP.
407 * @param ldap admin password supplied in TRP
408 * @throws DataBackendException Error accessing the data backend.
409 * @throws UnknownEntityException if the user account is not present.
410 * @throws NamingException when an error occurs with the named server.
411 */
412 public DirContext bind(String username, String password)
413 throws NamingException, DataBackendException, UnknownEntityException
414 {
415 DirContext ctx = null;
416
417 try
418 {
419 String host = TurbineResources.getString(LDAP_HOST);
420 String port = TurbineResources.getString(LDAP_PORT);
421
422 String providerURL = new String("ldap://" + host + ":" + port);
423
424 /*
425 * creating an initial context using Sun's client
426 * LDAP Provider.
427 */
428 Hashtable env = new Hashtable();
429 env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_PROVIDER);
430
431 env.put(Context.PROVIDER_URL, providerURL);
432 env.put(Context.SECURITY_AUTHENTICATION, "simple");
433 env.put(Context.SECURITY_PRINCIPAL, username);
434 env.put(Context.SECURITY_CREDENTIALS, password);
435
436 ctx = new javax.naming.directory.InitialDirContext(env);
437 Log.debug("CTX: " + ctx.toString());
438 }
439 catch (NamingException ne)
440 {
441 String errno = ParseExceptionMessage.findErrno(ne.getExplanation());
442
443 if (errno.equals("49"))
444 {
445 throw new UnknownEntityException(
446 "The given credentials for the administrator are invalid");
447 }
448 else if (errno.equals("22"))
449 {
450 throw new DataBackendException(
451 "The LDAP server specified is unavailable");
452 }
453 else
454 {
455 throw ne;
456 }
457 }
458 catch (Exception e)
459 {
460 Log.error(e);
461 }
462
463 return ctx;
464 }
465 }
This page was automatically generated by Maven