1 package org.apache.turbine.services.security.db;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.Hashtable;
20 import java.util.Iterator;
21 import java.util.List;
22
23 import org.apache.commons.configuration.Configuration;
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.torque.om.BaseObject;
26 import org.apache.torque.om.ObjectKey;
27 import org.apache.torque.om.Persistent;
28 import org.apache.torque.util.Criteria;
29 import org.apache.turbine.om.security.User;
30 import org.apache.turbine.om.security.peer.TurbineUserPeer;
31 import org.apache.turbine.services.security.TurbineSecurity;
32 import org.apache.turbine.services.security.UserManager;
33 import org.apache.turbine.util.db.map.TurbineMapBuilder;
34 import org.apache.turbine.util.security.DataBackendException;
35 import org.apache.turbine.util.security.EntityExistsException;
36 import org.apache.turbine.util.security.PasswordMismatchException;
37 import org.apache.turbine.util.security.UnknownEntityException;
38
39 /***
40 * An UserManager performs {@link org.apache.turbine.om.security.User}
41 * objects related tasks on behalf of the
42 * {@link org.apache.turbine.services.security.BaseSecurityService}.
43 *
44 * This implementation uses a relational database for storing user data. It
45 * expects that the User interface implementation will be castable to
46 * {@link org.apache.torque.om.BaseObject}.
47 *
48 * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a>
49 * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
50 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
51 * @author <a href="mailto:cberry@gluecode.com">Craig D. Berry</a>
52 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
53 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
54 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
55 * @version $Id: DBUserManager.java 278824 2005-09-05 20:01:15Z henning $
56 */
57 public class DBUserManager
58 implements UserManager
59 {
60 /***
61 * Initializes the UserManager
62 *
63 * @param conf A Configuration object to init this Manager
64 */
65 public void init(Configuration conf)
66 {
67
68 }
69
70 /***
71 * Check whether a specified user's account exists.
72 *
73 * The login name is used for looking up the account.
74 *
75 * @param user The user to be checked.
76 * @return true if the specified account exists
77 * @throws DataBackendException if there was an error accessing
78 * the data backend.
79 */
80 public boolean accountExists(User user)
81 throws DataBackendException
82 {
83 return accountExists(user.getName());
84 }
85
86 /***
87 * Check whether a specified user's account exists.
88 *
89 * The login name is used for looking up the account.
90 *
91 * @param userName The name of the user to be checked.
92 * @return true if the specified account exists
93 * @throws DataBackendException if there was an error accessing
94 * the data backend.
95 */
96 public boolean accountExists(String userName)
97 throws DataBackendException
98 {
99 Criteria criteria = new Criteria();
100 criteria.add(TurbineUserPeer.USERNAME, userName);
101 List users;
102 try
103 {
104 users = TurbineUserPeer.doSelect(criteria);
105 }
106 catch (Exception e)
107 {
108 throw new DataBackendException(
109 "Failed to check account's presence", e);
110 }
111 if (users.size() > 1)
112 {
113 throw new DataBackendException(
114 "Multiple Users with same username '" + userName + "'");
115 }
116 return (users.size() == 1);
117 }
118
119 /***
120 * Retrieve a user from persistent storage using username as the
121 * key.
122 *
123 * @param userName the name of the user.
124 * @return an User object.
125 * @exception UnknownEntityException if the user's account does not
126 * exist in the database.
127 * @exception DataBackendException if there is a problem accessing the
128 * storage.
129 */
130 public User retrieve(String userName)
131 throws UnknownEntityException, DataBackendException
132 {
133 Criteria criteria = new Criteria();
134 criteria.add(TurbineUserPeer.USERNAME, userName);
135
136 List users = retrieveList(criteria);
137
138 if (users.size() > 1)
139 {
140 throw new DataBackendException(
141 "Multiple Users with same username '" + userName + "'");
142 }
143 if (users.size() == 1)
144 {
145 return (User) users.get(0);
146 }
147 throw new UnknownEntityException("Unknown user '" + userName + "'");
148 }
149
150 /***
151 * Retrieve a user from persistent storage using the primary key
152 *
153 * @param key The primary key object
154 * @return an User object.
155 * @throws UnknownEntityException if the user's record does not
156 * exist in the database.
157 * @throws DataBackendException if there is a problem accessing the
158 * storage.
159 */
160 public User retrieveById(Object key)
161 throws UnknownEntityException, DataBackendException
162 {
163 Criteria criteria = new Criteria();
164 criteria.add(TurbineUserPeer.USER_ID, key);
165
166 List users = retrieveList(criteria);
167
168 if (users.size() > 1)
169 {
170 throw new DataBackendException(
171 "Multiple Users with same unique Key '" + String.valueOf(key) + "'");
172 }
173 if (users.size() == 1)
174 {
175 return (User) users.get(0);
176 }
177 throw new UnknownEntityException("Unknown user with key '" + String.valueOf(key) + "'");
178 }
179
180 /***
181 * Retrieve a list of users that meet the specified criteria.
182 *
183 * As the keys for the criteria, you should use the constants that
184 * are defined in {@link User} interface, plus the names
185 * of the custom attributes you added to your user representation
186 * in the data storage. Use verbatim names of the attributes -
187 * without table name prefix in case of Torque implementation.
188 *
189 * @param criteria The criteria of selection.
190 * @return a List of users meeting the criteria.
191 * @throws DataBackendException if there is a problem accessing the
192 * storage.
193 */
194 public List retrieveList(Criteria criteria)
195 throws DataBackendException
196 {
197 for (Iterator keys = criteria.keySet().iterator(); keys.hasNext(); )
198 {
199 String key = (String) keys.next();
200
201
202 Criteria.Criterion[] criterion = criteria
203 .getCriterion(key).getAttachedCriterion();
204
205 for (int i = 0; i < criterion.length; i++)
206 {
207 if (StringUtils.isEmpty(criterion[i].getTable()))
208 {
209 criterion[i].setTable(TurbineUserPeer.getTableName());
210 }
211 }
212 }
213 List users = null;
214 try
215 {
216 users = TurbineUserPeer.doSelect(criteria);
217 }
218 catch (Exception e)
219 {
220 throw new DataBackendException("Failed to retrieve users", e);
221 }
222 return users;
223 }
224
225 /***
226 * Retrieve a set of users that meet the specified criteria.
227 *
228 * As the keys for the criteria, you should use the constants that
229 * are defined in {@link User} interface, plus the names
230 * of the custom attributes you added to your user representation
231 * in the data storage. Use verbatim names of the attributes -
232 * without table name prefix in case of DB implementation.
233 *
234 * @param criteria The criteria of selection.
235 * @return a List of users meeting the criteria.
236 * @throws DataBackendException if there is a problem accessing the
237 * storage.
238 * @deprecated Use <a href="#retrieveList">retrieveList</a> instead.
239 */
240 public User[] retrieve(Criteria criteria)
241 throws DataBackendException
242 {
243 return (User []) retrieveList(criteria).toArray(new User[0]);
244 }
245
246 /***
247 * Retrieve a user from persistent storage using username as the
248 * key, and authenticate the user. The implementation may chose
249 * to authenticate to the server as the user whose data is being
250 * retrieved.
251 *
252 * @param userName the name of the user.
253 * @param password the user supplied password.
254 * @return an User object.
255 * @exception PasswordMismatchException if the supplied password was
256 * incorrect.
257 * @exception UnknownEntityException if the user's account does not
258 * exist in the database.
259 * @exception DataBackendException if there is a problem accessing the
260 * storage.
261 */
262 public User retrieve(String userName, String password)
263 throws PasswordMismatchException, UnknownEntityException,
264 DataBackendException
265 {
266 User user = retrieve(userName);
267 authenticate(user, password);
268 return user;
269 }
270
271 /***
272 * Save an User object to persistent storage. User's account is
273 * required to exist in the storage.
274 *
275 * @param user an User object to store.
276 * @exception UnknownEntityException if the user's account does not
277 * exist in the database.
278 * @exception DataBackendException if there is a problem accessing the
279 * storage.
280 */
281 public void store(User user)
282 throws UnknownEntityException, DataBackendException
283 {
284 if (!accountExists(user))
285 {
286 throw new UnknownEntityException("The account '" +
287 user.getName() + "' does not exist");
288 }
289
290 Criteria criteria = TurbineUserPeer.buildCriteria(user);
291 try
292 {
293 TurbineUserPeer.doUpdate(criteria);
294 }
295 catch (Exception e)
296 {
297 throw new DataBackendException("Failed to save user object", e);
298 }
299 }
300
301 /***
302 * Saves User data when the session is unbound. The user account is required
303 * to exist in the storage.
304 *
305 * LastLogin, AccessCounter, persistent pull tools, and any data stored
306 * in the permData hashtable that is not mapped to a column will be saved.
307 *
308 * @exception UnknownEntityException if the user's account does not
309 * exist in the database.
310 * @exception DataBackendException if there is a problem accessing the
311 * storage.
312 */
313 public void saveOnSessionUnbind(User user)
314 throws UnknownEntityException, DataBackendException
315 {
316 if (!user.hasLoggedIn())
317 {
318 return;
319 }
320
321 if (!accountExists(user))
322 {
323 throw new UnknownEntityException("The account '" +
324 user.getName() + "' does not exist");
325 }
326 Criteria crit = new Criteria();
327 if (!((Persistent) user).isNew())
328 {
329 crit.add(TurbineUserPeer.USER_ID, ((Persistent) user).getPrimaryKey());
330 }
331
332 Hashtable permStorage = (Hashtable) user.getPermStorage().clone();
333 crit.add(TurbineUserPeer.LAST_LOGIN, permStorage.remove(TurbineUserPeer.LAST_LOGIN));
334
335
336
337
338 for (int i = 1; i < TurbineUserPeer.columnNames.length; i++)
339 {
340 if (permStorage.containsKey(TurbineUserPeer.columnNames[i]))
341 {
342 permStorage.remove(TurbineUserPeer.columnNames[i]);
343 }
344 }
345 crit.add(TurbineUserPeer.OBJECT_DATA, permStorage);
346
347 try
348 {
349 TurbineUserPeer.doUpdate(crit);
350 }
351 catch (Exception e)
352 {
353 throw new DataBackendException("Failed to save user object", e);
354 }
355
356 }
357
358 /***
359 * Authenticate an User with the specified password. If authentication
360 * is successful the method returns nothing. If there are any problems,
361 * exception was thrown.
362 *
363 * @param user an User object to authenticate.
364 * @param password the user supplied password.
365 * @exception PasswordMismatchException if the supplied password was
366 * incorrect.
367 * @exception UnknownEntityException if the user's account does not
368 * exist in the database.
369 * @exception DataBackendException if there is a problem accessing the
370 * storage.
371 */
372 public void authenticate(User user, String password)
373 throws PasswordMismatchException, UnknownEntityException,
374 DataBackendException
375 {
376 if (!accountExists(user))
377 {
378 throw new UnknownEntityException("The account '" +
379 user.getName() + "' does not exist");
380 }
381
382
383
384
385
386
387
388
389
390
391 if (!TurbineSecurity.checkPassword(password, user.getPassword()))
392 {
393 throw new PasswordMismatchException("The passwords do not match");
394 }
395 }
396
397 /***
398 * Change the password for an User. The user must have supplied the
399 * old password to allow the change.
400 *
401 * @param user an User to change password for.
402 * @param oldPassword The old password to verify
403 * @param newPassword The new password to set
404 * @exception PasswordMismatchException if the supplied password was
405 * incorrect.
406 * @exception UnknownEntityException if the user's account does not
407 * exist in the database.
408 * @exception DataBackendException if there is a problem accessing the
409 * storage.
410 */
411 public void changePassword(User user, String oldPassword,
412 String newPassword)
413 throws PasswordMismatchException, UnknownEntityException,
414 DataBackendException
415 {
416 if (!accountExists(user))
417 {
418 throw new UnknownEntityException("The account '" +
419 user.getName() + "' does not exist");
420 }
421
422 if (!TurbineSecurity.checkPassword(oldPassword, user.getPassword()))
423 {
424 throw new PasswordMismatchException(
425 "The supplied old password for '" + user.getName() +
426 "' was incorrect");
427 }
428 user.setPassword(TurbineSecurity.encryptPassword(newPassword));
429
430
431
432 store(user);
433 }
434
435 /***
436 * Forcibly sets new password for an User.
437 *
438 * This is supposed by the administrator to change the forgotten or
439 * compromised passwords. Certain implementatations of this feature
440 * would require administrative level access to the authenticating
441 * server / program.
442 *
443 * @param user an User to change password for.
444 * @param password the new password.
445 * @exception UnknownEntityException if the user's record does not
446 * exist in the database.
447 * @exception DataBackendException if there is a problem accessing the
448 * storage.
449 */
450 public void forcePassword(User user, String password)
451 throws UnknownEntityException, DataBackendException
452 {
453 if (!accountExists(user))
454 {
455 throw new UnknownEntityException("The account '" +
456 user.getName() + "' does not exist");
457 }
458 user.setPassword(TurbineSecurity.encryptPassword(password));
459
460
461
462 store(user);
463 }
464
465 /***
466 * Creates new user account with specified attributes.
467 *
468 * @param user The object describing account to be created.
469 * @param initialPassword the password for the new account
470 * @throws DataBackendException if there was an error accessing
471 the data backend.
472 * @throws EntityExistsException if the user account already exists.
473 */
474 public void createAccount(User user, String initialPassword)
475 throws EntityExistsException, DataBackendException
476 {
477 if (StringUtils.isEmpty(user.getName()))
478 {
479 throw new DataBackendException("Could not create "
480 + "an user with empty name!");
481 }
482
483 if (accountExists(user))
484 {
485 throw new EntityExistsException("The account '" +
486 user.getName() + "' already exists");
487 }
488 user.setPassword(TurbineSecurity.encryptPassword(initialPassword));
489
490 Criteria criteria = TurbineUserPeer.buildCriteria(user);
491 try
492 {
493
494 ObjectKey pk = TurbineUserPeer.doInsert(criteria);
495
496
497 TurbineMapBuilder mapbuilder = (TurbineMapBuilder)
498 TurbineUserPeer.getMapBuilder("org.apache.turbine.util.db.map.TurbineMapBuilder");
499 user.setPerm(mapbuilder.getUserId(), pk);
500 ((BaseObject) user).setPrimaryKey(pk);
501 }
502 catch (Exception e)
503 {
504 throw new DataBackendException("Failed to create account '" +
505 user.getName() + "'", e);
506 }
507 }
508
509 /***
510 * Removes an user account from the system.
511 *
512 * @param user the object describing the account to be removed.
513 * @throws DataBackendException if there was an error accessing
514 the data backend.
515 * @throws UnknownEntityException if the user account is not present.
516 */
517 public void removeAccount(User user)
518 throws UnknownEntityException, DataBackendException
519 {
520 if (!accountExists(user))
521 {
522 throw new UnknownEntityException("The account '" +
523 user.getName() + "' does not exist");
524 }
525 Criteria criteria = new Criteria();
526 criteria.add(TurbineUserPeer.USERNAME, user.getName());
527 try
528 {
529 TurbineUserPeer.doDelete(criteria);
530 }
531 catch (Exception e)
532 {
533 throw new DataBackendException("Failed to remove account '" +
534 user.getName() + "'", e);
535 }
536 }
537 }