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.configuration2.Configuration;
028import org.apache.fulcrum.factory.FactoryService;
029import org.apache.fulcrum.security.acl.AccessControlList;
030import org.apache.fulcrum.security.model.turbine.TurbineUserManager;
031import org.apache.fulcrum.security.model.turbine.entity.TurbineUser;
032import org.apache.fulcrum.security.util.DataBackendException;
033import org.apache.fulcrum.security.util.EntityExistsException;
034import org.apache.fulcrum.security.util.PasswordMismatchException;
035import org.apache.fulcrum.security.util.UnknownEntityException;
036import org.apache.fulcrum.security.util.UserSet;
037import org.apache.logging.log4j.LogManager;
038import org.apache.logging.log4j.Logger;
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>
054 * <li>either in a method with the same name (and very similar signature)</li>
055 * <li>or mapped to method names as listed below:
056 *
057 * <ul>
058 * <li>method(s) in this manager -&gt; Fulcrum manager method(s)
059 * <li>{@link #createAccount(User, String)}createAccount -&gt; addUser(User, String)
060 * <li>{@link #removeAccount(User)} -&gt; removeUser(User)
061 * <li>{@link #store(User)} -&gt; saveUser(User)
062 * <li>{@link #retrieve(String)} and {@link #retrieve(String, String)} -&gt; getUser(String), getUser(String, String)
063 * <li>{@link #retrieveList(Object)} -&gt; getAllUsers()
064 * <li>{@link #accountExists(String)}, {@link #accountExists(User)} -&gt; checkExists(String), checkExists(User)
065 * </ul>
066 *
067 * </li>
068 * </ol>
069 *
070 * In this way all public methods of Fulcrum {@link TurbineUserManager} interface are used by reference of the Fulcrum delegate {@link #umDelegate}
071 * and wrapped by this manager.
072 *
073 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
074 * @version $Id: PassiveUserManager.java 1096130 2011-04-23 10:37:19Z ludwig $
075 */
076public class DefaultUserManager implements UserManager
077{
078    /** Fulcrum user manager instance to delegate to */
079    private TurbineUserManager umDelegate = null;
080
081    private FactoryService factoryService = null;
082
083    /** The user class, which the UserManager uses as wrapper for Fulcrum {@link TurbineUser} */
084    private String userWrapperClass;
085
086
087    /** Logging */
088    private static final Logger log = LogManager.getLogger(DefaultUserManager.class);
089
090    /**
091     * Wrap a Fulcrum user object into a Turbine user object
092     *
093     * @param <U> user class
094     * @param user the user object to delegate to
095     *
096     * @return the wrapped object
097     */
098    protected <U extends User> U wrap(TurbineUser user)
099    {
100        @SuppressWarnings("unchecked")
101        U u = (U) getUserWrapper(user);
102        return u;
103    }
104
105    /**
106     * Exception could be ignored, as it is tested before in {@link #init(Configuration)}.
107     *
108     * @param <U> user class
109     * @param user the user object to wrap
110     * @return instance extending {@link User}
111     */
112    @SuppressWarnings("unchecked")
113        public <U extends User> U getUserWrapper(TurbineUser user)
114    {
115                try
116                {
117            Object params[] = new Object[] { user };
118            String signature[] = new String[] { TurbineUser.class.getName() };
119            return (U) factoryService.getInstance(getUserWrapperClass(), params, signature);
120                }
121                catch (Exception e)
122                {
123                        log.error("after init/late instantiation exception", e);
124                        return null; // (U)new DefaultUserImpl(user);
125                }
126        }
127
128    /**
129     * Get the wrapper class for user objects
130     *
131     * @return the wrapper class name
132     */
133    public String getUserWrapperClass()
134    {
135                return userWrapperClass;
136        }
137
138    /**
139     * Set the wrapper class for user objects
140     *
141     * @param userWrapperClass2 the wrapper class name
142     */
143    public void setUserWrapperClass(String userWrapperClass2)
144    {
145                userWrapperClass = userWrapperClass2;
146        }
147
148        /**
149     * Initializes the UserManager
150     *
151     * @param conf A Configuration object to init this Manager
152     */
153    @Override
154    public void init(Configuration conf) throws InitializationException
155    {
156        ServiceManager manager = TurbineServices.getInstance();
157        this.umDelegate = (TurbineUserManager)manager.getService(TurbineUserManager.ROLE);
158
159        String userWrapperClass = conf.getString(
160                SecurityService.USER_WRAPPER_KEY,
161                SecurityService.USER_WRAPPER_DEFAULT);
162
163        try
164        {
165                factoryService = (FactoryService)manager.getService(FactoryService.ROLE);
166
167            //  check instantiation
168                // should provide default constructor
169                TurbineUser turbineUser = umDelegate.getUserInstance();
170                                //(TurbineUser) factoryService.getInstance(userClass);
171            Object params[] = new Object[] { turbineUser };
172            String signature[] = new String[] { TurbineUser.class.getName() };
173
174            // Just check if exceptions would occur
175            factoryService.getInstance(userWrapperClass, params, signature);
176
177            this.setUserWrapperClass(userWrapperClass);
178        }
179        catch (Exception e)
180            {
181               throw new InitializationException("Failed to instantiate user wrapper class", e);
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<>();
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}