    001package org.apache.turbine.services.security;
    002
    003
    004/*
    005 * Licensed to the Apache Software Foundation (ASF) under one
    006 * or more contributor license agreements.  See the NOTICE file
    007 * distributed with this work for additional information
    008 * regarding copyright ownership.  The ASF licenses this file
    009 * to you under the Apache License, Version 2.0 (the
    010 * "License"); you may not use this file except in compliance
    011 * with the License.  You may obtain a copy of the License at
    012 *
    013 *   http://www.apache.org/licenses/LICENSE-2.0
    014 *
    015 * Unless required by applicable law or agreed to in writing,
    016 * software distributed under the License is distributed on an
    017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    018 * KIND, either express or implied.  See the License for the
    019 * specific language governing permissions and limitations
    020 * under the License.
    021 */
    022
    023
    024import java.util.ArrayList;
    025import java.util.List;
    026
    027import org.apache.commons.configuration.Configuration;
    028import org.apache.commons.logging.Log;
    029import org.apache.commons.logging.LogFactory;
    030import org.apache.fulcrum.factory.FactoryService;
    031import org.apache.fulcrum.security.acl.AccessControlList;
    032import org.apache.fulcrum.security.model.turbine.TurbineUserManager;
    033import org.apache.fulcrum.security.model.turbine.entity.TurbineUser;
    034import org.apache.fulcrum.security.util.DataBackendException;
    035import org.apache.fulcrum.security.util.EntityExistsException;
    036import org.apache.fulcrum.security.util.PasswordMismatchException;
    037import org.apache.fulcrum.security.util.UnknownEntityException;
    038import org.apache.fulcrum.security.util.UserSet;
    039import org.apache.turbine.om.security.TurbineUserDelegate;
    040import org.apache.turbine.om.security.User;
    041import org.apache.turbine.services.InitializationException;
    042import org.apache.turbine.services.ServiceManager;
    043import org.apache.turbine.services.TurbineServices;
    044import org.apache.turbine.util.ObjectUtils;
    045
    046/**
    047 * Default user manager.
    048 *
    049 * The user manager wraps Fulcrum security user objects into
    050 * Turbine-specific ones.
    051 *
    052 *
    053 * <ol><li>either in a method with the same name (and very similar signature)
    054 * <li>or mapped to method names as listed below:
    055 *
    056 * <ul>
    057 * <li>method(s) in this manager -> Fulcrum manager method(s)
    058 * <li>{@link #createAccount(User, String)}createAccount -> addUser(User, String)
    059 * <li>{@link #removeAccount(User)} -> removeUser(User)
    060 * <li>{@link #store(User)} -> saveUser(User)
    061 * <li>{@link #retrieve(String)} and {@link #retrieve(String, String)} -> getUser(String), getUser(String, String)
    062 * <li>{@link #retrieveList(Object)} ->getAllUsers()
    063 * <li>{@link #accountExists(String)}, {@link #accountExists(User)} -> checkExists(String), checkExists(User)
    064 *
    065 * In this way all public methods of Fulcrum {@link TurbineUserManager} interface are used by reference of the Fulcrum delegate {@link #umDelegate}
    066 * and wrapped by this manager.
    067 *
    068 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
    069 * @version $Id: PassiveUserManager.java 1096130 2011-04-23 10:37:19Z ludwig $
    070 */
    071public class DefaultUserManager implements UserManager
    072{
    073    /** Fulcrum user manager instance to delegate to */
    074    private TurbineUserManager umDelegate = null;
    075
    076    private FactoryService factoryService = null;
    077
    078    /** The user class, which the UserManager uses as wrapper for Fulcrum {@link TurbineUser} */
    079    private String userWrapperClass;
    080
    081
    082    /** Logging */
    083    private static Log log = LogFactory.getLog(DefaultUserManager.class);
    084
    085    /**
    086     * Wrap a Fulcrum user object into a Turbine user object
    087     *
    088     * @param user the user object to delegate to
    089     *
    090     * @return the wrapped object
    091     */
    092    protected <U extends User> U wrap(TurbineUser user)
    093    {
    094        // U u = (U)new DefaultUserImpl(user);
    095        @SuppressWarnings("unchecked")
    096        U u = (U) getUserWrapper(user);
    097        return u;
    098    }
    099
    100    /**
    101     * Exception could be ignored, as it is tested before in {@link #init(Configuration)}.
    102     *
    103     * @param user the user object to wrap
    104     * @return instance extending {@link User}
    105     */
    106    @SuppressWarnings("unchecked")
    107        public <U extends User> U getUserWrapper(TurbineUser user)
    108    {
    109                try
    110                {
    111            Object params[] = new Object[] { user };
    112            String signature[] = new String[] { TurbineUser.class.getName() };
    113            return (U) factoryService.getInstance(getUserWrapperClass(), params, signature);
    114                }
    115                catch (Exception e)
    116                {
    117                        log.error("after init/late instantiation exception", e);
    118                        return null; // (U)new DefaultUserImpl(user);
    119                }
    120        }
    121
    122    /**
    123     * Get the wrapper class for user objects
    124     *
    125     * @return the wrapper class name
    126     */
    127    public String getUserWrapperClass()
    128    {
    129                return userWrapperClass;
    130        }
    131
    132    /**
    133     * Set the wrapper class for user objects
    134     *
    135     * @param userWrapperClass2 the wrapper class name
    136     */
    137    public void setUserWrapperClass(String userWrapperClass2)
    138    {
    139                userWrapperClass = userWrapperClass2;
    140        }
    141
    142        /**
    143     * Initializes the UserManager
    144     *
    145     * @param conf A Configuration object to init this Manager
    146     */
    147    @Override
    148    public void init(Configuration conf) throws InitializationException
    149    {
    150        ServiceManager manager = TurbineServices.getInstance();
    151        this.umDelegate = (TurbineUserManager)manager.getService(TurbineUserManager.ROLE);
    152
    153        String userWrapperClass = conf.getString(
    154                SecurityService.USER_WRAPPER_KEY,
    155                SecurityService.USER_WRAPPER_DEFAULT);
    156
    157//        String userClass = conf.getString(
    158//                SecurityService.USER_KEY,
    159//                SecurityService.USER_DEFAULT);
    160
    161        try
    162        {
    163                factoryService = (FactoryService)manager.getService(FactoryService.ROLE);
    164
    165            //  check instantiation
    166
    167                // should provide default constructor
    168                TurbineUser turbineUser = umDelegate.getUserInstance();
    169                                //(TurbineUser) factoryService.getInstance(userClass);
    170            Object params[] = new Object[] { turbineUser };
    171            String signature[] = new String[] { TurbineUser.class.getName() };
    172
    173            // Just check if exceptions would occur
    174            factoryService.getInstance(userWrapperClass, params, signature);
    175
    176            this.setUserWrapperClass(userWrapperClass);
    177        }
    178        catch (Exception e)
    179            {
    180               throw new InitializationException("Failed to instantiate user wrapper class", e);
    181            }
    182
    183    }
    184
    185
    186        /**
    187     * Check whether a specified user's account exists.
    188     *
    189     * The login name is used for looking up the account.
    190     *
    191     * @param user The user to be checked.
    192     * @return true if the specified account exists
    193     * @throws DataBackendException if there was an error accessing the data backend.
    194     */
    195    @Override
    196    public boolean accountExists(User user)
    197            throws DataBackendException
    198    {
    199        if (user == null) {
    200            return false;
    201        }
    202        return umDelegate.checkExists(user.getUserDelegate());
    203    }
    204
    205    /**
    206     * Check whether a specified user's account exists.
    207     *
    208     * The login name is used for looking up the account.
    209     *
    210     * @param userName The name of the user to be checked.
    211     * @return true if the specified account exists
    212     * @throws DataBackendException if there was an error accessing the data backend.
    213     */
    214    @Override
    215    public boolean accountExists(String userName)
    216            throws DataBackendException
    217    {
    218        return umDelegate.checkExists(userName);
    219    }
    220
    221    /**
    222     * Retrieve a user from persistent storage using username as the
    223     * key.
    224     *
    225     * @param username the name of the user.
    226     * @return an User object.
    227     * @throws UnknownEntityException if the user's record does not
    228     *            exist in the database.
    229     * @throws DataBackendException if there is a problem accessing the
    230     *            storage.
    231     */
    232    @Override
    233    public <U extends User> U retrieve(String username)
    234            throws UnknownEntityException, DataBackendException
    235    {
    236        TurbineUser u = umDelegate.getUser(username);
    237        return wrap(u);
    238    }
    239
    240    /**
    241     * Retrieve a set of users that meet the specified criteria.
    242     *
    243     * As the keys for the criteria, you should use the constants that
    244     * are defined in {@link User} interface, plus the names
    245     * of the custom attributes you added to your user representation
    246     * in the data storage. Use verbatim names of the attributes -
    247     * without table name prefix in case of DB implementation.
    248     *
    249     * @param criteria The criteria of selection.
    250     * @return a List of users meeting the criteria.
    251     * @throws DataBackendException if there is a problem accessing the
    252     *         storage.
    253     */
    254    @Override
    255    public List<? extends User> retrieveList(Object criteria)
    256            throws DataBackendException
    257    {
    258        UserSet<org.apache.fulcrum.security.entity.User> uset = umDelegate.getAllUsers();
    259        List<User> userList = new ArrayList<User>();
    260
    261        for (org.apache.fulcrum.security.entity.User u : uset)
    262        {
    263            TurbineUser tu = (TurbineUser)u;
    264            userList.add(wrap(tu));
    265        }
    266
    267        return userList;
    268    }
    269
    270    /**
    271     * Retrieve a user from persistent storage using username as the
    272     * key, and authenticate the user. The implementation may chose
    273     * to authenticate to the server as the user whose data is being
    274     * retrieved.
    275     *
    276     * @param username the name of the user.
    277     * @param password the user supplied password.
    278     * @return an User object.
    279     * @throws PasswordMismatchException if the supplied password was
    280     *            incorrect.
    281     * @throws UnknownEntityException if the user's record does not
    282     *            exist in the database.
    283     * @throws DataBackendException if there is a problem accessing the
    284     *            storage.
    285     */
    286    @Override
    287    public <U extends User> U retrieve(String username, String password)
    288            throws PasswordMismatchException, UnknownEntityException,
    289            DataBackendException
    290    {
    291        TurbineUser u = umDelegate.getUser(username, password);
    292        return wrap(u);
    293    }
    294
    295    /**
    296     * Save an User object to persistent storage. User's record is
    297     * required to exist in the storage.
    298     *
    299     * @param user an User object to store.
    300     * @throws UnknownEntityException if the user's record does not
    301     *            exist in the database.
    302     * @throws DataBackendException if there is a problem accessing the
    303     *            storage.
    304     */
    305    @Override
    306    public void store(User user)
    307            throws UnknownEntityException, DataBackendException
    308    {
    309        if (user == null) {
    310            throw new UnknownEntityException("user is null");
    311        }
    312        try
    313        {
    314            user.setObjectdata(ObjectUtils.serializeMap(user.getPermStorage()));
    315        }
    316        catch (Exception e)
    317        {
    318            throw new DataBackendException("Could not serialize permanent storage", e);
    319        }
    320
    321        umDelegate.saveUser(((TurbineUserDelegate)user).getUserDelegate());
    322    }
    323
    324    /**
    325     * Saves User data when the session is unbound. The user account is required
    326     * to exist in the storage.
    327     *
    328     * LastLogin, AccessCounter, persistent pull tools, and any data stored
    329     * in the permData hashtable that is not mapped to a column will be saved.
    330     *
    331     * @throws UnknownEntityException if the user's account does not
    332     *            exist in the database.
    333     * @throws DataBackendException if there is a problem accessing the
    334     *            storage.
    335     */
    336    @Override
    337    public void saveOnSessionUnbind(User user)
    338            throws UnknownEntityException, DataBackendException
    339    {
    340        store(user);
    341    }
    342
    343    /**
    344     * Authenticate an User with the specified password. If authentication
    345     * is successful the method returns nothing. If there are any problems,
    346     * exception was thrown.
    347     *
    348     * @param user an User object to authenticate.
    349     * @param password the user supplied password.
    350     * @throws PasswordMismatchException if the supplied password was
    351     *            incorrect.
    352     * @throws UnknownEntityException if the user's record does not
    353     *            exist in the database.
    354     * @throws DataBackendException if there is a problem accessing the
    355     *            storage.
    356     */
    357    @Override
    358    public void authenticate(User user, String password)
    359            throws PasswordMismatchException, UnknownEntityException,
    360            DataBackendException
    361    {
    362        umDelegate.authenticate(user, password);
    363    }
    364
    365    /**
    366     * Creates new user account with specified attributes.
    367     *
    368     * @param user the object describing account to be created.
    369     * @param initialPassword The password to use for the object creation
    370     *
    371     * @throws DataBackendException if there was an error accessing the data backend.
    372     * @throws EntityExistsException if the user account already exists.
    373     */
    374    @Override
    375    public void createAccount(User user, String initialPassword)
    376            throws UnknownEntityException, EntityExistsException, DataBackendException
    377    {
    378        if (user == null) {
    379            throw new UnknownEntityException("user is null");
    380        }
    381        umDelegate.addUser(user.getUserDelegate(), initialPassword);
    382    }
    383
    384    /**
    385     * Removes an user account from the system.
    386     *
    387     * @param user the object describing the account to be removed.
    388     * @throws DataBackendException if there was an error accessing the data backend.
    389     * @throws UnknownEntityException if the user account is not present.
    390     */
    391    @Override
    392    public void removeAccount(User user)
    393            throws UnknownEntityException, DataBackendException
    394    {
    395        if (user == null) {
    396            throw new UnknownEntityException("user is null");
    397        }
    398        umDelegate.removeUser(user.getUserDelegate());
    399    }
    400
    401    /**
    402     * Change the password for an User.
    403     *
    404     * @param user an User to change password for.
    405     * @param oldPassword the current password supplied by the user.
    406     * @param newPassword the current password requested by the user.
    407     * @throws PasswordMismatchException if the supplied password was
    408     *            incorrect.
    409     * @throws UnknownEntityException if the user's record does not
    410     *            exist in the database.
    411     * @throws DataBackendException if there is a problem accessing the
    412     *            storage.
    413     */
    414    @Override
    415    public void changePassword(User user, String oldPassword,
    416                               String newPassword)
    417            throws PasswordMismatchException, UnknownEntityException,
    418            DataBackendException
    419    {
    420        if (user == null) {
    421            throw new UnknownEntityException("user is null");
    422        }
    423        umDelegate.changePassword(
    424                ((TurbineUserDelegate)user).getUserDelegate(),
    425                oldPassword, newPassword);
    426    }
    427
    428    /**
    429     * Forcibly sets new password for an User.
    430     *
    431     * This is supposed by the administrator to change the forgotten or
    432     * compromised passwords. Certain implementations of this feature
    433     * would require administrative level access to the authenticating
    434     * server / program.
    435     *
    436     * @param user an User to change password for.
    437     * @param password the new password.
    438     * @throws UnknownEntityException if the user's record does not
    439     *            exist in the database.
    440     * @throws DataBackendException if there is a problem accessing the
    441     *            storage.
    442     */
    443    @Override
    444    public void forcePassword(User user, String password)
    445            throws UnknownEntityException, DataBackendException
    446    {
    447        if (user == null) {
    448            throw new UnknownEntityException("user is null");
    449        }
    450        umDelegate.forcePassword(user.getUserDelegate(), password);
    451    }
    452
    453    /**
    454     * Constructs an User object to represent an anonymous user of the
    455     * application.
    456     *
    457     * @return An anonymous Turbine User.
    458     * @throws UnknownEntityException
    459     *             if the anonymous User object couldn't be constructed.
    460     */
    461    @Override
    462    public <U extends User> U getAnonymousUser() throws UnknownEntityException
    463    {
    464        TurbineUser u = umDelegate.getAnonymousUser();
    465        return wrap(u);
    466    }
    467
    468    /**
    469     * Checks whether a passed user object matches the anonymous user pattern
    470     * according to the configured user manager
    471     *
    472     * @param u a user object
    473     *
    474     * @return True if this is an anonymous user
    475     *
    476     */
    477    @Override
    478    public boolean isAnonymousUser(User u)
    479    {
    480        return umDelegate.isAnonymousUser(u);
    481    }
    482
    483    /**
    484     * Construct a blank User object.
    485     *
    486     * This method calls getUserClass, and then creates a new object using the
    487     * default constructor.
    488     *
    489     * @return an object implementing User interface.
    490     * @throws DataBackendException
    491     *             if the object could not be instantiated.
    492     */
    493    @Override
    494    public <U extends User> U getUserInstance() throws DataBackendException
    495    {
    496        TurbineUser u = umDelegate.getUserInstance();
    497        return wrap(u);
    498    }
    499
    500    /**
    501     * Construct a blank User object.
    502     *
    503     * This method calls getUserClass, and then creates a new object using the
    504     * default constructor.
    505     *
    506     * @param userName
    507     *            The name of the user.
    508     *
    509     * @return an object implementing User interface.
    510     * @throws DataBackendException
    511     *             if the object could not be instantiated.
    512     */
    513    @Override
    514    public <U extends User> U getUserInstance(String userName) throws DataBackendException
    515    {
    516        TurbineUser u = umDelegate.getUserInstance(userName);
    517        return wrap(u);
    518    }
    519
    520    /**
    521     * Return a Class object representing the system's chosen implementation of
    522     * of ACL interface.
    523     *
    524     * @return systems's chosen implementation of ACL interface.
    525     * @throws UnknownEntityException
    526     *             if the implementation of ACL interface could not be
    527     *             determined, or does not exist.
    528     */
    529    @Override
    530    public <A extends AccessControlList> A getACL(User user) throws UnknownEntityException
    531    {
    532        if (user == null) {
    533            throw new UnknownEntityException("user is null");
    534        }
    535        return umDelegate.getACL(user.getUserDelegate());
    536    }
    537}