View Javadoc

    1   package org.apache.turbine.services.security;
    2
    3
    4   /*
    5    * Licensed to the Apache Software Foundation (ASF) under one
    6    * or more contributor license agreements.  See the NOTICE file
    7    * distributed with this work for additional information
    8    * regarding copyright ownership.  The ASF licenses this file
    9    * to you under the Apache License, Version 2.0 (the
    10   * "License"); you may not use this file except in compliance
    11   * with the License.  You may obtain a copy of the License at
    12   *
    13   *   http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing,
    16   * software distributed under the License is distributed on an
    17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    18   * KIND, either express or implied.  See the License for the
    19   * specific language governing permissions and limitations
    20   * under the License.
    21   */
    22
    23
    24  import java.util.Map;
    25
    26  import javax.servlet.ServletConfig;
    27
    28  import org.apache.commons.configuration.Configuration;
    29  import org.apache.commons.lang.StringUtils;
    30  import org.apache.commons.logging.Log;
    31  import org.apache.commons.logging.LogFactory;
    32  import org.apache.fulcrum.crypto.CryptoAlgorithm;
    33  import org.apache.fulcrum.crypto.CryptoService;
    34  import org.apache.fulcrum.factory.FactoryService;
    35  import org.apache.turbine.om.security.Group;
    36  import org.apache.turbine.om.security.Permission;
    37  import org.apache.turbine.om.security.Role;
    38  import org.apache.turbine.om.security.User;
    39  import org.apache.turbine.services.InitializationException;
    40  import org.apache.turbine.services.ServiceManager;
    41  import org.apache.turbine.services.TurbineBaseService;
    42  import org.apache.turbine.services.TurbineServices;
    43  import org.apache.turbine.util.security.AccessControlList;
    44  import org.apache.turbine.util.security.DataBackendException;
    45  import org.apache.turbine.util.security.EntityExistsException;
    46  import org.apache.turbine.util.security.GroupSet;
    47  import org.apache.turbine.util.security.PasswordMismatchException;
    48  import org.apache.turbine.util.security.PermissionSet;
    49  import org.apache.turbine.util.security.RoleSet;
    50  import org.apache.turbine.util.security.UnknownEntityException;
    51
    52  /**
    53   * This is a common subset of SecurityService implementation.
    54   *
    55   * Provided functionality includes:
    56   * <ul>
    57   * <li> methods for retrieving User objects, that delegates functionality
    58   *      to the pluggable implementations of the User interface.
    59   * <li> synchronization mechanism for methods reading/modifying the security
    60   *      information, that guarantees that multiple threads may read the
    61   *      information concurrently, but threads that modify the information
    62   *      acquires exclusive access.
    63   * <li> implementation of convenience methods for retrieving security entities
    64   *      that maintain in-memory caching of objects for fast access.
    65   * </ul>
    66   *
    67   * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
    68   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    69   * @author <a href="mailto:marco@intermeta.de">Marco Kn&uuml;ttel</a>
    70   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
    71   * @version $Id: BaseSecurityService.java 1096130 2011-04-23 10:37:19Z ludwig $
    72   */
    73  public abstract class BaseSecurityService
    74          extends TurbineBaseService
    75          implements SecurityService
    76  {
    77      /** The number of threads concurrently reading security information */
    78      private int readerCount = 0;
    79
    80      /** The instance of UserManager the SecurityService uses */
    81      private UserManager userManager = null;
    82
    83      /** The class of User the SecurityService uses */
    84      private Class userClass = null;
    85
    86      /** The class of Group the SecurityService uses */
    87      private Class groupClass = null;
    88
    89      /** The class of Permission the SecurityService uses */
    90      private Class permissionClass = null;
    91
    92      /** The class of Role the SecurityService uses */
    93      private Class roleClass = null;
    94
    95      /** The class of ACL the SecurityService uses */
    96      private Class aclClass = null;
    97
    98      /** A factory to construct ACL Objects */
    99      private FactoryService aclFactoryService = null;
    100
    101     /**
    102      * The Group object that represents the <a href="#global">global group</a>.
    103      */
    104     private static Group globalGroup = null;
    105
    106     /** Logging */
    107     private static Log log = LogFactory.getLog(BaseSecurityService.class);
    108
    109     /**
    110      * This method provides client-side encryption of passwords.
    111      *
    112      * If <code>secure.passwords</code> are enabled in TurbineResources,
    113      * the password will be encrypted, if not, it will be returned unchanged.
    114      * The <code>secure.passwords.algorithm</code> property can be used
    115      * to chose which digest algorithm should be used for performing the
    116      * encryption. <code>SHA</code> is used by default.
    117      *
    118      * @param password the password to process
    119      * @return processed password
    120      */
    121     public String encryptPassword(String password)
    122     {
    123         return encryptPassword(password, null);
    124     }
    125
    126     /**
    127      * This method provides client-side encryption of passwords.
    128      *
    129      * If <code>secure.passwords</code> are enabled in TurbineResources,
    130      * the password will be encrypted, if not, it will be returned unchanged.
    131      * The <code>secure.passwords.algorithm</code> property can be used
    132      * to chose which digest algorithm should be used for performing the
    133      * encryption. <code>SHA</code> is used by default.
    134      *
    135      * The used algorithms must be prepared to accept null as a
    136      * valid parameter for salt. All algorithms in the Fulcrum Cryptoservice
    137      * accept this.
    138      *
    139      * @param password the password to process
    140      * @param salt     algorithms that needs a salt can provide one here
    141      * @return processed password
    142      */
    143
    144     public String encryptPassword(String password, String salt)
    145     {
    146         if (password == null)
    147         {
    148             return null;
    149         }
    150         String secure = getConfiguration().getString(
    151                 SecurityService.SECURE_PASSWORDS_KEY,
    152                 SecurityService.SECURE_PASSWORDS_DEFAULT).toLowerCase();
    153
    154         String algorithm = getConfiguration().getString(
    155                 SecurityService.SECURE_PASSWORDS_ALGORITHM_KEY,
    156                 SecurityService.SECURE_PASSWORDS_ALGORITHM_DEFAULT);
    157
    158         CryptoService cs = null;
    159         try {
    160             ServiceManager serviceManager = TurbineServices.getInstance();
    161             cs = (CryptoService)serviceManager.getService(CryptoService.ROLE);
    162         }
    163         catch (Exception e){
    164             throw new RuntimeException("Could not access Crypto Service",e);
    165         }
    166
    167         if (cs != null && (secure.equals("true") || secure.equals("yes")))
    168         {
    169             try
    170             {
    171                 CryptoAlgorithm ca = cs.getCryptoAlgorithm(algorithm);
    172
    173                 ca.setSeed(salt);
    174
    175                 String result = ca.encrypt(password);
    176
    177                 return result;
    178             }
    179             catch (Exception e)
    180             {
    181                 log.error("Unable to encrypt password: ", e);
    182
    183                 return null;
    184             }
    185         }
    186         else
    187         {
    188             return password;
    189         }
    190     }
    191
    192     /**
    193      * Checks if a supplied password matches the encrypted password
    194      *
    195      * @param checkpw      The clear text password supplied by the user
    196      * @param encpw        The current, encrypted password
    197      *
    198      * @return true if the password matches, else false
    199      *
    200      */
    201
    202     public boolean checkPassword(String checkpw, String encpw)
    203     {
    204         String result = encryptPassword(checkpw, encpw);
    205
    206         return (result == null) ? false : result.equals(encpw);
    207     }
    208
    209     /**
    210      * Initializes the SecurityService, locating the apropriate UserManager
    211      * This is a zero parameter variant which queries the Turbine Servlet
    212      * for its config.
    213      *
    214      * @throws InitializationException Something went wrong in the init stage
    215      */
    216     public void init()
    217             throws InitializationException
    218     {
    219         Configuration conf = getConfiguration();
    220
    221         String userManagerClassName = conf.getString(
    222                 SecurityService.USER_MANAGER_KEY,
    223                 SecurityService.USER_MANAGER_DEFAULT);
    224
    225         String userClassName = conf.getString(
    226                 SecurityService.USER_CLASS_KEY,
    227                 SecurityService.USER_CLASS_DEFAULT);
    228
    229         String groupClassName = conf.getString(
    230                 SecurityService.GROUP_CLASS_KEY,
    231                 SecurityService.GROUP_CLASS_DEFAULT);
    232
    233         String permissionClassName = conf.getString(
    234                 SecurityService.PERMISSION_CLASS_KEY,
    235                 SecurityService.PERMISSION_CLASS_DEFAULT);
    236
    237         String roleClassName = conf.getString(
    238                 SecurityService.ROLE_CLASS_KEY,
    239                 SecurityService.ROLE_CLASS_DEFAULT);
    240
    241         String aclClassName = conf.getString(
    242                 SecurityService.ACL_CLASS_KEY,
    243                 SecurityService.ACL_CLASS_DEFAULT);
    244
    245         try
    246         {
    247             userClass = Class.forName(userClassName);
    248             groupClass = Class.forName(groupClassName);
    249             permissionClass = Class.forName(permissionClassName);
    250             roleClass = Class.forName(roleClassName);
    251             aclClass = Class.forName(aclClassName);
    252         }
    253         catch (Exception e)
    254         {
    255             if (userClass == null)
    256             {
    257                 throw new InitializationException(
    258                         "Failed to create a Class object for User implementation", e);
    259             }
    260             if (groupClass == null)
    261             {
    262                 throw new InitializationException(
    263                         "Failed to create a Class object for Group implementation", e);
    264             }
    265             if (permissionClass == null)
    266             {
    267                 throw new InitializationException(
    268                         "Failed to create a Class object for Permission implementation", e);
    269             }
    270             if (roleClass == null)
    271             {
    272                 throw new InitializationException(
    273                         "Failed to create a Class object for Role implementation", e);
    274             }
    275             if (aclClass == null)
    276             {
    277                 throw new InitializationException(
    278                         "Failed to create a Class object for ACL implementation", e);
    279             }
    280         }
    281
    282         try
    283         {
    284             UserManager userManager =
    285                     (UserManager) Class.forName(userManagerClassName).newInstance();
    286
    287             userManager.init(conf);
    288
    289             setUserManager(userManager);
    290         }
    291         catch (Exception e)
    292         {
    293             throw new InitializationException("Failed to instantiate UserManager", e);
    294         }
    295
    296         try
    297         {
    298             aclFactoryService = (FactoryService)TurbineServices.getInstance().getService(FactoryService.ROLE);
    299         }
    300         catch (Exception e)
    301         {
    302             throw new InitializationException(
    303                     "BaseSecurityService.init: Failed to get the Factory Service object", e);
    304         }
    305
    306         setInit(true);
    307     }
    308
    309     /**
    310      * Return a Class object representing the system's chosen implementation of
    311      * of User interface.
    312      *
    313      * @return systems's chosen implementation of User interface.
    314      * @throws UnknownEntityException if the implementation of User interface
    315      *         could not be determined, or does not exist.
    316      */
    317     public Class getUserClass()
    318             throws UnknownEntityException
    319     {
    320         if (userClass == null)
    321         {
    322             throw new UnknownEntityException(
    323                     "Failed to create a Class object for User implementation");
    324         }
    325         return userClass;
    326     }
    327
    328     /**
    329      * Construct a blank User object.
    330      *
    331      * This method calls getUserClass, and then creates a new object using
    332      * the default constructor.
    333      *
    334      * @return an object implementing User interface.
    335      * @throws UnknownEntityException if the object could not be instantiated.
    336      */
    337     public User getUserInstance()
    338             throws UnknownEntityException
    339     {
    340         User user;
    341         try
    342         {
    343             user = (User) getUserClass().newInstance();
    344         }
    345         catch (Exception e)
    346         {
    347             throw new UnknownEntityException(
    348                     "Failed instantiate an User implementation object", e);
    349         }
    350         return user;
    351     }
    352
    353     /**
    354      * Construct a blank User object.
    355      *
    356      * This method calls getUserClass, and then creates a new object using
    357      * the default constructor.
    358      *
    359      * @param userName The name of the user.
    360      *
    361      * @return an object implementing User interface.
    362      *
    363      * @throws UnknownEntityException if the object could not be instantiated.
    364      */
    365     public User getUserInstance(String userName)
    366             throws UnknownEntityException
    367     {
    368         User user = getUserInstance();
    369         user.setName(userName);
    370         return user;
    371     }
    372
    373     /**
    374      * Return a Class object representing the system's chosen implementation of
    375      * of Group interface.
    376      *
    377      * @return systems's chosen implementation of Group interface.
    378      * @throws UnknownEntityException if the implementation of Group interface
    379      *         could not be determined, or does not exist.
    380      */
    381     public Class getGroupClass()
    382             throws UnknownEntityException
    383     {
    384         if (groupClass == null)
    385         {
    386             throw new UnknownEntityException(
    387                     "Failed to create a Class object for Group implementation");
    388         }
    389         return groupClass;
    390     }
    391
    392     /**
    393      * Construct a blank Group object.
    394      *
    395      * This method calls getGroupClass, and then creates a new object using
    396      * the default constructor.
    397      *
    398      * @return an object implementing Group interface.
    399      * @throws UnknownEntityException if the object could not be instantiated.
    400      */
    401     public Group getGroupInstance()
    402             throws UnknownEntityException
    403     {
    404         Group group;
    405         try
    406         {
    407             group = (Group) getGroupClass().newInstance();
    408         }
    409         catch (Exception e)
    410         {
    411             throw new UnknownEntityException("Failed to instantiate a Group implementation object", e);
    412         }
    413         return group;
    414     }
    415
    416     /**
    417      * Construct a blank Group object.
    418      *
    419      * This method calls getGroupClass, and then creates a new object using
    420      * the default constructor.
    421      *
    422      * @param groupName The name of the Group
    423      *
    424      * @return an object implementing Group interface.
    425      *
    426      * @throws UnknownEntityException if the object could not be instantiated.
    427      */
    428     public Group getGroupInstance(String groupName)
    429             throws UnknownEntityException
    430     {
    431         Group group = getGroupInstance();
    432         group.setName(groupName);
    433         return group;
    434     }
    435
    436     /**
    437      * Return a Class object representing the system's chosen implementation of
    438      * of Permission interface.
    439      *
    440      * @return systems's chosen implementation of Permission interface.
    441      * @throws UnknownEntityException if the implementation of Permission interface
    442      *         could not be determined, or does not exist.
    443      */
    444     public Class getPermissionClass()
    445             throws UnknownEntityException
    446     {
    447         if (permissionClass == null)
    448         {
    449             throw new UnknownEntityException(
    450                     "Failed to create a Class object for Permission implementation");
    451         }
    452         return permissionClass;
    453     }
    454
    455     /**
    456      * Construct a blank Permission object.
    457      *
    458      * This method calls getPermissionClass, and then creates a new object using
    459      * the default constructor.
    460      *
    461      * @return an object implementing Permission interface.
    462      * @throws UnknownEntityException if the object could not be instantiated.
    463      */
    464     public Permission getPermissionInstance()
    465             throws UnknownEntityException
    466     {
    467         Permission permission;
    468         try
    469         {
    470             permission = (Permission) getPermissionClass().newInstance();
    471         }
    472         catch (Exception e)
    473         {
    474             throw new UnknownEntityException("Failed to instantiate a Permission implementation object", e);
    475         }
    476         return permission;
    477     }
    478
    479     /**
    480      * Construct a blank Permission object.
    481      *
    482      * This method calls getPermissionClass, and then creates a new object using
    483      * the default constructor.
    484      *
    485      * @param permName The name of the permission.
    486      *
    487      * @return an object implementing Permission interface.
    488      * @throws UnknownEntityException if the object could not be instantiated.
    489      */
    490     public Permission getPermissionInstance(String permName)
    491             throws UnknownEntityException
    492     {
    493         Permission perm = getPermissionInstance();
    494         perm.setName(permName);
    495         return perm;
    496     }
    497
    498     /**
    499      * Return a Class object representing the system's chosen implementation of
    500      * of Role interface.
    501      *
    502      * @return systems's chosen implementation of Role interface.
    503      * @throws UnknownEntityException if the implementation of Role interface
    504      *         could not be determined, or does not exist.
    505      */
    506     public Class getRoleClass()
    507             throws UnknownEntityException
    508     {
    509         if (roleClass == null)
    510         {
    511             throw new UnknownEntityException(
    512                     "Failed to create a Class object for Role implementation");
    513         }
    514         return roleClass;
    515     }
    516
    517     /**
    518      * Construct a blank Role object.
    519      *
    520      * This method calls getRoleClass, and then creates a new object using
    521      * the default constructor.
    522      *
    523      * @return an object implementing Role interface.
    524      * @throws UnknownEntityException if the object could not be instantiated.
    525      */
    526     public Role getRoleInstance()
    527             throws UnknownEntityException
    528     {
    529         Role role;
    530
    531         try
    532         {
    533             role = (Role) getRoleClass().newInstance();
    534         }
    535         catch (Exception e)
    536         {
    537             throw new UnknownEntityException("Failed to instantiate a Role implementation object", e);
    538         }
    539         return role;
    540     }
    541
    542     /**
    543      * Construct a blank Role object.
    544      *
    545      * This method calls getRoleClass, and then creates a new object using
    546      * the default constructor.
    547      *
    548      * @param roleName The name of the role.
    549      *
    550      * @return an object implementing Role interface.
    551      *
    552      * @throws UnknownEntityException if the object could not be instantiated.
    553      */
    554     public Role getRoleInstance(String roleName)
    555             throws UnknownEntityException
    556     {
    557         Role role = getRoleInstance();
    558         role.setName(roleName);
    559         return role;
    560     }
    561
    562     /**
    563      * Return a Class object representing the system's chosen implementation of
    564      * of ACL interface.
    565      *
    566      * @return systems's chosen implementation of ACL interface.
    567      * @throws UnknownEntityException if the implementation of ACL interface
    568      *         could not be determined, or does not exist.
    569      */
    570     public Class getAclClass()
    571             throws UnknownEntityException
    572     {
    573         if (aclClass == null)
    574         {
    575             throw new UnknownEntityException(
    576                     "Failed to create a Class object for ACL implementation");
    577         }
    578         return aclClass;
    579     }
    580
    581     /**
    582      * Construct a new ACL object.
    583      *
    584      * This constructs a new ACL object from the configured class and
    585      * initializes it with the supplied roles and permissions.
    586      *
    587      * @param roles The roles that this ACL should contain
    588      * @param permissions The permissions for this ACL
    589      *
    590      * @return an object implementing ACL interface.
    591      * @throws UnknownEntityException if the object could not be instantiated.
    592      */
    593     public AccessControlList getAclInstance(Map roles, Map permissions)
    594             throws UnknownEntityException
    595     {
    596         Object[] objects = {roles, permissions};
    597         String[] signatures = {Map.class.getName(), Map.class.getName()};
    598         AccessControlList accessControlList;
    599
    600         try
    601         {
    602             accessControlList =
    603                     (AccessControlList) aclFactoryService.getInstance(aclClass.getName(),
    604                             objects,
    605                             signatures);
    606         }
    607         catch (Exception e)
    608         {
    609             throw new UnknownEntityException(
    610                     "Failed to instantiate an ACL implementation object", e);
    611         }
    612
    613         return accessControlList;
    614     }
    615
    616     /**
    617      * Returns the configured UserManager.
    618      *
    619      * @return An UserManager object
    620      */
    621     public UserManager getUserManager()
    622     {
    623         return userManager;
    624     }
    625
    626     /**
    627      * Configure a new user Manager.
    628      *
    629      * @param userManager An UserManager object
    630      */
    631     public void setUserManager(UserManager userManager)
    632     {
    633         this.userManager = userManager;
    634     }
    635
    636     /**
    637      * Check whether a specified user's account exists.
    638      *
    639      * The login name is used for looking up the account.
    640      *
    641      * @param user The user to be checked.
    642      * @return true if the specified account exists
    643      * @throws DataBackendException if there was an error accessing the data
    644      *         backend.
    645      */
    646     public boolean accountExists(User user)
    647             throws DataBackendException
    648     {
    649         return getUserManager().accountExists(user);
    650     }
    651
    652     /**
    653      * Check whether a specified user's account exists.
    654      *
    655      * The login name is used for looking up the account.
    656      *
    657      * @param userName The name of the user to be checked.
    658      * @return true if the specified account exists
    659      * @throws DataBackendException if there was an error accessing the data
    660      *         backend.
    661      */
    662     public boolean accountExists(String userName)
    663             throws DataBackendException
    664     {
    665         return getUserManager().accountExists(userName);
    666     }
    667
    668     /**
    669      * Authenticates an user, and constructs an User object to represent
    670      * him/her.
    671      *
    672      * @param username The user name.
    673      * @param password The user password.
    674      * @return An authenticated Turbine User.
    675      * @throws PasswordMismatchException if the supplied password was incorrect.
    676      * @throws UnknownEntityException if the user's account does not
    677      *            exist in the database.
    678      * @throws DataBackendException if there is a problem accessing the storage.
    679      */
    680     public User getAuthenticatedUser(String username, String password)
    681             throws DataBackendException, UnknownEntityException,
    682                    PasswordMismatchException
    683     {
    684         return getUserManager().retrieve(username, password);
    685     }
    686
    687     /**
    688      * Constructs an User object to represent a registered user of the
    689      * application.
    690      *
    691      * @param username The user name.
    692      * @return A Turbine User.
    693      * @throws UnknownEntityException if the user's account does not exist
    694      * @throws DataBackendException if there is a problem accessing the storage.
    695      */
    696     public User getUser(String username)
    697             throws DataBackendException, UnknownEntityException
    698     {
    699         return getUserManager().retrieve(username);
    700     }
    701
    702
    703     /**
    704      * Constructs an User object to represent an anonymous user of the
    705      * application.
    706      *
    707      * @return An anonymous Turbine User.
    708      * @throws UnknownEntityException if the implementation of User interface
    709      *         could not be determined, or does not exist.
    710      */
    711     public User getAnonymousUser()
    712             throws UnknownEntityException
    713     {
    714         User user = getUserInstance();
    715         user.setName("");
    716         return user;
    717     }
    718
    719     /**
    720      * Checks whether a passed user object matches the anonymous user pattern
    721      * according to the configured user manager
    722      *
    723      * @param user An user object
    724      *
    725      * @return True if this is an anonymous user
    726      *
    727      */
    728     public boolean isAnonymousUser(User user)
    729     {
    730         // Either just null, the name is null or the name is the empty string
    731         return (user == null) || StringUtils.isEmpty(user.getName());
    732     }
    733
    734     /**
    735      * Saves User's data in the permanent storage. The user account is required
    736      * to exist in the storage.
    737      *
    738      * @param user the User object to save
    739      * @throws UnknownEntityException if the user's account does not
    740      *         exist in the database.
    741      * @throws DataBackendException if there is a problem accessing the storage.
    742      */
    743     public void saveUser(User user)
    744             throws UnknownEntityException, DataBackendException
    745     {
    746         getUserManager().store(user);
    747     }
    748
    749     /**
    750      * Saves User data when the session is unbound. The user account is required
    751      * to exist in the storage.
    752      *
    753      * LastLogin, AccessCounter, persistent pull tools, and any data stored
    754      * in the permData hashtable that is not mapped to a column will be saved.
    755      *
    756      * @exception UnknownEntityException if the user's account does not
    757      *            exist in the database.
    758      * @exception DataBackendException if there is a problem accessing the
    759      *            storage.
    760      */
    761     public void saveOnSessionUnbind(User user)
    762             throws UnknownEntityException, DataBackendException
    763     {
    764         userManager.saveOnSessionUnbind(user);
    765     }
    766
    767     /**
    768      * Creates new user account with specified attributes.
    769      *
    770      * @param user the object describing account to be created.
    771      * @param password The password to use for the account.
    772      *
    773      * @throws DataBackendException if there was an error accessing the
    774      *         data backend.
    775      * @throws EntityExistsException if the user account already exists.
    776      */
    777     public void addUser(User user, String password)
    778             throws DataBackendException, EntityExistsException
    779     {
    780         getUserManager().createAccount(user, password);
    781     }
    782
    783     /**
    784      * Removes an user account from the system.
    785      *
    786      * @param user the object describing the account to be removed.
    787      * @throws DataBackendException if there was an error accessing the data
    788      *         backend.
    789      * @throws UnknownEntityException if the user account is not present.
    790      */
    791     public void removeUser(User user)
    792             throws DataBackendException, UnknownEntityException
    793     {
    794         // revoke all roles form the user
    795         revokeAll(user);
    796
    797         getUserManager().removeAccount(user);
    798     }
    799
    800     /**
    801      * Change the password for an User.
    802      *
    803      * @param user an User to change password for.
    804      * @param oldPassword the current password supplied by the user.
    805      * @param newPassword the current password requested by the user.
    806      * @throws PasswordMismatchException if the supplied password was incorrect.
    807      * @throws UnknownEntityException if the user's record does not
    808      *            exist in the database.
    809      * @throws DataBackendException if there is a problem accessing the storage.
    810      */
    811     public void changePassword(User user, String oldPassword,
    812             String newPassword)
    813             throws PasswordMismatchException, UnknownEntityException,
    814                    DataBackendException
    815     {
    816         getUserManager().changePassword(user, oldPassword, newPassword);
    817     }
    818
    819     /**
    820      * Forcibly sets new password for an User.
    821      *
    822      * This is supposed by the administrator to change the forgotten or
    823      * compromised passwords. Certain implementatations of this feature
    824      * would require administrative level access to the authenticating
    825      * server / program.
    826      *
    827      * @param user an User to change password for.
    828      * @param password the new password.
    829      * @throws UnknownEntityException if the user's record does not
    830      *            exist in the database.
    831      * @throws DataBackendException if there is a problem accessing the storage.
    832      */
    833     public void forcePassword(User user, String password)
    834             throws UnknownEntityException, DataBackendException
    835     {
    836         getUserManager().forcePassword(user, password);
    837     }
    838
    839     /**
    840      * Acquire a shared lock on the security information repository.
    841      *
    842      * Methods that read security information need to invoke this
    843      * method at the beginning of their body.
    844      */
    845     protected synchronized void lockShared()
    846     {
    847         readerCount++;
    848     }
    849
    850     /**
    851      * Release a shared lock on the security information repository.
    852      *
    853      * Methods that read security information need to invoke this
    854      * method at the end of their body.
    855      */
    856     protected synchronized void unlockShared()
    857     {
    858         readerCount--;
    859         this.notify();
    860     }
    861
    862     /**
    863      * Acquire an exclusive lock on the security information repository.
    864      *
    865      * Methods that modify security information need to invoke this
    866      * method at the beginning of their body. Note! Those methods must
    867      * be <code>synchronized</code> themselves!
    868      */
    869     protected void lockExclusive()
    870     {
    871         while (readerCount > 0)
    872         {
    873             try
    874             {
    875                 this.wait();
    876             }
    877             catch (InterruptedException e)
    878             {
    879             }
    880         }
    881     }
    882
    883     /**
    884      * Release an exclusive lock on the security information repository.
    885      *
    886      * This method is provided only for completeness. It does not really
    887      * do anything. Note! Methods that modify security information
    888      * must be <code>synchronized</code>!
    889      */
    890     protected void unlockExclusive()
    891     {
    892         // do nothing
    893     }
    894
    895     /**
    896      * Provides a reference to the Group object that represents the
    897      * <a href="#global">global group</a>.
    898      *
    899      * @return a Group object that represents the global group.
    900      */
    901     public Group getGlobalGroup()
    902     {
    903         if (globalGroup == null)
    904         {
    905             synchronized (BaseSecurityService.class)
    906             {
    907                 if (globalGroup == null)
    908                 {
    909                     try
    910                     {
    911                         globalGroup = getAllGroups()
    912                                 .getGroupByName(Group.GLOBAL_GROUP_NAME);
    913                     }
    914                     catch (DataBackendException e)
    915                     {
    916                         log.error("Failed to retrieve global group object: ", e);
    917                     }
    918                 }
    919             }
    920         }
    921         return globalGroup;
    922     }
    923
    924     /**
    925      * Retrieve a Group object with specified name.
    926      *
    927      * @param name the name of the Group.
    928      * @return an object representing the Group with specified name.
    929      * @throws DataBackendException if there was an error accessing the
    930      *         data backend.
    931      * @throws UnknownEntityException if the group does not exist.
    932      */
    933     public Group getGroupByName(String name)
    934             throws DataBackendException, UnknownEntityException
    935     {
    936         Group group = getAllGroups().getGroupByName(name);
    937         if (group == null)
    938         {
    939             throw new UnknownEntityException(
    940                     "The specified group does not exist");
    941         }
    942         return group;
    943     }
    944
    945     /**
    946      * Retrieve a Group object with specified Id.
    947      *
    948      * @param id the id of the Group.
    949      * @return an object representing the Group with specified name.
    950      * @throws UnknownEntityException if the permission does not
    951      *            exist in the database.
    952      * @throws DataBackendException if there is a problem accessing the
    953      *            storage.
    954      */
    955     public Group getGroupById(int id)
    956             throws DataBackendException, UnknownEntityException
    957     {
    958         Group group = getAllGroups().getGroupById(id);
    959         if (group == null)
    960         {
    961             throw new UnknownEntityException(
    962                     "The specified group does not exist");
    963         }
    964         return group;
    965     }
    966
    967     /**
    968      * Retrieve a Role object with specified name.
    969      *
    970      * @param name the name of the Role.
    971      * @return an object representing the Role with specified name.
    972      * @throws DataBackendException if there was an error accessing the
    973      *         data backend.
    974      * @throws UnknownEntityException if the role does not exist.
    975      */
    976     public Role getRoleByName(String name)
    977             throws DataBackendException, UnknownEntityException
    978     {
    979         Role role = getAllRoles().getRoleByName(name);
    980         if (role == null)
    981         {
    982             throw new UnknownEntityException(
    983                     "The specified role does not exist");
    984         }
    985         role.setPermissions(getPermissions(role));
    986         return role;
    987     }
    988
    989     /**
    990      * Retrieve a Role object with specified Id.
    991      * @param id the id of the Role.
    992      * @return an object representing the Role with specified name.
    993      * @throws UnknownEntityException if the permission does not
    994      *            exist in the database.
    995      * @throws DataBackendException if there is a problem accessing the
    996      *            storage.
    997      */
    998     public Role getRoleById(int id)
    999             throws DataBackendException,
    1000                    UnknownEntityException
    1001     {
    1002         Role role = getAllRoles().getRoleById(id);
    1003         if (role == null)
    1004         {
    1005             throw new UnknownEntityException(
    1006                     "The specified role does not exist");
    1007         }
    1008         role.setPermissions(getPermissions(role));
    1009         return role;
    1010     }
    1011
    1012     /**
    1013      * Retrieve a Permission object with specified name.
    1014      *
    1015      * @param name the name of the Permission.
    1016      * @return an object representing the Permission with specified name.
    1017      * @throws DataBackendException if there was an error accessing the
    1018      *         data backend.
    1019      * @throws UnknownEntityException if the permission does not exist.
    1020      */
    1021     public Permission getPermissionByName(String name)
    1022             throws DataBackendException, UnknownEntityException
    1023     {
    1024         Permission permission = getAllPermissions().getPermissionByName(name);
    1025         if (permission == null)
    1026         {
    1027             throw new UnknownEntityException(
    1028                     "The specified permission does not exist");
    1029         }
    1030         return permission;
    1031     }
    1032
    1033     /**
    1034      * Retrieve a Permission object with specified Id.
    1035      *
    1036      * @param id the id of the Permission.
    1037      * @return an object representing the Permission with specified name.
    1038      * @throws UnknownEntityException if the permission does not
    1039      *            exist in the database.
    1040      * @throws DataBackendException if there is a problem accessing the
    1041      *            storage.
    1042      */
    1043     public Permission getPermissionById(int id)
    1044             throws DataBackendException,
    1045                    UnknownEntityException
    1046     {
    1047         Permission permission = getAllPermissions().getPermissionById(id);
    1048         if (permission == null)
    1049         {
    1050             throw new UnknownEntityException(
    1051                     "The specified permission does not exist");
    1052         }
    1053         return permission;
    1054     }
    1055
    1056     /**
    1057      * Retrieves all groups defined in the system.
    1058      *
    1059      * @return the names of all groups defined in the system.
    1060      * @throws DataBackendException if there was an error accessing the
    1061      *         data backend.
    1062      */
    1063     public abstract GroupSet getAllGroups()
    1064             throws DataBackendException;
    1065
    1066     /**
    1067      * Retrieves all roles defined in the system.
    1068      *
    1069      * @return the names of all roles defined in the system.
    1070      * @throws DataBackendException if there was an error accessing the
    1071      *         data backend.
    1072      */
    1073     public abstract RoleSet getAllRoles()
    1074             throws DataBackendException;
    1075
    1076     /**
    1077      * Retrieves all permissions defined in the system.
    1078      *
    1079      * @return the names of all roles defined in the system.
    1080      * @throws DataBackendException if there was an error accessing the
    1081      *         data backend.
    1082      */
    1083     public abstract PermissionSet getAllPermissions()
    1084             throws DataBackendException;
    1085 }