    001    package org.apache.turbine.services.security;
    002
    003
    004    /*
    005     * Licensed to the Apache Software Foundation (ASF) under one
    006     * or more contributor license agreements.  See the NOTICE file
    007     * distributed with this work for additional information
    008     * regarding copyright ownership.  The ASF licenses this file
    009     * to you under the Apache License, Version 2.0 (the
    010     * "License"); you may not use this file except in compliance
    011     * with the License.  You may obtain a copy of the License at
    012     *
    013     *   http://www.apache.org/licenses/LICENSE-2.0
    014     *
    015     * Unless required by applicable law or agreed to in writing,
    016     * software distributed under the License is distributed on an
    017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    018     * KIND, either express or implied.  See the License for the
    019     * specific language governing permissions and limitations
    020     * under the License.
    021     */
    022
    023
    024    import java.util.Map;
    025
    026    import javax.servlet.ServletConfig;
    027
    028    import org.apache.commons.configuration.Configuration;
    029    import org.apache.commons.lang.StringUtils;
    030    import org.apache.commons.logging.Log;
    031    import org.apache.commons.logging.LogFactory;
    032    import org.apache.fulcrum.crypto.CryptoAlgorithm;
    033    import org.apache.fulcrum.crypto.CryptoService;
    034    import org.apache.fulcrum.factory.FactoryService;
    035    import org.apache.turbine.om.security.Group;
    036    import org.apache.turbine.om.security.Permission;
    037    import org.apache.turbine.om.security.Role;
    038    import org.apache.turbine.om.security.User;
    039    import org.apache.turbine.services.InitializationException;
    040    import org.apache.turbine.services.ServiceManager;
    041    import org.apache.turbine.services.TurbineBaseService;
    042    import org.apache.turbine.services.TurbineServices;
    043    import org.apache.turbine.util.security.AccessControlList;
    044    import org.apache.turbine.util.security.DataBackendException;
    045    import org.apache.turbine.util.security.EntityExistsException;
    046    import org.apache.turbine.util.security.GroupSet;
    047    import org.apache.turbine.util.security.PasswordMismatchException;
    048    import org.apache.turbine.util.security.PermissionSet;
    049    import org.apache.turbine.util.security.RoleSet;
    050    import org.apache.turbine.util.security.UnknownEntityException;
    051
    052    /**
    053     * This is a common subset of SecurityService implementation.
    054     *
    055     * Provided functionality includes:
    056     * <ul>
    057     * <li> methods for retrieving User objects, that delegates functionality
    058     *      to the pluggable implementations of the User interface.
    059     * <li> synchronization mechanism for methods reading/modifying the security
    060     *      information, that guarantees that multiple threads may read the
    061     *      information concurrently, but threads that modify the information
    062     *      acquires exclusive access.
    063     * <li> implementation of convenience methods for retrieving security entities
    064     *      that maintain in-memory caching of objects for fast access.
    065     * </ul>
    066     *
    067     * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
    068     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    069     * @author <a href="mailto:marco@intermeta.de">Marco Kn&uuml;ttel</a>
    070     * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
    071     * @version $Id: BaseSecurityService.java 1096130 2011-04-23 10:37:19Z ludwig $
    072     */
    073    public abstract class BaseSecurityService
    074            extends TurbineBaseService
    075            implements SecurityService
    076    {
    077        /** The number of threads concurrently reading security information */
    078        private int readerCount = 0;
    079
    080        /** The instance of UserManager the SecurityService uses */
    081        private UserManager userManager = null;
    082
    083        /** The class of User the SecurityService uses */
    084        private Class userClass = null;
    085
    086        /** The class of Group the SecurityService uses */
    087        private Class groupClass = null;
    088
    089        /** The class of Permission the SecurityService uses */
    090        private Class permissionClass = null;
    091
    092        /** The class of Role the SecurityService uses */
    093        private Class roleClass = null;
    094
    095        /** The class of ACL the SecurityService uses */
    096        private Class aclClass = null;
    097
    098        /** A factory to construct ACL Objects */
    099        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    }