Package org.apache.turbine.services.security

Source Code of org.apache.turbine.services.security.BaseSecurityService

package org.apache.turbine.services.security;

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
*    "Apache Turbine" must not be used to endorse or promote products
*    derived from this software without prior written permission. For
*    written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    "Apache Turbine", nor may "Apache" appear in their name, without
*    prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

import javax.servlet.ServletConfig;

import org.apache.turbine.util.Log;
import org.apache.turbine.util.RunData;
import org.apache.turbine.util.db.Criteria;
import org.apache.turbine.om.security.User;
import org.apache.turbine.om.security.Group;
import org.apache.turbine.om.security.Role;
import org.apache.turbine.om.security.Permission;
import org.apache.turbine.om.security.peer.UserPeer;

import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.resources.TurbineResources;
import org.apache.turbine.services.security.UserManager;
import org.apache.turbine.util.security.GroupSet;
import org.apache.turbine.util.security.RoleSet;
import org.apache.turbine.util.security.PermissionSet;

import org.apache.turbine.util.security.DataBackendException;
import org.apache.turbine.util.security.UnknownEntityException;
import org.apache.turbine.util.security.EntityExistsException;
import org.apache.turbine.util.security.PasswordMismatchException;
import org.apache.turbine.util.security.TurbineSecurityException;

// Classes needed for password encryption
import javax.mail.internet.MimeUtility;
import java.security.MessageDigest;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;

