001package org.apache.fulcrum.security.spi; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021import org.apache.commons.lang3.StringUtils; 022import org.apache.fulcrum.security.UserManager; 023import org.apache.fulcrum.security.acl.AccessControlList; 024import org.apache.fulcrum.security.authenticator.Authenticator; 025import org.apache.fulcrum.security.entity.User; 026import org.apache.fulcrum.security.model.ACLFactory; 027import org.apache.fulcrum.security.util.DataBackendException; 028import org.apache.fulcrum.security.util.EntityExistsException; 029import org.apache.fulcrum.security.util.PasswordMismatchException; 030import org.apache.fulcrum.security.util.UnknownEntityException; 031 032/** 033 * This implementation keeps all objects in memory. This is mostly meant to help 034 * with testing and prototyping of ideas. 035 * 036 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> 037 * @version $Id$ 038 */ 039 040// TODO Need to load up Crypto component and actually encrypt passwords! 041 042public abstract class AbstractUserManager extends AbstractEntityManager implements UserManager 043{ 044 /** ID **/ 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * @param user user to persist 049 * @param <T> User type 050 * @return a User object 051 * @throws DataBackendException if fail to connect 052 */ 053 protected abstract <T extends User> T persistNewUser(T user) throws DataBackendException; 054 055 private ACLFactory aclFactory; 056 private Authenticator authenticator; 057 058 /* (non-Javadoc) 059 * @see org.apache.fulcrum.security.UserManager#getACL(org.apache.fulcrum.security.entity.User) 060 */ 061 @Override 062 public <T extends AccessControlList> T getACL(User user) throws UnknownEntityException 063 { 064 return getACLFactory().getAccessControlList(user); 065 } 066 067 /** 068 * Check whether a specified user's account exists. 069 * 070 * The login name is used for looking up the account. 071 * 072 * @param user 073 * The user to be checked. 074 * @return true if the specified account exists 075 * @throws DataBackendException 076 * if there was an error accessing the data backend. 077 */ 078 @Override 079 public boolean checkExists(User user) throws DataBackendException 080 { 081 return checkExists(user.getName()); 082 } 083 084 /** 085 * Retrieve a user from persistent storage using username as the key, and 086 * authenticate the user. The implementation may chose to authenticate to 087 * the server as the user whose data is being retrieved. 088 * 089 * @param userName 090 * the name of the user. 091 * @param password 092 * the user supplied password. 093 * @return an User object. 094 * @exception PasswordMismatchException 095 * if the supplied password was incorrect. 096 * @exception UnknownEntityException 097 * if the user's account does not exist in the database. 098 * @exception DataBackendException 099 * 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}