View Javadoc

1   package org.apache.turbine.services.security.db;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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          // GNDN
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             // set the table name for all attached criterion
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         // The OBJECT_DATA column only stores data not mapped to a column.  We must
343         // remove all of the extra data and serialize the rest.  Access Counter
344         // is not mapped to a column so it will be serialized into OBJECT_DATA.
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         // log.debug("Supplied Pass: " + password);
390         // log.debug("User Pass: " + user.getPassword());
391 
392         /*
393          * Unix crypt needs the existing, encrypted password text as
394          * salt for checking the supplied password. So we supply it
395          * into the checkPassword routine
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         // save the changes in the database imediately, to prevent the password
437         // being 'reverted' to the old value if the user data is lost somehow
438         // before it is saved at session's expiry.
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         // save the changes in the database immediately, to prevent the
467         // password being 'reverted' to the old value if the user data
468         // is lost somehow before it is saved at session's expiry.
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             // perform the insert to the database
501             ObjectKey pk = TurbineUserPeer.doInsert(criteria);
502 
503             // update the user object with the primary key
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 }