KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jetspeed > services > security > ldap > LDAPUserManagement


1 /*
2  * Copyright 2000-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.jetspeed.services.security.ldap;
18
19 import java.security.Principal JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Vector JavaDoc;
23 import javax.naming.NamingEnumeration JavaDoc;
24 import javax.naming.directory.Attributes JavaDoc;
25 import javax.naming.directory.BasicAttributes JavaDoc;
26 import javax.naming.directory.DirContext JavaDoc;
27 import javax.naming.directory.SearchResult JavaDoc;
28 import javax.servlet.ServletConfig JavaDoc;
29
30 // Jetspeed classes
31
import org.apache.jetspeed.om.profile.Profile;
32 import org.apache.jetspeed.om.security.JetspeedUser;
33 import org.apache.jetspeed.om.security.UserNamePrincipal;
34 import org.apache.jetspeed.om.security.ldap.LDAPUser;
35 import org.apache.jetspeed.services.JetspeedLDAP;
36 import org.apache.jetspeed.services.JetspeedSecurity;
37 import org.apache.jetspeed.services.Profiler;
38 import org.apache.jetspeed.services.PsmlManager;
39 import org.apache.jetspeed.services.ldap.LDAPURL;
40 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
41 import org.apache.jetspeed.services.logging.JetspeedLogger;
42 import org.apache.jetspeed.services.rundata.JetspeedRunData;
43 import org.apache.jetspeed.services.rundata.JetspeedRunDataService;
44 import org.apache.jetspeed.services.security.CredentialsManagement;
45 import org.apache.jetspeed.services.security.JetspeedSecurityException;
46 import org.apache.jetspeed.services.security.JetspeedSecurityService;
47 import org.apache.jetspeed.services.security.NotUniqueUserException;
48 import org.apache.jetspeed.services.security.UnknownUserException;
49 import org.apache.jetspeed.services.security.UserException;
50 import org.apache.jetspeed.services.security.UserManagement;
51
52 // Turbine classes
53
import org.apache.turbine.services.InitializationException;
54 import org.apache.turbine.services.TurbineBaseService;
55 import org.apache.turbine.services.TurbineServices;
56 import org.apache.turbine.services.resources.ResourceService;
57 import org.apache.turbine.services.rundata.RunDataService;
58
59 /**
60  *
61  * @author <a HREF="mailto:ender@kilicoglu.nom.tr">Ender KILICOGLU</a>
62  * @author <a HREF="mailto:sami.leino@netorek.fi">Sami Leino</a>
63  *
64  * @version $Id: LDAPUserManagement.java,v 1.10 2004/02/23 03:52:33 jford Exp $
65  *
66  */

