View Javadoc

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