/**
* This is a common subset of SecurityService implementation.
*
* Provided functionality includes:
* <ul>
* <li> methods for retrieving User objects, that delegates functionality
*      to the pluggable implementations of the User interface.
* <li> synchronization mechanism for methods reading/modifying the security
*      information, that guarantees that multiple threads may read the
*      information concurrently, but threads that modify the information
*      acquires exclusive access.
* <li> implementation of convenience methods for retrieving security entities
*      that maintain in-memory caching of objects for fast access.
* </ul>
*
* @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
* @version $Id$
*/
public abstract class BaseSecurityService
    extends TurbineBaseService implements SecurityService
{
    // management of Groups/Role/Permissions

    /** Holds a list of all groups in the systems, for speeding up the access */
    private GroupSet allGroups = null;

    /** Holds a list of all roles in the systems, for speeding up the access */
    private RoleSet allRoles = null;
   
    /** Holds a list of all permissions in the systems, for speeding up the access */
    private PermissionSet allPermissions = null;

    /** The number of threads concurrently reading security information */
    private int readerCount = 0;

    /** The instance of UserManager the SecurityService uses */
    private UserManager userManager = null;
  
    /**
     * The Group object that represents the <a href="#global">global group</a>.
     */
    private static Group globalGroup = null;

    /**
     * This method provides client-side encryption of passwords.
     *
     * If <code>secure.passwords</code> are enabled in TurbineResources,
     * the password will be encrypted, if not, it will be returned unchanged.
     * The <code>secure.passwords.algorithm</code> property can be used
     * to chose which digest algorithm should be used for performing the
     * encryption. <code>SHA</code> is used by default.
     *
     * @param password the password to process
     * @return processed password
     */
    public String encryptPassword( String password )
    {
        if(password == null)
            return null;
        String secure = getProperties().getProperty(
            SecurityService.SECURE_PASSWORDS_KEY,
            SecurityService.SECURE_PASSWORDS_DEFAULT).toLowerCase();
        String algorithm = getProperties().getProperty(
            SecurityService.SECURE_PASSWORDS_ALGORITHM_KEY,
            SecurityService.SECURE_PASSWORDS_ALGORITHM_DEFAULT);
        if (secure.equals("true") || secure.equals("yes"))
        {
            try
            {
                MessageDigest md = MessageDigest.getInstance(algorithm);
                // We need to use unicode here, to be independent of platform's
                // default encoding. Thanks to SGawin for spotting this.
                byte[] digest = md.digest(password.getBytes("UTF-8"));
                ByteArrayOutputStream bas = new ByteArrayOutputStream(digest.length + digest.length / 3 + 1);
                OutputStream encodedStream = MimeUtility.encode(bas, "base64");
                encodedStream.write(digest);
                return bas.toString();
            }
            catch (Exception e)
            {
                Log.error("Unable to encrypt password."+e.getMessage());
                Log.error(e);
           
                return null;
            }
        } else {
            return password;
        }
    }

    /**
     * Initializes the SecurityService, locating the apropriate UserManager
     *
     * @param config a ServletConfig, to enforce early initialization
     */
    public void init(ServletConfig config)
        throws InitializationException
    {
        String userManagerClassName = getProperties().getProperty(
            SecurityService.USER_MANAGER_KEY,
            SecurityService.USER_MANAGER_DEFAULT);
        try
        {
            userManager =  (UserManager)Class.
                forName(userManagerClassName).newInstance();
            setInit(true);
        }
        catch(Exception e)
        {
            throw new InitializationException("BaseSecurityService.init: Failed to instantiate UserManager" ,e);
        }
    }

    /**
     * Return a Class object representing the system's chosen implementation of
     * of User interface.
     *
     * @return systems's chosen implementation of User interface.
     * @throws UnknownEntityException if the implementation of User interface
     *         could not be determined, or does not exist.
     */
    public Class getUserClass()
        throws UnknownEntityException
    {
        String userClassName = getProperties().getProperty(
            SecurityService.USER_CLASS_KEY,
            SecurityService.USER_CLASS_DEFAULT);
        try
        {
            return Class.forName(userClassName);
        }
        catch(Exception e)
        {
            throw new UnknownEntityException("Failed create a Class object for User implementation", e);
        }
    }

    /**
     * Construct a blank User object.
     *
     * This method calls getUserClass, and then creates a new object using
     * the default constructor.
     *
     * @return an object implementing User interface.
     * @throws UnknownEntityException if the object could not be instantiated.
     */
    public User getUserInstance()
        throws UnknownEntityException
    {
        User user;
        try
        {
            user = (User)getUserClass().newInstance();
        }
        catch(Exception e)
        {
            throw new UnknownEntityException("Failed instantiate an User implementation object", e);
        }
        return user;
    }
 
    /**
     * Check whether a specified user's account exists.
     *
     * The login name is used for looking up the account.
     *
     * @param user The user to be checked.
     * @return true if the specified account exists
     * @throws DataBackendException if there was an error accessing the data backend.
     */
    public boolean accountExists( User user )
        throws DataBackendException
    {
        return userManager.accountExists(user);
    }

    /**
     * Check whether a specified user's account exists.
     *
     * The login name is used for looking up the account.
     *
     * @param usename The name of the user to be checked.
     * @return true if the specified account exists
     * @throws DataBackendException if there was an error accessing the data backend.
     */
    public boolean accountExists( String username )
        throws DataBackendException
    {
        return userManager.accountExists(username);
    }

    /**
     * Authenticates an user, and constructs an User object to represent
     * him/her.
     *
     * @param username The user name.
     * @param password The user password.
     * @return An authenticated Turbine User.
     * @exception PasswordMismatchException if the supplied password was
     *            incorrect.
     * @exception UnknownEntityException if the user's account does not
     *            exist in the database.
     * @exception DataBackendException if there is a problem accessing the
     *            storage.
     */
    public User getAuthenticatedUser( String username, String password )
        throws DataBackendException, UnknownEntityException,
               PasswordMismatchException
    {
        return userManager.retrieve(username, password);
    }

    /**
     * Constructs an User object to represent a registered user of the application.
     *
     * @param username The user name.
     * @return A Turbine User.
     * @exception UnknownEntityException if the user's account does not
     *            exist in the database.
     * @exception DataBackendException if there is a problem accessing the
     *            storage.
     */
    public User getUser( String username )
        throws DataBackendException, UnknownEntityException
    {
        return userManager.retrieve(username);
    }

    /**
     * Retrieve a set of users that meet the specified criteria.
     *
     * As the keys for the criteria, you should use the constants that
     * are defined in {@link User} interface, plus the names
     * of the custom attributes you added to your user representation
     * in the data storage. Use verbatim names of the attributes -
     * without table name prefix in case of DB implementation.
     *
     * @param criteria The criteria of selection.
     * @return a List of users meeting the criteria.
     * @throws DataBackendException if there is a problem accessing the
     *         storage.
     */
    public User[] getUsers( Criteria criteria )
        throws DataBackendException
    {
        return userManager.retrieve(criteria);
    }

    /**
     * Constructs an User object to represent an anonymous user of the application.
     *
     * @return An anonymous Turbine User.
     * @throws UnknownEntityException if the implementation of User interface
     *         could not be determined, or does not exist.
     */
    public User getAnonymousUser()
        throws UnknownEntityException
    {
        User user = getUserInstance();
        user.setUserName("");
        return user;
    }

    /**
     * Saves User's data in the permanent storage. The user account is required
     * to exist in the storage.
     *
     * @exception UnknownEntityException if the user's account does not
     *            exist in the database.
     * @exception DataBackendException if there is a problem accessing the
     *            storage.
     */
    public void saveUser( User user )
        throws UnknownEntityException, DataBackendException
    {
        userManager.store(user);
    }

    /**
     * Creates new user account with specified attributes.
     *
     * @param user the object describing account to be created.
     * @throws DataBackendException if there was an error accessing the data backend.
     * @throws EntityExistsException if the user account already exists.
     */
    public void addUser( User user, String password )
        throws DataBackendException, EntityExistsException
    {
        userManager.createAccount(user, password);
    }

    /**
     * Removes an user account from the system.
     *
     * @param user the object describing the account to be removed.
     * @throws DataBackendException if there was an error accessing the data backend.
     * @throws UnknownEntityException if the user account is not present.
     */
    public void removeUser( User user )
        throws DataBackendException, UnknownEntityException
    {
        // revoke all roles form the user
        revokeAll(user);

        userManager.removeAccount(user);
    }

    /**
     * Change the password for an User.
     *
     * @param user an User to change password for.
     * @param oldPassword the current password supplied by the user.
     * @param newPassword the current password requested by the user.
     * @exception PasswordMismatchException if the supplied password was
     *            incorrect.
     * @exception UnknownEntityException if the user's record does not
     *            exist in the database.
     * @exception DataBackendException if there is a problem accessing the
     *            storage.
     */
    public void changePassword( User user, String oldPassword, String newPassword )
        throws PasswordMismatchException, UnknownEntityException,
               DataBackendException
    {
        userManager.changePassword(user, oldPassword, newPassword);
    }

    /**
     * Forcibly sets new password for an User.
     *
     * This is supposed by the administrator to change the forgotten or
     * compromised passwords. Certain implementatations of this feature
     * would require administrative level access to the authenticating
     * server / program.
     *
     * @param user an User to change password for.
     * @param password the new password.
     * @exception UnknownEntityException if the user's record does not
     *            exist in the database.
     * @exception DataBackendException if there is a problem accessing the
     *            storage.
     */
    public void forcePassword( User user, String password )
        throws UnknownEntityException, DataBackendException
    {
        userManager.forcePassword( user, password );
    }

    /**
     * Acquire a shared lock on the security information repository.
     *
     * Methods that read security information need to invoke this
     * method at the beginning of their body.
     */
    protected synchronized void lockShared()
    {
        readerCount++;
    }

    /**
     * Release a shared lock on the security information repository.
     *
     * Methods that read security information need to invoke this
     * method at the end of their body.
     */
    protected synchronized void unlockShared()
    {
        readerCount--;
        this.notify();
    }

    /**
     * Acquire an exclusive lock on the security information repository.
     *
     * Methods that modify security information need to invoke this
     * method at the beginning of their body. Note! Those methods must
     * be <code>synchronized</code> themselves!
     */
    protected void lockExclusive()
    {
        while(readerCount>0)
        {
            try
            {
               this.wait();
            }
            catch(InterruptedException e)
            {
            }
        }
    }

    /**
     * Release an exclusive lock on the security information repository.
     *
     * This method is provided only for completeness. It does not really
     * do anything. Note! Methods that modify security information
     * must be <code>synchronized</code>!
     */
    protected void unlockExclusive()
    {
        // do nothing
    }

    /**
     * Provides a reference to the Group object that represents the
     * <a href="#global">global group</a>.
     *
     * @return a Group object that represents the global group.
     */
    public Group getGlobalGroup()
    {
        if(globalGroup == null)
        {
            synchronized(BaseSecurityService.class)
            {
                if(globalGroup == null)
                {
                    try
                    {
                        globalGroup = getAllGroups()
                            .getGroup(Group.GLOBAL_GROUP_NAME);
                    }
                    catch (DataBackendException e)
                    {
                        Log.error("Failed to retrieve global group object");
                        Log.error(e);
                    }
                }
            }
        }
        return globalGroup;
    }

    /**
     * Retrieve a Group object with specified name.
     *
     * @param name the name of the Group.
     * @return an object representing the Group with specified name.
     */
    public Group getGroup( String name )
        throws DataBackendException, UnknownEntityException
    {
        GroupSet groups = getAllGroups();
        Group group = groups.getGroup(name);
        if(group != null)
        {
            return group;
        }
        else
        {
            throw new UnknownEntityException("The specified group does not exist");
        }
    }

    /**
     * Retrieve a Role object with specified name.
     *
     * @param name the name of the Role.
     * @return an object representing the Role with specified name.
     */
    public Role getRole( String name )
        throws DataBackendException, UnknownEntityException
    {
        RoleSet roles = getAllRoles();
        Role role = roles.getRole(name);
        if(role != null)
        {
            role.setPermissions(getPermissions(role));
            return role;
        }
        else
        {
            throw new UnknownEntityException("The specified role does not exist");
        }
    }

    /**
     * Retrieve a Permission object with specified name.
     *
     * @param name the name of the Permission.
     * @return an object representing the Permission with specified name.
     */
    public Permission getPermission( String name )
        throws DataBackendException, UnknownEntityException
    {
        PermissionSet permissions = getAllPermissions();
        Permission permission = permissions.getPermission(name);
        if(permission != null)
        {
            return permission;
        }
        else
        {
            throw new UnknownEntityException("The specified permission does not exist");
        }
    }

    /**
     * Retrieves all groups defined in the system.
     *
     * @return the names of all groups defined in the system.
     * @throws DataBackendException if there was an error accessing the data backend.
     */
    public GroupSet getAllGroups()
        throws DataBackendException
    {
        if(allGroups == null)
        {
            synchronized(this)
            {
                if(allGroups == null)
                {
                    allGroups = getGroups( new Criteria() );
                }
            }
        }
        return allGroups;
    }

    /**
     * Retrieves all roles defined in the system.
     *
     * @return the names of all roles defined in the system.
     * @throws DataBackendException if there was an error accessing the data backend.
     */
    public RoleSet getAllRoles()
        throws DataBackendException
    {
        if(allRoles == null)
        {
            synchronized(this)
            {
                if(allRoles == null)
                {
                    allRoles = getRoles( new Criteria() );
                }
            }
        }
        return allRoles;
    }

    /**
     * Retrieves all permissions defined in the system.
     *
     * @return the names of all roles defined in the system.
     * @throws DataBackendException if there was an error accessing the data backend.
     */
    public PermissionSet getAllPermissions()
        throws DataBackendException
    {
        if(allPermissions == null)
        {
            synchronized(this)
            {
                if(allPermissions == null)
                {
                    allPermissions = getPermissions( new Criteria() );
                }
            }
        }
        return allPermissions;
    }
}
TOP

Related Classes of org.apache.turbine.services.security.BaseSecurityService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.