KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jetspeed > services > security > turbine > TurbineUserManagement


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.turbine;
18
19 import java.util.List JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Date JavaDoc;
22 import javax.servlet.ServletConfig JavaDoc;
23 import java.security.Principal JavaDoc;
24 import java.util.Vector JavaDoc;
25
26 // Torque
27
import org.apache.torque.util.Criteria;
28 import org.apache.torque.om.NumberKey;
29
30 // Turbine
31
import org.apache.turbine.services.TurbineBaseService;
32 import org.apache.turbine.services.TurbineServices;
33 import org.apache.turbine.services.InitializationException;
34 import org.apache.turbine.services.resources.ResourceService;
35
36 // Jetspeed Database OM
37
import org.apache.jetspeed.om.security.turbine.TurbineUser;
38 import org.apache.jetspeed.om.security.turbine.TurbineUserPeer;
39
40 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
41 import org.apache.jetspeed.services.logging.JetspeedLogger;
42 import org.apache.jetspeed.om.profile.Profile;
43
44 // Jetspeed Security
45
import org.apache.jetspeed.om.security.JetspeedUser;
46 import org.apache.jetspeed.om.security.BaseJetspeedUser;
47 import org.apache.jetspeed.om.security.UserNamePrincipal;
48 import org.apache.jetspeed.om.security.UserIdPrincipal;
49
50 import org.apache.jetspeed.services.JetspeedSecurity;
51 import org.apache.jetspeed.services.Profiler;
52 import org.apache.jetspeed.services.PsmlManager;
53 import org.apache.jetspeed.services.security.UserManagement;
54 import org.apache.jetspeed.services.security.JetspeedSecurityService;
55
56 import org.apache.jetspeed.services.security.CredentialsManagement;
57 import org.apache.jetspeed.services.security.UserException;
58 import org.apache.jetspeed.services.security.UnknownUserException;
59 import org.apache.jetspeed.services.security.NotUniqueUserException;
60 import org.apache.jetspeed.services.security.JetspeedSecurityException;
61 import org.apache.jetspeed.services.rundata.JetspeedRunDataService;
62 import org.apache.jetspeed.services.rundata.JetspeedRunData;
63 import org.apache.turbine.services.localization.Localization;
64 import org.apache.turbine.services.rundata.RunDataService;
65
66 // Password encryption
67
import javax.mail.internet.MimeUtility JavaDoc;
68 import java.security.MessageDigest JavaDoc;
69 import java.io.OutputStream JavaDoc;
70 import java.io.ByteArrayOutputStream JavaDoc;
71
72
73 /**
74  * Default Jetspeed-Turbine User Management implementation
75  *
76  *
77  * @author <a HREF="mailto:david@bluesunrise.com">David Sean Taylor</a>
78  * @author <a HREF="mailto:morciuch@apache.org">Mark Orciuch</a>
79  * @version $Id: TurbineUserManagement.java,v 1.13 2004/02/23 03:54:49 jford Exp $
80  */

