View Javadoc

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