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 }