View Javadoc

    1   package org.apache.turbine.services.security.torque;
    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.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 1096130 2011-04-23 10:37:19Z ludwig $
    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(Object 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(Object criteria)
    210         throws DataBackendException
    211     {
    212         if (criteria instanceof Criteria)
    213         {
    214             Criteria c = (Criteria)criteria;
    215             for (Iterator keys = c.keySet().iterator(); keys.hasNext(); )
    216             {
    217                 String key = (String) keys.next();
    218
    219                 // set the table name for all attached criterion
    220                 Criteria.Criterion[] criterion =
    221                     c.getCriterion(key).getAttachedCriterion();
    222
    223                 for (int i = 0; i < criterion.length; i++)
    224                 {
    225                     if (StringUtils.isEmpty(criterion[i].getTable()))
    226                     {
    227                         criterion[i].setTable(UserPeerManager.getTableName());
    228                     }
    229                 }
    230             }
    231             List users = null;
    232             try
    233             {
    234                 users = UserPeerManager.doSelect(c);
    235             }
    236             catch (Exception e)
    237             {
    238                 throw new DataBackendException("Failed to retrieve users", e);
    239             }
    240             return users;
    241         }
    242         else
    243         {
    244             throw new DataBackendException("Failed to retrieve users with invalid criteria");
    245         }
    246     }
    247
    248     /**
    249      * Retrieve a user from persistent storage using username as the
    250      * key, and authenticate the user. The implementation may chose
    251      * to authenticate to the server as the user whose data is being
    252      * retrieved.
    253      *
    254      * @param userName the name of the user.
    255      * @param password the user supplied password.
    256      * @return an User object.
    257      * @exception PasswordMismatchException if the supplied password was
    258      *            incorrect.
    259      * @exception UnknownEntityException if the user's account does not
    260      *            exist in the database.
    261      * @exception DataBackendException if there is a problem accessing the
    262      *            storage.
    263      */
    264     public User retrieve(String userName, String password)
    265         throws PasswordMismatchException, UnknownEntityException,
    266                DataBackendException
    267     {
    268         User user = retrieve(userName);
    269         authenticate(user, password);
    270         return user;
    271     }
    272
    273     /**
    274      * Save an User object to persistent storage. User's account is
    275      * required to exist in the storage.
    276      *
    277      * @param user an User object to store.
    278      * @exception UnknownEntityException if the user's account does not
    279      *            exist in the database.
    280      * @exception DataBackendException if there is a problem accessing the
    281      *            storage.
    282      */
    283     public void store(User user)
    284         throws UnknownEntityException, DataBackendException
    285     {
    286         if (!accountExists(user))
    287         {
    288             throw new UnknownEntityException("The account '" +
    289                                              user.getName() + "' does not exist");
    290         }
    291
    292         try
    293         {
    294             // this is to mimic the old behavior of the method, the user
    295             // should be new that is passed to this method.  It would be
    296             // better if this was checked, but the original code did not
    297             // care about the user's state, so we set it to be appropriate
    298             ((Persistent) user).setNew(false);
    299             ((Persistent) user).setModified(true);
    300             ((Persistent) user).save();
    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         store(user);
    328     }
    329
    330
    331     /**
    332      * Authenticate an User with the specified password. If authentication
    333      * is successful the method returns nothing. If there are any problems,
    334      * exception was thrown.
    335      *
    336      * @param user an User object to authenticate.
    337      * @param password the user supplied password.
    338      * @exception PasswordMismatchException if the supplied password was
    339      *            incorrect.
    340      * @exception UnknownEntityException if the user's account does not
    341      *            exist in the database.
    342      * @exception DataBackendException if there is a problem accessing the
    343      *            storage.
    344      */
    345     public void authenticate(User user, String password)
    346         throws PasswordMismatchException, UnknownEntityException,
    347                DataBackendException
    348     {
    349         if (!accountExists(user))
    350         {
    351             throw new UnknownEntityException("The account '" +
    352                                              user.getName() + "' does not exist");
    353         }
    354
    355         // log.debug("Supplied Pass: " + password);
    356         // log.debug("User Pass: " + user.getPassword());
    357
    358         /*
    359          * Unix crypt needs the existing, encrypted password text as
    360          * salt for checking the supplied password. So we supply it
    361          * into the checkPassword routine
    362          */
    363
    364         if (!TurbineSecurity.checkPassword(password, user.getPassword()))
    365         {
    366             throw new PasswordMismatchException("The passwords do not match");
    367         }
    368     }
    369
    370     /**
    371      * Change the password for an User. The user must have supplied the
    372      * old password to allow the change.
    373      *
    374      * @param user an User to change password for.
    375      * @param oldPassword The old password to verify
    376      * @param newPassword The new password to set
    377      * @exception PasswordMismatchException if the supplied password was
    378      *            incorrect.
    379      * @exception UnknownEntityException if the user's account does not
    380      *            exist in the database.
    381      * @exception DataBackendException if there is a problem accessing the
    382      *            storage.
    383      */
    384     public void changePassword(User user, String oldPassword,
    385                                String newPassword)
    386         throws PasswordMismatchException, UnknownEntityException,
    387                DataBackendException
    388     {
    389         if (!accountExists(user))
    390         {
    391             throw new UnknownEntityException("The account '" +
    392                                              user.getName() + "' does not exist");
    393         }
    394
    395         if (!TurbineSecurity.checkPassword(oldPassword, user.getPassword()))
    396         {
    397             throw new PasswordMismatchException(
    398                 "The supplied old password for '" + user.getName() +
    399                 "' was incorrect");
    400         }
    401         user.setPassword(TurbineSecurity.encryptPassword(newPassword));
    402         // save the changes in the database imediately, to prevent the password
    403         // being 'reverted' to the old value if the user data is lost somehow
    404         // before it is saved at session's expiry.
    405         store(user);
    406     }
    407
    408     /**
    409      * Forcibly sets new password for an User.
    410      *
    411      * This is supposed by the administrator to change the forgotten or
    412      * compromised passwords. Certain implementatations of this feature
    413      * would require administrative level access to the authenticating
    414      * server / program.
    415      *
    416      * @param user an User to change password for.
    417      * @param password the new password.
    418      * @exception UnknownEntityException if the user's record does not
    419      *            exist in the database.
    420      * @exception DataBackendException if there is a problem accessing the
    421      *            storage.
    422      */
    423     public void forcePassword(User user, String password)
    424         throws UnknownEntityException, DataBackendException
    425     {
    426         if (!accountExists(user))
    427         {
    428             throw new UnknownEntityException("The account '" +
    429                                              user.getName() + "' does not exist");
    430         }
    431         user.setPassword(TurbineSecurity.encryptPassword(password));
    432         // save the changes in the database immediately, to prevent the
    433         // password being 'reverted' to the old value if the user data
    434         // is lost somehow before it is saved at session's expiry.
    435         store(user);
    436     }
    437
    438     /**
    439      * Creates new user account with specified attributes.
    440      *
    441      * @param user The object describing account to be created.
    442      * @param initialPassword the password for the new account
    443      * @throws DataBackendException if there was an error accessing
    444      the data backend.
    445      * @throws EntityExistsException if the user account already exists.
    446      */
    447     public void createAccount(User user, String initialPassword)
    448         throws EntityExistsException, DataBackendException
    449     {
    450         if(StringUtils.isEmpty(user.getName()))
    451         {
    452             throw new DataBackendException("Could not create "
    453                                            + "an user with empty name!");
    454         }
    455
    456         if (accountExists(user))
    457         {
    458             throw new EntityExistsException("The account '" +
    459                                             user.getName() + "' already exists");
    460         }
    461         user.setPassword(TurbineSecurity.encryptPassword(initialPassword));
    462
    463         try
    464         {
    465             // this is to mimic the old behavior of the method, the user
    466             // should be new that is passed to this method.  It would be
    467             // better if this was checked, but the original code did not
    468             // care about the user's state, so we set it to be appropriate
    469             ((Persistent) user).setNew(true);
    470             ((Persistent) user).setModified(true);
    471             ((Persistent) user).save();
    472         }
    473         catch (Exception e)
    474         {
    475             throw new DataBackendException("Failed to create account '" +
    476                                            user.getName() + "'", e);
    477         }
    478     }
    479
    480     /**
    481      * Removes an user account from the system.
    482      *
    483      * @param user the object describing the account to be removed.
    484      * @throws DataBackendException if there was an error accessing
    485      the data backend.
    486      * @throws UnknownEntityException if the user account is not present.
    487      */
    488     public void removeAccount(User user)
    489         throws UnknownEntityException, DataBackendException
    490     {
    491         if (!accountExists(user))
    492         {
    493             throw new UnknownEntityException("The account '" +
    494                                              user.getName() + "' does not exist");
    495         }
    496         Criteria criteria = new Criteria();
    497         criteria.add(UserPeerManager.getNameColumn(), user.getName());
    498         try
    499         {
    500             UserPeerManager.doDelete(criteria);
    501         }
    502         catch (Exception e)
    503         {
    504             throw new DataBackendException("Failed to remove account '" +
    505                                            user.getName() + "'", e);
    506         }
    507     }
    508 }