Coverage Report - org.apache.turbine.services.security.BaseSecurityService
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseSecurityService
26%
52/193
11%
6/52
2,391
 
 1  
 package org.apache.turbine.services.security;
 2  
 
 3  
 
 4  
 /*
 5  
  * Licensed to the Apache Software Foundation (ASF) under one
 6  
  * or more contributor license agreements.  See the NOTICE file
 7  
  * distributed with this work for additional information
 8  
  * regarding copyright ownership.  The ASF licenses this file
 9  
  * to you under the Apache License, Version 2.0 (the
 10  
  * "License"); you may not use this file except in compliance
 11  
  * with the License.  You may obtain a copy of the License at
 12  
  *
 13  
  *   http://www.apache.org/licenses/LICENSE-2.0
 14  
  *
 15  
  * Unless required by applicable law or agreed to in writing,
 16  
  * software distributed under the License is distributed on an
 17  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 18  
  * KIND, either express or implied.  See the License for the
 19  
  * specific language governing permissions and limitations
 20  
  * under the License.
 21  
  */
 22  
 
 23  
 
 24  
 import java.util.Map;
 25  
 
 26  
 import javax.servlet.ServletConfig;
 27  
 
 28  
 import org.apache.commons.configuration.Configuration;
 29  
 import org.apache.commons.lang.StringUtils;
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 import org.apache.fulcrum.crypto.CryptoAlgorithm;
 33  
 import org.apache.fulcrum.crypto.CryptoService;
 34  
 import org.apache.fulcrum.factory.FactoryService;
 35  
 import org.apache.turbine.om.security.Group;
 36  
 import org.apache.turbine.om.security.Permission;
 37  
 import org.apache.turbine.om.security.Role;
 38  
 import org.apache.turbine.om.security.User;
 39  
 import org.apache.turbine.services.InitializationException;
 40  
 import org.apache.turbine.services.ServiceManager;
 41  
 import org.apache.turbine.services.TurbineBaseService;
 42  
 import org.apache.turbine.services.TurbineServices;
 43  
 import org.apache.turbine.util.security.AccessControlList;
 44  
 import org.apache.turbine.util.security.DataBackendException;
 45  
 import org.apache.turbine.util.security.EntityExistsException;
 46  
 import org.apache.turbine.util.security.GroupSet;
 47  
 import org.apache.turbine.util.security.PasswordMismatchException;
 48  
 import org.apache.turbine.util.security.PermissionSet;
 49  
 import org.apache.turbine.util.security.RoleSet;
 50  
 import org.apache.turbine.util.security.UnknownEntityException;
 51  
 
 52  
 /**
 53  
  * This is a common subset of SecurityService implementation.
 54  
  *
 55  
  * Provided functionality includes:
 56  
  * <ul>
 57  
  * <li> methods for retrieving User objects, that delegates functionality
 58  
  *      to the pluggable implementations of the User interface.
 59  
  * <li> synchronization mechanism for methods reading/modifying the security
 60  
  *      information, that guarantees that multiple threads may read the
 61  
  *      information concurrently, but threads that modify the information
 62  
  *      acquires exclusive access.
 63  
  * <li> implementation of convenience methods for retrieving security entities
 64  
  *      that maintain in-memory caching of objects for fast access.
 65  
  * </ul>
 66  
  *
 67  
  * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
 68  
  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 69  
  * @author <a href="mailto:marco@intermeta.de">Marco Kn&uuml;ttel</a>
 70  
  * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
 71  
  * @version $Id: BaseSecurityService.java 1096130 2011-04-23 10:37:19Z ludwig $
 72  
  */
 73  20
 public abstract class BaseSecurityService
 74  
         extends TurbineBaseService
 75  
         implements SecurityService
 76  
 {
 77  
     /** The number of threads concurrently reading security information */
 78  20
     private int readerCount = 0;
 79  
 
 80  
     /** The instance of UserManager the SecurityService uses */
 81  20
     private UserManager userManager = null;
 82  
 
 83  
     /** The class of User the SecurityService uses */
 84  20
     private Class userClass = null;
 85  
 
 86  
     /** The class of Group the SecurityService uses */
 87  20
     private Class groupClass = null;
 88  
 
 89  
     /** The class of Permission the SecurityService uses */
 90  20
     private Class permissionClass = null;
 91  
 
 92  
     /** The class of Role the SecurityService uses */
 93  20
     private Class roleClass = null;
 94  
 
 95  
     /** The class of ACL the SecurityService uses */
 96  20
     private Class aclClass = null;
 97  
 
 98  
     /** A factory to construct ACL Objects */
 99  20
     private FactoryService aclFactoryService = null;
 100  
 
 101  
     /**
 102  
      * The Group object that represents the <a href="#global">global group</a>.
 103  
      */
 104  20
     private static Group globalGroup = null;
 105  
 
 106  
     /** Logging */
 107  20
     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  0
         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  0
         if (password == null)
 147  
         {
 148  0
             return null;
 149  
         }
 150  0
         String secure = getConfiguration().getString(
 151  
                 SecurityService.SECURE_PASSWORDS_KEY,
 152  
                 SecurityService.SECURE_PASSWORDS_DEFAULT).toLowerCase();
 153  
 
 154  0
         String algorithm = getConfiguration().getString(
 155  
                 SecurityService.SECURE_PASSWORDS_ALGORITHM_KEY,
 156  
                 SecurityService.SECURE_PASSWORDS_ALGORITHM_DEFAULT);
 157  
 
 158  0
         CryptoService cs = null;
 159  
         try {
 160  0
             ServiceManager serviceManager = TurbineServices.getInstance();
 161  0
             cs = (CryptoService)serviceManager.getService(CryptoService.ROLE);
 162  
         }
 163  0
         catch (Exception e){
 164  0
             throw new RuntimeException("Could not access Crypto Service",e);
 165  0
         }
 166  
 
 167  0
         if (cs != null && (secure.equals("true") || secure.equals("yes")))
 168  
         {
 169  
             try
 170  
             {
 171  0
                 CryptoAlgorithm ca = cs.getCryptoAlgorithm(algorithm);
 172  
 
 173  0
                 ca.setSeed(salt);
 174  
 
 175  0
                 String result = ca.encrypt(password);
 176  
 
 177  0
                 return result;
 178  
             }
 179  0
             catch (Exception e)
 180  
             {
 181  0
                 log.error("Unable to encrypt password: ", e);
 182  
 
 183  0
                 return null;
 184  
             }
 185  
         }
 186  
         else
 187  
         {
 188  0
             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  0
         String result = encryptPassword(checkpw, encpw);
 205  
 
 206  0
         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  24
         Configuration conf = getConfiguration();
 220  
 
 221  24
         String userManagerClassName = conf.getString(
 222  
                 SecurityService.USER_MANAGER_KEY,
 223  
                 SecurityService.USER_MANAGER_DEFAULT);
 224  
 
 225  24
         String userClassName = conf.getString(
 226  
                 SecurityService.USER_CLASS_KEY,
 227  
                 SecurityService.USER_CLASS_DEFAULT);
 228  
 
 229  24
         String groupClassName = conf.getString(
 230  
                 SecurityService.GROUP_CLASS_KEY,
 231  
                 SecurityService.GROUP_CLASS_DEFAULT);
 232  
 
 233  24
         String permissionClassName = conf.getString(
 234  
                 SecurityService.PERMISSION_CLASS_KEY,
 235  
                 SecurityService.PERMISSION_CLASS_DEFAULT);
 236  
 
 237  24
         String roleClassName = conf.getString(
 238  
                 SecurityService.ROLE_CLASS_KEY,
 239  
                 SecurityService.ROLE_CLASS_DEFAULT);
 240  
 
 241  24
         String aclClassName = conf.getString(
 242  
                 SecurityService.ACL_CLASS_KEY,
 243  
                 SecurityService.ACL_CLASS_DEFAULT);
 244  
 
 245  
         try
 246  
         {
 247  24
             userClass = Class.forName(userClassName);
 248  24
             groupClass = Class.forName(groupClassName);
 249  24
             permissionClass = Class.forName(permissionClassName);
 250  24
             roleClass = Class.forName(roleClassName);
 251  24
             aclClass = Class.forName(aclClassName);
 252  
         }
 253  0
         catch (Exception e)
 254  
         {
 255  0
             if (userClass == null)
 256  
             {
 257  0
                 throw new InitializationException(
 258  
                         "Failed to create a Class object for User implementation", e);
 259  
             }
 260  0
             if (groupClass == null)
 261  
             {
 262  0
                 throw new InitializationException(
 263  
                         "Failed to create a Class object for Group implementation", e);
 264  
             }
 265  0
             if (permissionClass == null)
 266  
             {
 267  0
                 throw new InitializationException(
 268  
                         "Failed to create a Class object for Permission implementation", e);
 269  
             }
 270  0
             if (roleClass == null)
 271  
             {
 272  0
                 throw new InitializationException(
 273  
                         "Failed to create a Class object for Role implementation", e);
 274  
             }
 275  0
             if (aclClass == null)
 276  
             {
 277  0
                 throw new InitializationException(
 278  
                         "Failed to create a Class object for ACL implementation", e);
 279  
             }
 280  24
         }
 281  
 
 282  
         try
 283  
         {
 284  24
             UserManager userManager =
 285  
                     (UserManager) Class.forName(userManagerClassName).newInstance();
 286  
 
 287  24
             userManager.init(conf);
 288  
 
 289  24
             setUserManager(userManager);
 290  
         }
 291  0
         catch (Exception e)
 292  
         {
 293  0
             throw new InitializationException("Failed to instantiate UserManager", e);
 294  24
         }
 295  
 
 296  
         try
 297  
         {
 298  24
             aclFactoryService = (FactoryService)TurbineServices.getInstance().getService(FactoryService.ROLE);
 299  
         }
 300  0
         catch (Exception e)
 301  
         {
 302  0
             throw new InitializationException(
 303  
                     "BaseSecurityService.init: Failed to get the Factory Service object", e);
 304  24
         }
 305  
 
 306  24
         setInit(true);
 307  24
     }
 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  4
         if (userClass == null)
 321  
         {
 322  0
             throw new UnknownEntityException(
 323  
                     "Failed to create a Class object for User implementation");
 324  
         }
 325  4
         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  4
             user = (User) getUserClass().newInstance();
 344  
         }
 345  0
         catch (Exception e)
 346  
         {
 347  0
             throw new UnknownEntityException(
 348  
                     "Failed instantiate an User implementation object", e);
 349  4
         }
 350  4
         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  0
         User user = getUserInstance();
 369  0
         user.setName(userName);
 370  0
         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  0
         if (groupClass == null)
 385  
         {
 386  0
             throw new UnknownEntityException(
 387  
                     "Failed to create a Class object for Group implementation");
 388  
         }
 389  0
         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  0
             group = (Group) getGroupClass().newInstance();
 408  
         }
 409  0
         catch (Exception e)
 410  
         {
 411  0
             throw new UnknownEntityException("Failed to instantiate a Group implementation object", e);
 412  0
         }
 413  0
         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  0
         Group group = getGroupInstance();
 432  0
         group.setName(groupName);
 433  0
         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  0
         if (permissionClass == null)
 448  
         {
 449  0
             throw new UnknownEntityException(
 450  
                     "Failed to create a Class object for Permission implementation");
 451  
         }
 452  0
         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  0
             permission = (Permission) getPermissionClass().newInstance();
 471  
         }
 472  0
         catch (Exception e)
 473  
         {
 474  0
             throw new UnknownEntityException("Failed to instantiate a Permission implementation object", e);
 475  0
         }
 476  0
         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  0
         Permission perm = getPermissionInstance();
 494  0
         perm.setName(permName);
 495  0
         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  0
         if (roleClass == null)
 510  
         {
 511  0
             throw new UnknownEntityException(
 512  
                     "Failed to create a Class object for Role implementation");
 513  
         }
 514  0
         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  0
             role = (Role) getRoleClass().newInstance();
 534  
         }
 535  0
         catch (Exception e)
 536  
         {
 537  0
             throw new UnknownEntityException("Failed to instantiate a Role implementation object", e);
 538  0
         }
 539  0
         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  0
         Role role = getRoleInstance();
 558  0
         role.setName(roleName);
 559  0
         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  2
         if (aclClass == null)
 574  
         {
 575  0
             throw new UnknownEntityException(
 576  
                     "Failed to create a Class object for ACL implementation");
 577  
         }
 578  2
         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  4
         Object[] objects = {roles, permissions};
 597  4
         String[] signatures = {Map.class.getName(), Map.class.getName()};
 598  
         AccessControlList accessControlList;
 599  
 
 600  
         try
 601  
         {
 602  4
             accessControlList =
 603  
                     (AccessControlList) aclFactoryService.getInstance(aclClass.getName(),
 604  
                             objects,
 605  
                             signatures);
 606  
         }
 607  0
         catch (Exception e)
 608  
         {
 609  0
             throw new UnknownEntityException(
 610  
                     "Failed to instantiate an ACL implementation object", e);
 611  4
         }
 612  
 
 613  4
         return accessControlList;
 614  
     }
 615  
 
 616  
     /**
 617  
      * Returns the configured UserManager.
 618  
      *
 619  
      * @return An UserManager object
 620  
      */
 621  
     public UserManager getUserManager()
 622  
     {
 623  2
         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  24
         this.userManager = userManager;
 634  24
     }
 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  0
         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  0
         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  2
         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  0
         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  4
         User user = getUserInstance();
 715  4
         user.setName("");
 716  4
         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  20
         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  0
         getUserManager().store(user);
 747  0
     }
 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  0
         userManager.saveOnSessionUnbind(user);
 765  0
     }
 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  0
         getUserManager().createAccount(user, password);
 781  0
     }
 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  0
         revokeAll(user);
 796  
 
 797  0
         getUserManager().removeAccount(user);
 798  0
     }
 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  0
         getUserManager().changePassword(user, oldPassword, newPassword);
 817  0
     }
 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  0
         getUserManager().forcePassword(user, password);
 837  0
     }
 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  0
         readerCount++;
 848  0
     }
 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  0
         readerCount--;
 859  0
         this.notify();
 860  0
     }
 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  0
         while (readerCount > 0)
 872  
         {
 873  
             try
 874  
             {
 875  0
                 this.wait();
 876  
             }
 877  0
             catch (InterruptedException e)
 878  
             {
 879  0
             }
 880  
         }
 881  0
     }
 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  0
     }
 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  0
         if (globalGroup == null)
 904  
         {
 905  0
             synchronized (BaseSecurityService.class)
 906  
             {
 907  0
                 if (globalGroup == null)
 908  
                 {
 909  
                     try
 910  
                     {
 911  0
                         globalGroup = getAllGroups()
 912  
                                 .getGroupByName(Group.GLOBAL_GROUP_NAME);
 913  
                     }
 914  0
                     catch (DataBackendException e)
 915  
                     {
 916  0
                         log.error("Failed to retrieve global group object: ", e);
 917  0
                     }
 918  
                 }
 919  0
             }
 920  
         }
 921  0
         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  0
         Group group = getAllGroups().getGroupByName(name);
 937  0
         if (group == null)
 938  
         {
 939  0
             throw new UnknownEntityException(
 940  
                     "The specified group does not exist");
 941  
         }
 942  0
         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  0
         Group group = getAllGroups().getGroupById(id);
 959  0
         if (group == null)
 960  
         {
 961  0
             throw new UnknownEntityException(
 962  
                     "The specified group does not exist");
 963  
         }
 964  0
         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  0
         Role role = getAllRoles().getRoleByName(name);
 980  0
         if (role == null)
 981  
         {
 982  0
             throw new UnknownEntityException(
 983  
                     "The specified role does not exist");
 984  
         }
 985  0
         role.setPermissions(getPermissions(role));
 986  0
         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  0
         Role role = getAllRoles().getRoleById(id);
 1003  0
         if (role == null)
 1004  
         {
 1005  0
             throw new UnknownEntityException(
 1006  
                     "The specified role does not exist");
 1007  
         }
 1008  0
         role.setPermissions(getPermissions(role));
 1009  0
         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  0
         Permission permission = getAllPermissions().getPermissionByName(name);
 1025  0
         if (permission == null)
 1026  
         {
 1027  0
             throw new UnknownEntityException(
 1028  
                     "The specified permission does not exist");
 1029  
         }
 1030  0
         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  0
         Permission permission = getAllPermissions().getPermissionById(id);
 1048  0
         if (permission == null)
 1049  
         {
 1050  0
             throw new UnknownEntityException(
 1051  
                     "The specified permission does not exist");
 1052  
         }
 1053  0
         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  
 }