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