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