View Javadoc
1 package org.apache.turbine.services.security.ldap; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>;. 55 */ 56 57 import java.util.Hashtable; 58 import java.util.StringTokenizer; 59 import java.util.Vector; 60 import javax.naming.Context; 61 import javax.naming.NamingEnumeration; 62 import javax.naming.NamingException; 63 import javax.naming.directory.Attributes; 64 import javax.naming.directory.DirContext; 65 import javax.naming.directory.SearchControls; 66 import javax.naming.directory.SearchResult; 67 import org.apache.torque.util.Criteria; 68 import org.apache.turbine.om.security.User; 69 import org.apache.turbine.services.resources.TurbineResources; 70 import org.apache.turbine.services.security.UserManager; 71 import org.apache.turbine.services.security.ldap.util.ParseExceptionMessage; 72 import org.apache.turbine.util.Log; 73 import org.apache.turbine.util.security.DataBackendException; 74 import org.apache.turbine.util.security.EntityExistsException; 75 import org.apache.turbine.util.security.PasswordMismatchException; 76 import org.apache.turbine.util.security.UnknownEntityException; 77 78 /*** 79 * A UserManager performs {@link org.apache.turbine.om.security.User} 80 * object related tasks on behalf of the 81 * {@link org.apache.turbine.services.security.SecurityService}. 82 * 83 * This implementation uses ldap for retrieving user data. It 84 * expects that the User interface implementation will be castable to 85 * {@link org.apache.turbine.om.BaseObject}. 86 * 87 * @author <a href="mailto:jon@collab.net">Jon S. Stevens</a> 88 * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a> 89 * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a> 90 * @author <a href="mailto:cberry@gluecode.com">Craig D. Berry</a> 91 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a> 92 * @author <a href="mailto:tadewunmi@gluecode.com">Tracy M. Adewunmi</a> 93 * @author <a href="mailto:lflournoy@gluecode.com">Leonard J. Flournoy</a> 94 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> 95 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 96 * @version $Id: LDAPUserManager.java,v 1.3 2002/07/11 16:53:24 mpoeschl Exp $ 97 */ 98 public class LDAPUserManager implements UserManager, LDAPSecurityConstants 99 { 100 /*** 101 * Check wether a specified user's account exists. 102 * 103 * The login name is used for looking up the account. 104 * 105 * @param user The user to be checked. 106 * @return true if the specified account exists 107 * @throws DataBackendException Error accessing the data backend. 108 */ 109 public boolean accountExists(User user) throws DataBackendException 110 { 111 return accountExists(user.getUserName()); 112 } 113 114 /*** 115 * 116 * Check wether a specified user's account exists. 117 * The login name is used for looking up the account. 118 * 119 * @param usename The name of the user to be checked. 120 * @return true if the specified account exists 121 * @throws DataBackendException Error accessing the data backend. 122 */ 123 public boolean accountExists(String username) 124 throws DataBackendException 125 { 126 /*! 127 * Is it possible to retrieve more then one user? 128 * Possibly the check for multiple users that is 129 * employed in the DBUserManager can be employed 130 * here. 131 */ 132 133 try 134 { 135 User ldapUser = retrieve(username); 136 } 137 catch (Exception e) 138 { 139 throw new DataBackendException( 140 "Failed to check account's presence", e); 141 } 142 143 return true; 144 } 145 146 /*** 147 * Retrieve a user from persistent storage using username as the 148 * key. 149 * 150 * @param username the name of the user. 151 * @return an User object. 152 * @exception UnknownEntityException if the user's account does not 153 * exist in the database. 154 * @exception DataBackendException Error accessing the data backend. 155 */ 156 public User retrieve(String username) 157 throws UnknownEntityException, DataBackendException 158 { 159 String dNAttribute = TurbineResources.getString(LDAP_DN_ATTR); 160 String filter = TurbineResources.getString(LDAP_SEARCH_FLTR); 161 String userBaseSearch = TurbineResources.getString(LDAP_BASE_SERACH); 162 String dN = null; 163 String adminUser = TurbineResources.getString(LDAP_ADMIN_USRNAME); 164 String adminPassword = TurbineResources.getString(LDAP_ADMIN_PASSWRD); 165 166 adminUser = adminUser.replace('/', '='); 167 adminUser = adminUser.replace('%', ','); 168 169 User ldapUser = null; 170 171 /* 172 * The userBaseSearch string contains some 173 * characters that need to be transformed. 174 */ 175 userBaseSearch = userBaseSearch.replace('/', '='); 176 userBaseSearch = userBaseSearch.replace('%', ','); 177 178 try 179 { 180 DirContext ctx = bind(adminUser, adminPassword); 181 182 /* 183 * Create the default search controls. 184 */ 185 SearchControls ctls = new SearchControls(); 186 187 /* 188 * Create filter. 189 */ 190 filter = "("+filter + "="+username + ")"; 191 192 NamingEnumeration answer = 193 ctx.search(userBaseSearch, filter, ctls); 194 195 while (answer.hasMore()) 196 { 197 SearchResult sr = (SearchResult) answer.next(); 198 Attributes attribs = sr.getAttributes(); 199 Log.debug("attribs: " + attribs.get(dNAttribute)); 200 dN = attribs.get(dNAttribute).toString(); 201 Log.debug("dN: " + dN); 202 } 203 204 if (dN == null) 205 { 206 throw new UnknownEntityException("The given user: " + 207 username + "\n does not exist."); 208 } 209 210 StringTokenizer sT = new StringTokenizer(dN, ":"); 211 212 while (sT.hasMoreElements()) 213 { 214 dN = sT.nextToken(); 215 } 216 217 dN = dN.trim(); 218 219 ldapUser = new LDAPUser(); 220 ldapUser.setUserName(dN); 221 222 ldapUser.setTemp("turbine.user", ldapUser); 223 224 } 225 catch (NamingException nameEx) 226 { 227 throw new DataBackendException( 228 "The LDAP server specified is unavailable"); 229 } 230 return ldapUser; 231 } 232 233 /*** 234 * This is currently not implemented to behave as expected. It is 235 * just here to support the interface requirement. 236 * 237 * Retrieve a set of users that meet the specified criteria. 238 * 239 * As the keys for the criteria, you should use the constants that 240 * are defined in {@link User} interface, plus the the names 241 * of the custom attributes you added to your user representation 242 * in the data storage. Use verbatim names of the attributes - 243 * without table name prefix in case of DB implementation. 244 * 245 * @param criteria The criteria of selection. 246 * @return a List of users meeting the criteria. 247 * @throws DataBackendException Error accessing the data backend. 248 */ 249 public User[] retrieve(Criteria criteria) throws DataBackendException 250 { 251 Vector users = new Vector(0); 252 return (User[]) users.toArray(new User[0]); 253 } 254 255 /*** 256 * Retrieve a user from persistent storage using username as the 257 * key, and authenticate the user. The implementation may chose 258 * to authenticate to the server as the user whose data is being 259 * retrieved. 260 * 261 * @param username the name of the user. 262 * @param password the user supplied password. 263 * @return an User object. 264 * @exception PasswordMismatchException if the supplied password was 265 * incorrect. 266 * @exception UnknownEntityException if the user's account does not 267 * exist in the database. 268 * @exception DataBackendException Error accessing the data backend. 269 */ 270 public User retrieve(String username, String password) 271 throws PasswordMismatchException, 272 UnknownEntityException,DataBackendException 273 { 274 275 User user = retrieve(username); 276 authenticate(user, password); 277 return user; 278 } 279 280 /*** 281 * This method is not yet implemented 282 * 283 * Save a User object to persistent storage. User's account is 284 * required to exist in the storage. 285 * 286 * @param user an User object to store. 287 * @exception UnknownEntityException if the user's account does not 288 * exist in the database. 289 * 290 */ 291 public void store(User user) 292 throws UnknownEntityException,DataBackendException 293 { 294 if (!accountExists(user)) 295 { 296 throw new UnknownEntityException("The account '"+ 297 user.getUserName() + "' does not exist"); 298 } 299 } 300 301 /*** 302 * Authenticate a User with the specified password. If authentication 303 * is successful the method returns nothing. If there are any problems, 304 * exception was thrown. 305 * 306 * @param user a User object to authenticate. 307 * @param password the user supplied password. 308 * @exception PasswordMismatchException if the supplied password was 309 * incorrect. 310 * @exception UnknownEntityException if the user's account does not 311 * exist in the database. 312 * @exception DataBackendException Error accessing the data backend. 313 */ 314 public void authenticate(User user, String password) 315 throws PasswordMismatchException, 316 UnknownEntityException, DataBackendException 317 { 318 try 319 { 320 bind(user.getUserName(), password); 321 } 322 catch (NamingException authEx) 323 { 324 throw new PasswordMismatchException( 325 "The given password for: " + 326 user.getUserName() + " is invalid\n"); 327 } 328 } 329 330 /*** 331 * This method is not yet implemented 332 * Change the password for an User. 333 * 334 * @param user an User to change password for. 335 * @param password the new password. 336 * @exception PasswordMismatchException if the supplied password was 337 * incorrect. 338 * @exception UnknownEntityException if the user's account does not 339 * exist in the database. 340 * @exception DataBackendException Error accessing the data backend. 341 */ 342 public void changePassword(User user, String oldPassword, String newPassword) 343 throws PasswordMismatchException, 344 UnknownEntityException, DataBackendException 345 { 346 throw new DataBackendException( 347 "The method changePassword has no implementation."); 348 } 349 350 /*** 351 * This method is not yet implemented 352 * Forcibly sets new password for an User. 353 * 354 * This is supposed to be used by the administrator to change the forgotten or 355 * compromised passwords. Certain implementatations of this feature 356 * would require adminstrative level access to the authenticating 357 * server / program. 358 * 359 * @param user an User to change password for. 360 * @param password the new password. 361 * @exception UnknownEntityException if the user's record does not 362 * exist in the database. 363 * @exception DataBackendException Error accessing the data backend. 364 */ 365 public void forcePassword(User user, String password) 366 throws UnknownEntityException,DataBackendException 367 { 368 throw new DataBackendException( 369 "The method forcePassword has no implementation."); 370 } 371 372 /*** 373 * This method is not yet implemented 374 * Creates new user account with specified attributes. 375 * 376 * @param user the object describing account to be created. 377 * @throws DataBackendException Error accessing the data backend. 378 * @throws EntityExistsException if the user account already exists. 379 */ 380 public void createAccount(User user, String initialPassword) 381 throws EntityExistsException,DataBackendException 382 { 383 throw new DataBackendException( 384 "The method createAccount has no implementation."); 385 } 386 387 /*** 388 * This method is not yet implemented 389 * Removes an user account from the system. 390 * 391 * @param user the object describing the account to be removed. 392 * @throws DataBackendException Error accessing the data backend. 393 * @throws UnknownEntityException if the user account is not present. 394 */ 395 public void removeAccount(User user) 396 throws UnknownEntityException,DataBackendException 397 { 398 throw new DataBackendException( 399 "The method removeAccount has no implementation."); 400 } 401 402 403 /*** 404 * Creats an initial context. 405 * 406 * @param ldap admin username supplied in TRP. 407 * @param ldap admin password supplied in TRP 408 * @throws DataBackendException Error accessing the data backend. 409 * @throws UnknownEntityException if the user account is not present. 410 * @throws NamingException when an error occurs with the named server. 411 */ 412 public DirContext bind(String username, String password) 413 throws NamingException, DataBackendException, UnknownEntityException 414 { 415 DirContext ctx = null; 416 417 try 418 { 419 String host = TurbineResources.getString(LDAP_HOST); 420 String port = TurbineResources.getString(LDAP_PORT); 421 422 String providerURL = new String("ldap://" + host + ":" + port); 423 424 /* 425 * creating an initial context using Sun's client 426 * LDAP Provider. 427 */ 428 Hashtable env = new Hashtable(); 429 env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_PROVIDER); 430 431 env.put(Context.PROVIDER_URL, providerURL); 432 env.put(Context.SECURITY_AUTHENTICATION, "simple"); 433 env.put(Context.SECURITY_PRINCIPAL, username); 434 env.put(Context.SECURITY_CREDENTIALS, password); 435 436 ctx = new javax.naming.directory.InitialDirContext(env); 437 Log.debug("CTX: " + ctx.toString()); 438 } 439 catch (NamingException ne) 440 { 441 String errno = ParseExceptionMessage.findErrno(ne.getExplanation()); 442 443 if (errno.equals("49")) 444 { 445 throw new UnknownEntityException( 446 "The given credentials for the administrator are invalid"); 447 } 448 else if (errno.equals("22")) 449 { 450 throw new DataBackendException( 451 "The LDAP server specified is unavailable"); 452 } 453 else 454 { 455 throw ne; 456 } 457 } 458 catch (Exception e) 459 { 460 Log.error(e); 461 } 462 463 return ctx; 464 } 465 }

This page was automatically generated by Maven