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 at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * 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 and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.services.security.turbine;
1819import java.util.List;
20import java.util.Iterator;
21import java.util.Date;
22import javax.servlet.ServletConfig;
23import java.security.Principal;
24import java.util.Vector;
2526// Torque27import org.apache.torque.util.Criteria;
28import org.apache.torque.om.NumberKey;
2930// Turbine31import org.apache.turbine.services.TurbineBaseService;
32import org.apache.turbine.services.TurbineServices;
33import org.apache.turbine.services.InitializationException;
34import org.apache.turbine.services.resources.ResourceService;
3536// Jetspeed Database OM37import org.apache.jetspeed.om.security.turbine.TurbineUser;
38import org.apache.jetspeed.om.security.turbine.TurbineUserPeer;
3940import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
41import org.apache.jetspeed.services.logging.JetspeedLogger;
42import org.apache.jetspeed.om.profile.Profile;
4344// Jetspeed Security45import org.apache.jetspeed.om.security.JetspeedUser;
46import org.apache.jetspeed.om.security.BaseJetspeedUser;
47import org.apache.jetspeed.om.security.UserNamePrincipal;
48import org.apache.jetspeed.om.security.UserIdPrincipal;
4950import org.apache.jetspeed.services.JetspeedSecurity;
51import org.apache.jetspeed.services.Profiler;
52import org.apache.jetspeed.services.PsmlManager;
53import org.apache.jetspeed.services.security.UserManagement;
54import org.apache.jetspeed.services.security.JetspeedSecurityService;
5556import org.apache.jetspeed.services.security.CredentialsManagement;
57import org.apache.jetspeed.services.security.UserException;
58import org.apache.jetspeed.services.security.UnknownUserException;
59import org.apache.jetspeed.services.security.NotUniqueUserException;
60import org.apache.jetspeed.services.security.JetspeedSecurityException;
61import org.apache.jetspeed.services.rundata.JetspeedRunDataService;
62import org.apache.jetspeed.services.rundata.JetspeedRunData;
63import org.apache.turbine.services.localization.Localization;
64import org.apache.turbine.services.rundata.RunDataService;
6566// Password encryption67import javax.mail.internet.MimeUtility;
68import java.security.MessageDigest;
69import java.io.OutputStream;
70import java.io.ByteArrayOutputStream;
717273/***74 * Default Jetspeed-Turbine User Management implementation75 *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 */8182publicclassTurbineUserManagementextends TurbineBaseService
83 implements UserManagement,
84CredentialsManagement85 {
86/***87 * Static initialization of the logger for this class88 */89privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(TurbineUserManagement.class.getName());
9091privatefinalstatic String CONFIG_SECURE_PASSWORDS_KEY = "secure.passwords";
92privatefinalstatic String CONFIG_SECURE_PASSWORDS_ALGORITHM = "secure.passwords.algorithm";
93privatefinalstatic String CONFIG_SYSTEM_USERS = "system.users";
9495boolean securePasswords = false;
96 String passwordsAlgorithm = "SHA";
97 Vector systemUsers = null;
9899privatefinalstatic String CONFIG_NEWUSER_ROLES = "newuser.roles";
100privatefinalstatic String [] DEFAULT_CONFIG_NEWUSER_ROLES =
101 { "user" };
102103 String roles[] = null;
104105/*** The JetspeedRunData Service. */106privateJetspeedRunDataService runDataService = null;
107108///////////////////////////////////////////////////////////////////////////109// User Management Interfaces110///////////////////////////////////////////////////////////////////////////111112/***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 context119 * 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 match125 * the principal identity to a user.126 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege127 */128publicJetspeedUser getUser(Principal principal)
129 throws JetspeedSecurityException130 {
131// TODO: check requestor for permission132133 Criteria criteria = new Criteria();
134if (principal instanceof UserNamePrincipal)
135 {
136 criteria.add(TurbineUserPeer.LOGIN_NAME, principal.getName());
137 }
138elseif (principal instanceof UserIdPrincipal)
139 {
140 criteria.add(TurbineUserPeer.USER_ID, principal.getName());
141 }
142else143 {
144thrownewUserException("Invalid Principal Type in getUser: " + principal.getClass().getName());
145 }
146 List users;
147try148 {
149 users = TurbineUserPeer.doSelectUsers(criteria);
150 }
151catch(Exception e)
152 {
153 String message = "Failed to retrieve user '" + principal.getName() + "'";
154 logger.error( message, e );
155thrownewUserException( message, e );
156 }
157if ( users.size() > 1 )
158 {
159thrownewUserException(
160"Multiple Users with same username '" + principal.getName() + "'");
161 }
162if ( users.size() == 1 )
163 {
164return (JetspeedUser)users.get(0);
165 }
166thrownewUnknownUserException("Unknown user '" + principal.getName() + "'");
167168 }
169170/***171 * Retrieves a collection of all <code>JetspeedUser</code>s.172 * The security service may optionally check the current user context173 * 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 privilege178 */179public Iterator getUsers()
180 throws JetspeedSecurityException181 {
182 Criteria criteria = new Criteria();
183 List users;
184try185 {
186 users = TurbineUserPeer.doSelectUsers(criteria);
187 }
188catch(Exception e)
189 {
190 logger.error( "Failed to retrieve users ", e );
191thrownewUserException("Failed to retrieve users ", e);
192 }
193return users.iterator();
194 }
195196/***197 * Retrieves a collection of <code>JetspeedUser</code>s filtered by a security198 * provider-specific query string. For example SQL, OQL, JDOQL.199 * The security service may optionally check the current user context200 * 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 privilege205 */206public Iterator getUsers(String filter)
207 throws JetspeedSecurityException208 {
209// TODO: implement this with a SQL string210211 Criteria criteria = new Criteria();
212 List users;
213try214 {
215 users = TurbineUserPeer.doSelectUsers(criteria);
216 }
217catch(Exception e)
218 {
219 logger.error( "Failed to retrieve users ", e );
220thrownewUserException("Failed to retrieve users ", e);
221 }
222return users.iterator();
223 }
224225/***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 context229 * 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 privilege233 */234publicvoid saveUser(JetspeedUser user)
235 throws JetspeedSecurityException236 {
237if(!accountExists(user, true))
238 {
239thrownewUnknownUserException("Cannot save user '" + user.getUserName() +
240"', User doesn't exist");
241 }
242 Criteria criteria = TurbineUserPeer.buildCriteria(user);
243try244 {
245 TurbineUserPeer.doUpdate(criteria);
246 }
247catch(Exception e)
248 {
249 logger.error( "Failed to save user object ", e );
250thrownewUserException("Failed to save user object ", e);
251 }
252253 }
254255256/***257 * Adds a <code>JetspeedUser</code> into permanent storage.258 * The security service can throw a <code>NotUniqueUserException</code> when the public259 * credentials fail to meet the security provider-specific unique constraints.260 * The security service may optionally check the current user context261 * 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 meet265 * the security provider-specific unique constraints.266 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege267 */268publicvoid addUser(JetspeedUser user)
269 throws JetspeedSecurityException270 {
271if(accountExists(user))
272 {
273thrownewNotUniqueUserException("The account '" +
274 user.getUserName() + "' already exists");
275 }
276 String initialPassword = user.getPassword();
277 String encrypted = JetspeedSecurity.encryptPassword(initialPassword);
278 user.setPassword(encrypted);
279 Criteria criteria = TurbineUserPeer.buildCriteria(user);
280try281 {
282283 NumberKey key = (NumberKey)TurbineUserPeer.doInsert(criteria);
284285 ((BaseJetspeedUser)user).setUserId(key.toString());
286287 }
288catch(Exception e)
289 {
290 String message = "Failed to create account '" + user.getUserName() + "'";
291 logger.error( message, e );
292thrownewUserException( message, e );
293 }
294295 addDefaultPSML(user);
296 }
297298/*299 * A default PSML page is added for the user, and the Jetspeed default roles300 * are assigned to the new user.301 *302 * @param user The new user.303 * @throws304 */305protectedvoid addDefaultPSML(JetspeedUser user)
306 throws JetspeedSecurityException307 {
308for (int ix = 0; ix < roles.length; ix++)
309 {
310try311 {
312 JetspeedSecurity.grantRole(user.getUserName(),
313 JetspeedSecurity.getRole(roles[ix]).getName());
314 }
315catch(Exception e)
316 {
317 logger.error("Could not grant role: " + roles[ix] + " to user " + user.getUserName(), e);
318 }
319 }
320try321 {
322JetspeedRunData rundata = getRunData();
323if (rundata != null && Profiler.useRoleProfileMerging() == false)
324 {
325Profile profile = Profiler.createProfile();
326 profile.setUser(user);
327 profile.setMediaType("html");
328 Profiler.createProfile(getRunData(), profile);
329 }
330 }
331catch (Exception e)
332 {
333 logger.error( "Failed to create profile for new user ", e );
334 removeUser(newUserNamePrincipal(user.getUserName()));
335thrownewUserException("Failed to create profile for new user ", e);
336 }
337 }
338339/***340 * Removes a <code>JetspeedUser</code> from the permanent store.341 * The security service may optionally check the current user context342 * 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 match347 * the principal identity to a user.348 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege349 */350publicvoid removeUser(Principal principal)
351 throws JetspeedSecurityException352 {
353if (systemUsers.contains(principal.getName()))
354 {
355thrownewUserException("[" + principal.getName() + "] is a system user and cannot be removed");
356 }
357358JetspeedUser user = getUser(principal);
359360 Criteria criteria = new Criteria();
361if (principal instanceof UserNamePrincipal)
362 {
363 criteria.add(TurbineUserPeer.LOGIN_NAME, principal.getName());
364 }
365elseif (principal instanceof UserIdPrincipal)
366 {
367 criteria.add(TurbineUserPeer.USER_ID, principal.getName());
368 }
369else370 {
371thrownewUserException("Invalid Principal Type in removeUser: " + principal.getClass().getName());
372 }
373374try375 {
376 TurbineUserPeer.doDelete(criteria);
377 PsmlManager.removeUserDocuments(user);
378 }
379catch(Exception e)
380 {
381 String message = "Failed to remove account '" + user.getUserName() + "'";
382 logger.error( message, e );
383thrownewUserException( message, e );
384 }
385386 }
387388389///////////////////////////////////////////////////////////////////////////390// Credentials Management391///////////////////////////////////////////////////////////////////////////392393/***394 * Allows for a user to change their own password.395 *396 * @param user the JetspeedUser to change password397 * @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 match401 * the principal identity to a user.402 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege403 */404publicvoid changePassword( JetspeedUser user,
405 String oldPassword,
406 String newPassword )
407 throws JetspeedSecurityException408 {
409 oldPassword = JetspeedSecurity.convertPassword(oldPassword);
410 newPassword = JetspeedSecurity.convertPassword(newPassword);
411412 String encrypted = JetspeedSecurity.encryptPassword(oldPassword);
413if(!accountExists(user))
414 {
415thrownewUnknownUserException(Localization.getString("UPDATEACCOUNT_NOUSER"));
416 }
417if(!user.getPassword().equals(encrypted))
418 {
419thrownewUserException(Localization.getString("UPDATEACCOUNT_BADOLDPASSWORD"));
420 }
421 user.setPassword(JetspeedSecurity.encryptPassword(newPassword));
422423// Set the last password change date424 user.setPasswordChanged(new Date());
425426// save the changes in the database immediately, to prevent the password427// being 'reverted' to the old value if the user data is lost somehow428// before it is saved at session's expiry.429 saveUser(user);
430 }
431432/***433 * Forcibly sets new password for a User.434 *435 * Provides an administrator the ability to change the forgotten or436 * compromised passwords. Certain implementatations of this feature437 * would require administrative level access to the authenticating438 * 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 match444 * the principal identity to a user.445 * @exception InsufficientPrivilegeException when the requestor is denied due to insufficient privilege446 */447publicvoid forcePassword( JetspeedUser user, String password )
448 throws JetspeedSecurityException449 {
450if(!accountExists(user))
451 {
452thrownewUnknownUserException("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 the457// password being 'reverted' to the old value if the user data458// is lost somehow before it is saved at session's expiry.459 saveUser(user);
460 }
461462/***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 used468 * to chose which digest algorithm should be used for performing the469 * encryption. <code>SHA</code> is used by default.470 *471 * @param password the password to process472 * @return processed password473 */474public String encryptPassword( String password )
475 throws JetspeedSecurityException476 {
477if (securePasswords == false)
478 {
479return password;
480 }
481if(password == null)
482 {
483returnnull;
484 }
485486try487 {
488 MessageDigest md = MessageDigest.getInstance(passwordsAlgorithm);
489// We need to use unicode here, to be independent of platform's490// default encoding. Thanks to SGawin for spotting this.491 byte[] digest = md.digest(password.getBytes("UTF-8"));
492 ByteArrayOutputStream bas = new ByteArrayOutputStream(digest.length + digest.length / 3 + 1);
493 OutputStream encodedStream = MimeUtility.encode(bas, "base64");
494 encodedStream.write(digest);
495 encodedStream.flush();
496 encodedStream.close();
497return bas.toString();
498 }
499catch (Exception e)
500 {
501 logger.error("Unable to encrypt password."+e.getMessage(), e);
502returnnull;
503 }
504 }
505506///////////////////////////////////////////////////////////////////////////507// Service Init508///////////////////////////////////////////////////////////////////////////509510511/***512 * This is the early initialization method called by the513 * Turbine <code>Service</code> framework514 * @param conf The <code>ServletConfig</code>515 * @exception throws a <code>InitializationException</code> if the service516 * fails to initialize517 */518publicsynchronizedvoid init(ServletConfig conf)
519 throws InitializationException
520 {
521if (getInit()) return;
522523super.init(conf);
524525// get configuration parameters from Jetspeed Resources526 ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
527 .getResources(JetspeedSecurityService.SERVICE_NAME);
528529 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());
534535try536 {
537 roles = serviceConf.getStringArray(CONFIG_NEWUSER_ROLES);
538 }
539catch (Exception e)
540 {}
541542if (null == roles || roles.length == 0)
543 {
544 roles = DEFAULT_CONFIG_NEWUSER_ROLES;
545 }
546547this.runDataService =
548 (JetspeedRunDataService)TurbineServices.getInstance()
549 .getService(RunDataService.SERVICE_NAME);
550551 setInit(true);
552 }
553554///////////////////////////////////////////////////////////////////////////555// Internal556///////////////////////////////////////////////////////////////////////////557558/***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 id565 * @return true if the specified account exists566 * @throws UserException if there was a general db access error567 *568 */569protectedboolean accountExists( JetspeedUser user )
570 throws UserException571 {
572return accountExists(user, false);
573 }
574575protectedboolean accountExists( JetspeedUser user, boolean checkUniqueId )
576 throws UserException577 {
578 String id = user.getUserId();
579 Criteria criteria = new Criteria();
580 criteria.add(TurbineUserPeer.LOGIN_NAME, user.getUserName());
581 List users;
582try583 {
584 users = TurbineUserPeer.doSelect(criteria);
585 }
586catch(Exception e)
587 {
588 logger.error( "Failed to check account's presence", e );
589thrownewUserException(
590"Failed to check account's presence", e);
591 }
592if (users.size() < 1)
593 {
594return false;
595 }
596TurbineUser retrieved = (TurbineUser)users.get(0);
597int key = retrieved.getUserId();
598 String keyId = String.valueOf(key);
599if (checkUniqueId && !keyId.equals(id))
600 {
601thrownewUserException("User exists but under a different unique ID");
602 }
603returntrue;
604 }
605606protectedJetspeedRunData getRunData()
607 {
608JetspeedRunData rundata = null;
609if (this.runDataService != null)
610 {
611 rundata = this.runDataService.getCurrentRunData();
612 }
613return rundata;
614 }
615616617618 }
619