67 public class LDAPUserManagement extends TurbineBaseService
68                                 implements UserManagement,
69                                            CredentialsManagement
70 {
71     /**
72      * Static initialization of the logger for this class
73      */

74     private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(LDAPUserManagement.class.getName());
75     
76     // Constants
77
private final static String JavaDoc CONFIG_SECURE_PASSWORDS_KEY = "secure.passwords";
78     private final static String JavaDoc CONFIG_SECURE_PASSWORDS_ALGORITHM = "secure.passwords.algorithm";
79     private final static String JavaDoc CONFIG_SECURE_PASSWORDS_SUFFIX = "secure.passwords.suffix";
80     private final static String JavaDoc CONFIG_NEWUSER_ROLES = "newuser.roles";
81     private final static String JavaDoc[] DEFAULT_CONFIG_NEWUSER_ROLES = { "user" };
82
83     private final static String JavaDoc[] ATTRS = { "ou", "userPassword", "uid", "mail", "sn", "givenName",
84                                             "uidNumber", "name", "objectdata", "objectClass",
85                                             "usergrouprole", "lastlogindate", "lastmodifieddate",
86                                             "creationdate", "confirm", "disabled" };
87     
88     // Class variables
89
protected static boolean securePasswords = false;
90     protected static String JavaDoc passwordsAlgorithm = "crypt";
91     protected static String JavaDoc passwordsSuffix = "{crypt}";
92
93     // Instance variables
94
protected JetspeedRunDataService runDataService = null;
95     protected String JavaDoc roles[] = null;
96
97     ///////////////////////////////////////////////////////////////////////////
98
// User Management Interfaces
99

100     ///////////////////////////////////////////////////////////////////////////
101

102     /**
103      * Retrieves a <code>JetspeedUser</code> given the primary principle.
104      * The principal can be any valid Jetspeed Security Principal:
105      * <code>org.apache.jetspeed.om.security.UserNamePrincipal</code>
106      * <code>org.apache.jetspeed.om.security.UserIdPrincipal</code>
107      *
108      * The security service may optionally check the current user context
109      * to determine if the requestor has permission to perform this action.
110      *
111      * @param principal a principal identity to be retrieved.
112      *
113      * @return a <code>JetspeedUser</code> associated to the principal identity.
114      * @exception UserException when the security provider has a general failure retrieving a user.
115      * @exception UnknownUserException when the security provider cannot match
116      * the principal identity to a user.
117      * @exception InsufficientPrivilegeException when the requestor is denied
118      * due to insufficient privilege
119      */

120     public JetspeedUser getUser(Principal JavaDoc principal)
121         throws JetspeedSecurityException
122     {
123         BasicAttributes JavaDoc attr = new BasicAttributes JavaDoc();
124         Vector JavaDoc userurls = new Vector JavaDoc();
125         LDAPUser user = null;
126
127         try
128         {
129             userurls = JetspeedLDAP.search(JetspeedLDAP.buildURL("ou=users"),
130                        "(&(uid="+principal.getName()+")(objectclass=jetspeeduser))", ATTRS, true);
131         }
132         catch (Exception JavaDoc e)
133         {
134             logger.error( "Failed to retrieve user '" + principal.getName() + "'", e );
135             throw new UserException("Failed to retrieve user '" + principal.getName() + "'", e);
136         }
137          
138         if (userurls.size() == 1)
139         {
140             user = new LDAPUser((LDAPURL) ((Vector JavaDoc)userurls.elementAt(0)).firstElement());
141             return user;
142         }
143         else if(userurls.size() > 1)
144         {
145             throw new UserException("Multiple Users with same username '" + principal.getName() + "'");
146         }
147         else
148         {
149             throw new UnknownUserException("Unknown user '" + principal.getName() + "'");
150         }
151     }
152
153     /**
154      * Retrieves a collection of all <code>JetspeedUser</code>s.
155      * The security service may optionally check the current user context
156      * to determine if the requestor has permission to perform this action.
157      *
158      * @return a collection of <code>JetspeedUser</code> entities.
159      * @exception UserException when the security provider has a general failure retrieving users.
160      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
161      */

162     public Iterator JavaDoc getUsers()
163         throws JetspeedSecurityException
164     {
165         String JavaDoc filter = "(objectclass=jetspeeduser)";
166         return getUsersUsingLDAPSpecificFilter(filter, null);
167     }
168
169     /**
170      * Retrieves a collection of <code>JetspeedUser</code>s filtered by a security
171      * provider-specific query string. For example SQL, OQL, JDOQL.
172      * The security service may optionally check the current user context
173      * to determine if the requestor has permission to perform this action.
174      *
175      * @return a collection of <code>JetspeedUser</code> entities.
176      * @exception UserException when the security provider has a general failure retrieving users.
177      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
178      *
179      */

180     public Iterator JavaDoc getUsers(String JavaDoc filter)
181         throws JetspeedSecurityException
182     {
183         // String ldapFilter = convert(filter);
184
return getUsersUsingLDAPSpecificFilter(filter, null);
185     }
186
187     /**
188      * Retrieves a collection of <code>JetspeedUser</code>s filtered by a security
189      * provider-specific query string. For example SQL, OQL, JDOQL.
190      * The security service may optionally check the current user context
191      * to determine if the requestor has permission to perform this action.
192      *
193      * @return a collection of <code>JetspeedUser</code> entities.
194      * @exception UserException when the security provider has a general failure retrieving users.
195      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
196      */

197     protected Iterator JavaDoc getUsersUsingLDAPSpecificFilter(String JavaDoc filter, String JavaDoc[] attributesToFetch)
198         throws JetspeedSecurityException
199     {
200         String JavaDoc baseDN = "ou=users";
201         NamingEnumeration JavaDoc userEnum = null;
202         List JavaDoc resultList = new Vector JavaDoc(1024);
203
204         try
205         {
206             LDAPURL url = JetspeedLDAP.buildURL( baseDN );
207             DirContext JavaDoc ctx = JetspeedLDAP.getService().connect(url);
208             userEnum = JetspeedLDAP.search(ctx, url.getDN(), filter, attributesToFetch, JetspeedLDAP.getService().SUB);
209
210             while (userEnum.hasMoreElements())
211             {
212                 LDAPUser user = buildUser(((SearchResult JavaDoc)userEnum.nextElement()).getAttributes());
213                 resultList.add( user );
214             }
215
216             JetspeedLDAP.getService().checkAndCloseContext(ctx);
217         }
218         catch ( Exception JavaDoc e )
219         {
220             logger.error( "Failed to retrieve user with filter:" + filter, e );
221             throw new UserException( "Failed to retrieve user with filter:" + filter, e );
222         }
223
224         return ( resultList.iterator() );
225     }
226
227     protected LDAPUser buildUser(Attributes JavaDoc attributes)
228     {
229         return new LDAPUser(attributes);
230     }
231     
232     /**
233      * Saves a <code>JetspeedUser</code>'s attributes into permanent storage.
234      * The user's account is required to exist in the storage.
235      * The security service may optionally check the current user context
236      * to determine if the requestor has permission to perform this action.
237      *
238      * @exception UserException when the security provider has a general failure retrieving users.
239      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
240      */

241     public void saveUser(JetspeedUser user)
242         throws JetspeedSecurityException
243     {
244         if(!accountExists(user, true))
245         {
246             throw new UnknownUserException("Cannot save user '" + user.getUserName() +
247                                            "', User doesn't exist");
248         }
249         try
250         {
251             ((LDAPUser)user).update(false);
252         }
253         catch(Exception JavaDoc e)
254         {
255             logger.error( "Failed to save user object ", e);
256             throw new UserException("Failed to save user object ", e);
257         }
258     }
259
260     /**
261      * Adds a <code>JetspeedUser</code> into permanent storage.
262      * The security service can throw a <code>NotUniqueUserException</code> when the public
263      * credentials fail to meet the security provider-specific unique constraints.
264      * The security service may optionally check the current user context
265      * to determine if the requestor has permission to perform this action.
266      *
267      * @exception UserException when the security provider has a general failure retrieving users.
268      * @exception NotUniqueUserException when the public credentials fail to meet
269      * the security provider-specific unique constraints.
270      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
271      */

272     public void addUser(JetspeedUser user)
273         throws JetspeedSecurityException
274     {
275         if(accountExists(user))
276         {
277             throw new NotUniqueUserException("The account '" +
278                 user.getUserName() + "' already exists");
279         }
280
281         String JavaDoc initialPassword = user.getPassword();
282         String JavaDoc encrypted = JetspeedSecurity.encryptPassword(initialPassword);
283         user.setPassword(encrypted);
284         ((LDAPUser)user).update(true);
285
286         addDefaultPSML(user);
287     }
288
289     /*
290      * A default PSML page is added for the user, and the Jetspeed default roles
291      * are assigned to the new user.
292      *
293      * @param user The new user.
294      * @throws
295      */

296     protected void addDefaultPSML(JetspeedUser user)
297         throws JetspeedSecurityException
298     {
299         for (int ix = 0; ix < roles.length; ix++)
300         {
301             try
302             {
303
304                 JetspeedSecurity.grantRole(user.getUserName(),
305                       JetspeedSecurity.getRole(roles[ix]).getName());
306             }
307             catch(Exception JavaDoc e)
308             {
309                 logger.error("Could not grant role: " + roles[ix] + " to user " + user.getUserName(), e);
310             }
311         }
312         try
313         {
314             JetspeedRunData rundata = getRunData();
315             if (rundata != null)
316             {
317                 Profile profile = Profiler.createProfile();
318                 profile.setUser(user);
319                 profile.setMediaType("html");
320                 Profiler.createProfile(getRunData(), profile);
321             }
322         }
323         catch (Exception JavaDoc e)
324         {
325             logger.error( "Failed to create profile for new user ", e );
326             removeUser(new UserNamePrincipal(user.getUserName()));
327             throw new UserException("Failed to create profile for new user ", e);
328         }
329     }
330
331     /**
332      * Removes a <code>JetspeedUser</code> from the permanent store.
333      * The security service may optionally check the current user context
334      * to determine if the requestor has permission to perform this action.
335      *
336      * @param principal the principal identity to be retrieved.
337      * @exception UserException when the security provider has a general failure retrieving a user.
338      * @exception UnknownUserException when the security provider cannot match
339      * the principal identity to a user.
340      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
341      */

342     public void removeUser(Principal JavaDoc principal)
343         throws JetspeedSecurityException
344     {
345         BasicAttributes JavaDoc attr= new BasicAttributes JavaDoc();
346         Vector JavaDoc userurls = new Vector JavaDoc();
347         LDAPUser user = (LDAPUser)getUser(principal);
348
349         try
350         {
351             JetspeedLDAP.deleteEntry(user.getldapurl());
352             PsmlManager.removeUserDocuments(user);
353         }
354         catch(Exception JavaDoc e)
355         {
356             logger.error( "Failed to remove account '" + user.getUserName() + "'", e );
357             throw new UserException("Failed to remove account '" +
358                 user.getUserName() + "'", e);
359         }
360
361     }
362
363     ///////////////////////////////////////////////////////////////////////////
364
// Credentials Management
365
///////////////////////////////////////////////////////////////////////////
366

367     /**
368      * Allows for a user to change their own password.
369      *
370      * @param user the JetspeedUser to change password
371      * @param oldPassword the current password supplied by the user.
372      * @param newPassword the current password requested by the user.
373      * @exception UserException when the security provider has a general failure retrieving a user.
374      * @exception UnknownUserException when the security provider cannot match
375      * the principal identity to a user.
376      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
377      */

378     public void changePassword( JetspeedUser user,
379                                 String JavaDoc oldPassword,
380                                 String JavaDoc newPassword )
381         throws JetspeedSecurityException
382     {
383         oldPassword = JetspeedSecurity.convertPassword(oldPassword);
384         newPassword = JetspeedSecurity.convertPassword(newPassword);
385
386         if(!accountExists(user))
387         {
388             throw new UnknownUserException("The account '" +
389                 user.getUserName() + "' does not exist");
390         }
391         else if (!passwordsMatch(user, oldPassword))
392         {
393             throw new UserException(
394                "The supplied old password for '" + user.getUserName() +
395                 "' was incorrect");
396         }
397
398         String JavaDoc encrypted = JetspeedSecurity.encryptPassword( newPassword );
399         user.setPassword(encrypted);
400
401         // save the changes in the database immediately, to prevent the password
402
// being 'reverted' to the old value if the user data is lost somehow
403
// before it is saved at session's expiry.
404
saveUser(user);
405     }
406
407     /**
408      * Forcibly sets new password for a User.
409      *
410      * Provides an administrator the ability to change the forgotten or
411      * compromised passwords. Certain implementatations of this feature
412      * would require administrative level access to the authenticating
413      * server / program.
414      *
415      * @param user the user to change the password for.
416      * @param password the new password.
417      *
418      * @exception UserException when the security provider has a general
419      * failure retrieving a user.
420      * @exception UnknownUserException when the security provider cannot match
421      * the principal identity to a user.
422      * @exception InsufficientPrivilegeException when the requestor is
423      * denied due to insufficient privilege
424      */

425     public void forcePassword( JetspeedUser user, String JavaDoc password )
426         throws JetspeedSecurityException
427     {
428         if(!accountExists(user))
429         {
430             throw new UnknownUserException("The account '" +
431                 user.getUserName() + "' does not exist");
432         }
433
434         String JavaDoc encrypted = JetspeedSecurity.encryptPassword( password );
435         user.setPassword(encrypted);
436
437
438         // save the changes in the database immediately, to prevent the
439
// password being 'reverted' to the old value if the user data
440
// is lost somehow before it is saved at session's expiry.
441
saveUser(user);
442     }
443
444     /**
445      * This method provides client-side encryption of passwords.
446      *
447      * If <code>secure.passwords</code> are enabled in
448      * JetspeedSecurity.properties,
449      * the password will be encrypted, if not, it will be returned unchanged.
450      * The <code>secure.passwords.algorithm</code> property can be used
451      * to chose which digest algorithm should be used for performing the
452      * encryption. <code>SHA</code> is used by default.
453      *
454      * @param password the password to process
455      *
456      * @return processed password
457      *
458      */

459     public String JavaDoc encryptPassword( String JavaDoc password )
460         throws JetspeedSecurityException
461     {
462         if (securePasswords == false)
463         {
464             return password;
465         }
466         else if(password == null)
467         {
468             return null;
469         }
470         else if(password.startsWith(passwordsSuffix))
471         {
472             // A kludge
473
return password;
474         }
475  
476         return passwordsSuffix + UnixCrypt.crypt(password);
477     }
478
479     /**
480      * <p>Check's if user's current password matches with the
481      * supplied password.</p>
482      *
483      * @param user User whose password will be checked
484      * @param suppliedPassword Password to match
485      *
486      * @return True if passwords match.
487      *
488      */

489     public static boolean passwordsMatch(JetspeedUser user, String JavaDoc suppliedPassword)
490     {
491         if (securePasswords == false)
492         {
493             return user.getPassword().equals(suppliedPassword);
494         }
495         else
496         {
497             return UnixCrypt.matches(user.getPassword().substring(passwordsSuffix.length()), suppliedPassword);
498         }
499     }
500
501     ///////////////////////////////////////////////////////////////////////////
502
// Service Init
503
///////////////////////////////////////////////////////////////////////////
504

505     /**
506      * This is the early initialization method called by the
507      * Turbine <code>Service</code> framework
508      * @param conf The <code>ServletConfig</code>
509      * @exception throws a <code>InitializationException</code> if the service
510      * fails to initialize
511      */

512     public synchronized void init(ServletConfig JavaDoc conf)
513         throws InitializationException
514     {
515         if (getInit()) return;
516
517         super.init(conf);
518
519         // get configuration parameters from Jetspeed Resources
520
ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
521                                                      .getResources(JetspeedSecurityService.SERVICE_NAME);
522
523         securePasswords = serviceConf.getBoolean(CONFIG_SECURE_PASSWORDS_KEY,
524                                                   securePasswords);
525         passwordsAlgorithm = serviceConf.getString(CONFIG_SECURE_PASSWORDS_ALGORITHM,
526                                                    passwordsAlgorithm);
527         passwordsSuffix = serviceConf.getString(CONFIG_SECURE_PASSWORDS_SUFFIX,
528                                                    passwordsSuffix);
529
530         try
531         {
532             roles = serviceConf.getStringArray(CONFIG_NEWUSER_ROLES);
533         }
534         catch (Exception JavaDoc e)
535         {
536         }
537
538         if (null == roles || roles.length == 0)
539         {
540             roles = DEFAULT_CONFIG_NEWUSER_ROLES;
541         }
542
543         this.runDataService = (JetspeedRunDataService)TurbineServices.getInstance()
544                .getService(RunDataService.SERVICE_NAME);
545
546         setInit(true);
547      }
548
549     ///////////////////////////////////////////////////////////////////////////
550
// Internal
551
///////////////////////////////////////////////////////////////////////////
552

553     /**
554      * Check whether a specified user's account exists.
555      *
556      * The login name is used for looking up the account.
557      *
558      * @param user The user to be checked.
559      * @param checkUniqueId Make sure that we aren't
560      * overwriting another user
561      * with different id.
562      *
563      * @return true If the specified account exists
564      *
565      * @throws UserException If there was a general db access error
566      *
567      */

568     protected boolean accountExists( JetspeedUser user )
569         throws UserException
570     {
571         return accountExists(user, false);
572     }
573
574     protected boolean accountExists( JetspeedUser user, boolean checkUniqueId )
575         throws UserException
576     {
577         UserNamePrincipal principal = new UserNamePrincipal(user.getUserName());
578
579         try
580         {
581             getUser(principal);
582             return true;
583         }
584         catch (Exception JavaDoc e)
585         {
586             return false;
587         }
588     }
589
590     protected JetspeedRunData getRunData()
591     {
592         JetspeedRunData rundata = null;
593
594         if (this.runDataService != null)
595         {
596             rundata = this.runDataService.getCurrentRunData();
597         }
598
599         return rundata;
600     }
601
602 }
603
Popular Tags