81
82 public class TurbineUserManagement extends TurbineBaseService
83                                    implements UserManagement,
84                                               CredentialsManagement
85 {
86     /**
87      * Static initialization of the logger for this class
88      */

89     private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(TurbineUserManagement.class.getName());
90     
91     private final static String JavaDoc CONFIG_SECURE_PASSWORDS_KEY = "secure.passwords";
92     private final static String JavaDoc CONFIG_SECURE_PASSWORDS_ALGORITHM = "secure.passwords.algorithm";
93     private final static String JavaDoc CONFIG_SYSTEM_USERS = "system.users";
94
95     boolean securePasswords = false;
96     String JavaDoc passwordsAlgorithm = "SHA";
97     Vector JavaDoc systemUsers = null;
98
99     private final static String JavaDoc CONFIG_NEWUSER_ROLES = "newuser.roles";
100     private final static String JavaDoc [] DEFAULT_CONFIG_NEWUSER_ROLES =
101     { "user" };
102
103     String JavaDoc roles[] = null;
104
105     /** The JetspeedRunData Service. */
106     private JetspeedRunDataService runDataService = null;
107
108     ///////////////////////////////////////////////////////////////////////////
109
// User Management Interfaces
110
///////////////////////////////////////////////////////////////////////////
111

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

128     public JetspeedUser getUser(Principal JavaDoc principal)
129         throws JetspeedSecurityException
130     {
131         // TODO: check requestor for permission
132

133         Criteria criteria = new Criteria();
134         if (principal instanceof UserNamePrincipal)
135         {
136             criteria.add(TurbineUserPeer.LOGIN_NAME, principal.getName());
137         }
138         else if (principal instanceof UserIdPrincipal)
139         {
140             criteria.add(TurbineUserPeer.USER_ID, principal.getName());
141         }
142         else
143         {
144             throw new UserException("Invalid Principal Type in getUser: " + principal.getClass().getName());
145         }
146         List JavaDoc users;
147         try
148         {
149             users = TurbineUserPeer.doSelectUsers(criteria);
150         }
151         catch(Exception JavaDoc e)
152         {
153             String JavaDoc message = "Failed to retrieve user '" + principal.getName() + "'";
154             logger.error( message, e );
155             throw new UserException( message, e );
156         }
157         if ( users.size() > 1 )
158         {
159             throw new UserException(
160                 "Multiple Users with same username '" + principal.getName() + "'");
161         }
162         if ( users.size() == 1 )
163         {
164             return (JetspeedUser)users.get(0);
165         }
166         throw new UnknownUserException("Unknown user '" + principal.getName() + "'");
167
168     }
169
170   /**
171      * Retrieves a collection of all <code>JetspeedUser</code>s.
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     public Iterator JavaDoc getUsers()
180         throws JetspeedSecurityException
181     {
182         Criteria criteria = new Criteria();
183         List JavaDoc users;
184         try
185         {
186             users = TurbineUserPeer.doSelectUsers(criteria);
187         }
188         catch(Exception JavaDoc e)
189         {
190             logger.error( "Failed to retrieve users ", e );
191             throw new UserException("Failed to retrieve users ", e);
192         }
193         return users.iterator();
194     }
195
196     /**
197      * Retrieves a collection of <code>JetspeedUser</code>s filtered by a security
198      * provider-specific query string. For example SQL, OQL, JDOQL.
199      * The security service may optionally check the current user context
200      * to determine if the requestor has permission to perform this action.
201      *
202      * @return a collection of <code>JetspeedUser</code> entities.
203      * @exception UserException when the security provider has a general failure retrieving users.
204      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
205      */

206     public Iterator JavaDoc getUsers(String JavaDoc filter)
207         throws JetspeedSecurityException
208     {
209         // TODO: implement this with a SQL string
210

211         Criteria criteria = new Criteria();
212         List JavaDoc users;
213         try
214         {
215             users = TurbineUserPeer.doSelectUsers(criteria);
216         }
217         catch(Exception JavaDoc e)
218         {
219             logger.error( "Failed to retrieve users ", e );
220             throw new UserException("Failed to retrieve users ", e);
221         }
222         return users.iterator();
223     }
224
225     /**
226      * Saves a <code>JetspeedUser</code>'s attributes into permanent storage.
227      * The user's account is required to exist in the storage.
228      * The security service may optionally check the current user context
229      * to determine if the requestor has permission to perform this action.
230      *
231      * @exception UserException when the security provider has a general failure retrieving users.
232      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
233      */

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

268     public void addUser(JetspeedUser user)
269         throws JetspeedSecurityException
270     {
271         if(accountExists(user))
272         {
273             throw new NotUniqueUserException("The account '" +
274                 user.getUserName() + "' already exists");
275         }
276         String JavaDoc initialPassword = user.getPassword();
277         String JavaDoc encrypted = JetspeedSecurity.encryptPassword(initialPassword);
278         user.setPassword(encrypted);
279         Criteria criteria = TurbineUserPeer.buildCriteria(user);
280         try
281         {
282
283             NumberKey key = (NumberKey)TurbineUserPeer.doInsert(criteria);
284
285             ((BaseJetspeedUser)user).setUserId(key.toString());
286
287         }
288         catch(Exception JavaDoc e)
289         {
290             String JavaDoc message = "Failed to create account '" + user.getUserName() + "'";
291             logger.error( message, e );
292             throw new UserException( message, e );
293         }
294
295         addDefaultPSML(user);
296     }
297
298     /*
299      * A default PSML page is added for the user, and the Jetspeed default roles
300      * are assigned to the new user.
301      *
302      * @param user The new user.
303      * @throws
304      */

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

350     public void removeUser(Principal JavaDoc principal)
351         throws JetspeedSecurityException
352     {
353         if (systemUsers.contains(principal.getName()))
354         {
355             throw new UserException("[" + principal.getName() + "] is a system user and cannot be removed");
356         }
357
358         JetspeedUser user = getUser(principal);
359
360         Criteria criteria = new Criteria();
361         if (principal instanceof UserNamePrincipal)
362         {
363             criteria.add(TurbineUserPeer.LOGIN_NAME, principal.getName());
364         }
365         else if (principal instanceof UserIdPrincipal)
366         {
367             criteria.add(TurbineUserPeer.USER_ID, principal.getName());
368         }
369         else
370         {
371             throw new UserException("Invalid Principal Type in removeUser: " + principal.getClass().getName());
372         }
373
374         try
375         {
376             TurbineUserPeer.doDelete(criteria);
377             PsmlManager.removeUserDocuments(user);
378         }
379         catch(Exception JavaDoc e)
380         {
381             String JavaDoc message = "Failed to remove account '" + user.getUserName() + "'";
382             logger.error( message, e );
383             throw new UserException( message, e );
384         }
385
386     }
387
388
389     ///////////////////////////////////////////////////////////////////////////
390
// Credentials Management
391
///////////////////////////////////////////////////////////////////////////
392

393     /**
394      * Allows for a user to change their own password.
395      *
396      * @param user the JetspeedUser to change password
397      * @param oldPassword the current password supplied by the user.
398      * @param newPassword the current password requested by the user.
399      * @exception UserException when the security provider has a general failure retrieving a user.
400      * @exception UnknownUserException when the security provider cannot match
401      * the principal identity to a user.
402      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
403      */

404     public void changePassword( JetspeedUser user,
405                                 String JavaDoc oldPassword,
406                                 String JavaDoc newPassword )
407         throws JetspeedSecurityException
408     {
409         oldPassword = JetspeedSecurity.convertPassword(oldPassword);
410         newPassword = JetspeedSecurity.convertPassword(newPassword);
411
412         String JavaDoc encrypted = JetspeedSecurity.encryptPassword(oldPassword);
413         if(!accountExists(user))
414         {
415             throw new UnknownUserException(Localization.getString("UPDATEACCOUNT_NOUSER"));
416         }
417         if(!user.getPassword().equals(encrypted))
418         {
419             throw new UserException(Localization.getString("UPDATEACCOUNT_BADOLDPASSWORD"));
420         }
421         user.setPassword(JetspeedSecurity.encryptPassword(newPassword));
422
423         // Set the last password change date
424
user.setPasswordChanged(new Date JavaDoc());
425
426         // save the changes in the database immediately, to prevent the password
427
// being 'reverted' to the old value if the user data is lost somehow
428
// before it is saved at session's expiry.
429
saveUser(user);
430     }
431
432     /**
433      * Forcibly sets new password for a User.
434      *
435      * Provides an administrator the ability to change the forgotten or
436      * compromised passwords. Certain implementatations of this feature
437      * would require administrative level access to the authenticating
438      * server / program.
439      *
440      * @param user the user to change the password for.
441      * @param password the new password.
442      * @exception UserException when the security provider has a general failure retrieving a user.
443      * @exception UnknownUserException when the security provider cannot match
444      * the principal identity to a user.
445      * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege
446      */

447     public void forcePassword( JetspeedUser user, String JavaDoc password )
448         throws JetspeedSecurityException
449     {
450         if(!accountExists(user))
451         {
452             throw new UnknownUserException("The account '" +
453                 user.getUserName() + "' does not exist");
454         }
455         user.setPassword(JetspeedSecurity.encryptPassword(password));
456         // save the changes in the database immediately, to prevent the
457
// password being 'reverted' to the old value if the user data
458
// is lost somehow before it is saved at session's expiry.
459
saveUser(user);
460     }
461
462     /**
463      * This method provides client-side encryption of passwords.
464      *
465      * If <code>secure.passwords</code> are enabled in JetspeedSecurity properties,
466      * the password will be encrypted, if not, it will be returned unchanged.
467      * The <code>secure.passwords.algorithm</code> property can be used
468      * to chose which digest algorithm should be used for performing the
469      * encryption. <code>SHA</code> is used by default.
470      *
471      * @param password the password to process
472      * @return processed password
473      */

474     public String JavaDoc encryptPassword( String JavaDoc password )
475         throws JetspeedSecurityException
476     {
477         if (securePasswords == false)
478         {
479             return password;
480         }
481         if(password == null)
482         {
483             return null;
484         }
485
486         try
487         {
488             MessageDigest JavaDoc md = MessageDigest.getInstance(passwordsAlgorithm);
489             // We need to use unicode here, to be independent of platform's
490
// default encoding. Thanks to SGawin for spotting this.
491
byte[] digest = md.digest(password.getBytes("UTF-8"));
492             ByteArrayOutputStream JavaDoc bas = new ByteArrayOutputStream JavaDoc(digest.length + digest.length / 3 + 1);
493             OutputStream JavaDoc encodedStream = MimeUtility.encode(bas, "base64");
494             encodedStream.write(digest);
495             encodedStream.flush();
496             encodedStream.close();
497             return bas.toString();
498         }
499         catch (Exception JavaDoc e)
500         {
501             logger.error("Unable to encrypt password."+e.getMessage(), e);
502             return null;
503         }
504     }
505
506     ///////////////////////////////////////////////////////////////////////////
507
// Service Init
508
///////////////////////////////////////////////////////////////////////////
509

510
511     /**
512      * This is the early initialization method called by the
513      * Turbine <code>Service</code> framework
514      * @param conf The <code>ServletConfig</code>
515      * @exception throws a <code>InitializationException</code> if the service
516      * fails to initialize
517      */

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

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

569     protected boolean accountExists( JetspeedUser user )
570         throws UserException
571     {
572         return accountExists(user, false);
573     }
574
575     protected boolean accountExists( JetspeedUser user, boolean checkUniqueId )
576         throws UserException
577     {
578         String JavaDoc id = user.getUserId();
579         Criteria criteria = new Criteria();
580         criteria.add(TurbineUserPeer.LOGIN_NAME, user.getUserName());
581         List JavaDoc users;
582         try
583         {
584             users = TurbineUserPeer.doSelect(criteria);
585         }
586         catch(Exception JavaDoc e)
587         {
588             logger.error( "Failed to check account's presence", e );
589             throw new UserException(
590                 "Failed to check account's presence", e);
591         }
592         if (users.size() < 1)
593         {
594             return false;
595         }
596         TurbineUser retrieved = (TurbineUser)users.get(0);
597         int key = retrieved.getUserId();
598         String JavaDoc keyId = String.valueOf(key);
599         if (checkUniqueId && !keyId.equals(id))
600         {
601             throw new UserException("User exists but under a different unique ID");
602         }
603         return true;
604     }
605
606     protected JetspeedRunData getRunData()
607      {
608          JetspeedRunData rundata = null;
609          if (this.runDataService != null)
610          {
611              rundata = this.runDataService.getCurrentRunData();
612          }
613          return rundata;
614      }
615
616
617
618 }
619
620
Popular Tags