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