View Javadoc
1   package org.apache.fulcrum.security.spi;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  import org.apache.commons.lang3.StringUtils;
22  import org.apache.fulcrum.security.UserManager;
23  import org.apache.fulcrum.security.acl.AccessControlList;
24  import org.apache.fulcrum.security.authenticator.Authenticator;
25  import org.apache.fulcrum.security.entity.User;
26  import org.apache.fulcrum.security.model.ACLFactory;
27  import org.apache.fulcrum.security.util.DataBackendException;
28  import org.apache.fulcrum.security.util.EntityExistsException;
29  import org.apache.fulcrum.security.util.PasswordMismatchException;
30  import org.apache.fulcrum.security.util.UnknownEntityException;
31  
32  /**
33   * This implementation keeps all objects in memory. This is mostly meant to help
34   * with testing and prototyping of ideas.
35   * 
36   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
37   * @version $Id$
38   */
39  
40  // TODO Need to load up Crypto component and actually encrypt passwords!
41  
42  public abstract class AbstractUserManager extends AbstractEntityManager implements UserManager
43  {
44      /** ID **/
45      private static final long serialVersionUID = 1L;
46  
47      /**
48       * @param user user to persist
49       * @param <T> User type
50       * @return a User object
51       * @throws DataBackendException if fail to connect
52       */
53      protected abstract <T extends User> T persistNewUser(T user) throws DataBackendException;
54  
55      private ACLFactory aclFactory;
56      private Authenticator authenticator;
57  
58      /* (non-Javadoc)
59       * @see org.apache.fulcrum.security.UserManager#getACL(org.apache.fulcrum.security.entity.User)
60       */
61      @Override
62  	public <T extends AccessControlList> T getACL(User user) throws UnknownEntityException
63      {
64          return getACLFactory().getAccessControlList(user);
65      }
66  
67      /**
68       * Check whether a specified user's account exists.
69       *
70       * The login name is used for looking up the account.
71       *
72       * @param user
73       *            The user to be checked.
74       * @return true if the specified account exists
75       * @throws DataBackendException
76       *             if there was an error accessing the data backend.
77       */
78      @Override
79  	public boolean checkExists(User user) throws DataBackendException
80      {
81          return checkExists(user.getName());
82      }
83  
84      /**
85       * Retrieve a user from persistent storage using username as the key, and
86       * authenticate the user. The implementation may chose to authenticate to
87       * the server as the user whose data is being retrieved.
88       *
89       * @param userName
90       *            the name of the user.
91       * @param password
92       *            the user supplied password.
93       * @return an User object.
94       * @exception PasswordMismatchException
95       *                if the supplied password was incorrect.
96       * @exception UnknownEntityException
97       *                if the user's account does not exist in the database.
98       * @exception DataBackendException
99       *                if there is a problem accessing the storage.
100      */
101     @Override
102 	public <T extends User> T getUser(String userName, String password) throws PasswordMismatchException, UnknownEntityException, DataBackendException
103     {
104         T user = getUser(userName);
105         authenticate(user, password);
106         return user;
107     }
108 
109     @Override
110 	public <T extends User> T getUser(String name) throws DataBackendException, UnknownEntityException
111     {
112         @SuppressWarnings("unchecked")
113 		T user = (T)getAllUsers().getByName(name);
114         if (user == null)
115         {
116             throw new UnknownEntityException("The specified user does not exist");
117         }
118         return user;
119     }
120 
121     /**
122      * Retrieve a User object with specified Id.
123      *
124      * @param id
125      *            the id of the User.
126      *
127      * @return an object representing the User with specified id.
128      *
129      * @throws UnknownEntityException
130      *             if the user does not exist in the database.
131      * @throws DataBackendException
132      *             if there is a problem accessing the storage.
133      */
134     @Override
135 	public <T extends User> T getUserById(Object id) throws DataBackendException, UnknownEntityException
136     {
137         @SuppressWarnings("unchecked")
138 		T user = (T)getAllUsers().getById(id);
139         if (user == null)
140         {
141             throw new UnknownEntityException("The specified user does not exist");
142         }
143         return user;
144     }
145 
146     /**
147      * Authenticate an User with the specified password. If authentication is
148      * successful the method returns nothing. If there are any problems,
149      * exception was thrown.
150      *
151      * @param user
152      *            an User object to authenticate.
153      * @param password
154      *            the user supplied password.
155      * @exception PasswordMismatchException
156      *                if the supplied password was incorrect.
157      * @exception UnknownEntityException
158      *                if the user's account does not exist in the database.
159      * @exception DataBackendException
160      *                if there is a problem accessing the storage.
161      */
162     @Override
163 	public void authenticate(User user, String password) throws PasswordMismatchException, UnknownEntityException, DataBackendException
164     {
165         if (authenticator == null)
166         {
167             authenticator = (Authenticator) resolve(Authenticator.ROLE);
168 
169         }
170         if (!authenticator.authenticate(user, password))
171         {
172             throw new PasswordMismatchException("Can not authenticate user.");
173         }
174     }
175 
176     /**
177      * Change the password for an User. The user must have supplied the old
178      * password to allow the change.
179      *
180      * @param user
181      *            an User to change password for.
182      * @param oldPassword
183      *            The old password to verify
184      * @param newPassword
185      *            The new password to set
186      * @exception PasswordMismatchException
187      *                if the supplied password was incorrect.
188      * @exception UnknownEntityException
189      *                if the user's account does not exist in the database.
190      * @exception DataBackendException
191      *                if there is a problem accessing the storage.
192      */
193     @Override
194 	public void changePassword(User user, String oldPassword, String newPassword) throws PasswordMismatchException, UnknownEntityException,
195             DataBackendException
196     {
197         if (!checkExists(user))
198         {
199             throw new UnknownEntityException("The account '" + user.getName() + "' does not exist");
200         }
201         if (!oldPassword.equals(user.getPassword()))
202         {
203             throw new PasswordMismatchException("The supplied old password for '" + user.getName() + "' was incorrect");
204         }
205         user.setPassword(newPassword);
206         // save the changes in the database immediately, to prevent the password
207         // being 'reverted' to the old value if the user data is lost somehow
208         // before it is saved at session's expiry.
209         saveUser(user);
210     }
211 
212     /**
213      * Forcibly sets new password for an User.
214      *
215      * This is supposed by the administrator to change the forgotten or
216      * compromised passwords. Certain implementatations of this feature would
217      * require administrative level access to the authenticating server /
218      * program.
219      *
220      * @param user
221      *            an User to change password for.
222      * @param password
223      *            the new password.
224      * @exception UnknownEntityException
225      *                if the user's record does not exist in the database.
226      * @exception DataBackendException
227      *                if there is a problem accessing the storage.
228      */
229     @Override
230 	public void forcePassword(User user, String password) throws UnknownEntityException, DataBackendException
231     {
232         if (!checkExists(user))
233         {
234             throw new UnknownEntityException("The account '" + user.getName() + "' does not exist");
235         }
236         user.setPassword(password);
237         // save the changes in the database immediately, to prevent the
238         // password being 'reverted' to the old value if the user data
239         // is lost somehow before it is saved at session's expiry.
240         saveUser(user);
241     }
242 
243     /**
244      * Construct a blank User object.
245      *
246      * This method calls getUserClass, and then creates a new object using the
247      * default constructor.
248      *
249      * @return an object implementing User interface.
250      * @throws DataBackendException
251      *             if the object could not be instantiated.
252      */
253     @Override
254 	public <T extends User> T getUserInstance() throws DataBackendException
255     {
256         try
257         {
258             @SuppressWarnings("unchecked")
259 			T user = (T) Class.forName(getClassName()).newInstance();
260             return user;
261         }
262         catch (Exception e)
263         {
264             throw new DataBackendException("Problem creating instance of class " + getClassName(), e);
265         }
266     }
267 
268     /**
269      * Construct a blank User object.
270      *
271      * This method calls getUserClass, and then creates a new object using the
272      * default constructor.
273      *
274      * @param userName
275      *            The name of the user.
276      *
277      * @return an object implementing User interface.
278      *
279      * @throws DataBackendException
280      *             if the object could not be instantiated.
281      */
282     @Override
283 	public <T extends User> T getUserInstance(String userName) throws DataBackendException
284     {
285         T user = getUserInstance();
286         user.setName(userName);
287         return user;
288     }
289 
290     /**
291      * Creates new user account with specified attributes.
292      *
293      * @param user
294      *            the object describing account to be created.
295      * @param password
296      *            The password to use for the account.
297      *
298      * @throws DataBackendException
299      *             if there was an error accessing the data backend.
300      * @throws EntityExistsException
301      *             if the user account already exists.
302      */
303     @Override
304 	public <T extends User> T addUser(T user, String password) throws DataBackendException, EntityExistsException
305     {
306         if (StringUtils.isEmpty(user.getName()))
307         {
308             throw new DataBackendException("Could not create " + "an user with empty name!");
309         }
310         if (checkExists(user))
311         {
312             throw new EntityExistsException("The account '" + user.getName() + "' already exists");
313         }
314         user.setPassword(password);
315         try
316         {
317             return persistNewUser(user);
318         }
319         catch (Exception e)
320         {
321             throw new DataBackendException("Failed to create account '" + user.getName() + "'", e);
322         }
323     }
324 
325     /**
326      * @return Returns the ACLFactory.
327      */
328     public ACLFactory getACLFactory()
329     {
330         if (aclFactory == null)
331         {
332             aclFactory = (ACLFactory) resolve(ACLFactory.ROLE);
333         }
334         return aclFactory;
335     }
336 
337 }