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}