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