1 package org.codehaus.redback.rest.services;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import net.sf.ehcache.CacheManager;
23 import org.apache.archiva.redback.configuration.UserConfiguration;
24 import org.apache.archiva.redback.users.UserManager;
25 import org.apache.archiva.redback.users.UserNotFoundException;
26 import org.apache.commons.lang.StringUtils;
27 import org.codehaus.plexus.cache.Cache;
28 import org.apache.archiva.redback.authentication.AuthenticationException;
29 import org.apache.archiva.redback.authentication.TokenBasedAuthenticationDataSource;
30 import org.codehaus.plexus.redback.keys.AuthenticationKey;
31 import org.codehaus.plexus.redback.keys.KeyManager;
32 import org.codehaus.plexus.redback.keys.KeyManagerException;
33 import org.codehaus.plexus.redback.keys.KeyNotFoundException;
34 import org.codehaus.plexus.redback.policy.AccountLockedException;
35 import org.codehaus.plexus.redback.policy.MustChangePasswordException;
36 import org.codehaus.plexus.redback.policy.PasswordEncoder;
37 import org.codehaus.plexus.redback.policy.UserSecurityPolicy;
38 import org.codehaus.plexus.redback.rbac.RBACManager;
39 import org.codehaus.plexus.redback.rbac.RbacManagerException;
40 import org.codehaus.plexus.redback.rbac.RbacObjectNotFoundException;
41 import org.codehaus.plexus.redback.rbac.UserAssignment;
42 import org.codehaus.plexus.redback.role.RoleManager;
43 import org.codehaus.plexus.redback.role.RoleManagerException;
44 import org.codehaus.plexus.redback.system.SecuritySystem;
45 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
46 import org.codehaus.redback.integration.mail.Mailer;
47 import org.codehaus.redback.integration.security.role.RedbackRoleConstants;
48 import org.codehaus.redback.rest.api.model.ErrorMessage;
49 import org.codehaus.redback.rest.api.model.Operation;
50 import org.codehaus.redback.rest.api.model.Permission;
51 import org.codehaus.redback.rest.api.model.RegistrationKey;
52 import org.codehaus.redback.rest.api.model.Resource;
53 import org.codehaus.redback.rest.api.model.User;
54 import org.codehaus.redback.rest.api.services.RedbackServiceException;
55 import org.codehaus.redback.rest.api.services.UserService;
56 import org.codehaus.redback.rest.services.utils.PasswordValidator;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.stereotype.Service;
61 import javax.inject.Inject;
62 import javax.inject.Named;
63 import javax.mail.internet.AddressException;
64 import javax.mail.internet.InternetAddress;
65 import javax.servlet.http.HttpServletRequest;
66 import javax.ws.rs.core.Context;
67 import javax.ws.rs.core.Response;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collection;
71 import java.util.List;
74 @Service( "userService#rest" )
75 public class DefaultUserService
76 implements UserService
79 private Logger log = LoggerFactory.getLogger( getClass() );
81 private static final String VALID_USERNAME_CHARS = "[a-zA-Z_0-9\\-.@]*";
83 private UserManager userManager;
85 private SecuritySystem securitySystem;
88 private UserConfiguration config;
91 private RoleManager roleManager;
94 * cache used for user assignments
97 @Named( value = "cache#userAssignments" )
98 private Cache userAssignmentsCache;
101 * cache used for user permissions
104 @Named( value = "cache#userPermissions" )
105 private Cache userPermissionsCache;
108 * Cache used for users
111 @Named( value = "cache#users" )
112 private Cache usersCache;
115 private Mailer mailer;
118 @Named( value = "rBACManager#cached" )
119 private RBACManager rbacManager;
121 private HttpAuthenticator httpAuthenticator;
124 private PasswordValidator passwordValidator;
127 private HttpServletRequest httpServletRequest;
130 public DefaultUserService( @Named( value = "userManager#cached" ) UserManager userManager,
131 SecuritySystem securitySystem,
132 @Named( "httpAuthenticator#basic" ) HttpAuthenticator httpAuthenticator )
134 this.userManager = userManager;
135 this.securitySystem = securitySystem;
136 this.httpAuthenticator = httpAuthenticator;
140 public Boolean createUser( User user )
141 throws RedbackServiceException
146 org.apache.archiva.redback.users.User u = userManager.findUser( user.getUsername() );
149 throw new RedbackServiceException(
150 new ErrorMessage( "user " + user.getUsername() + " already exists" ) );
153 catch ( UserNotFoundException e )
155 //ignore we just want to prevent non human readable error message from backend :-)
156 log.debug( "user {} not exists", user.getUsername() );
160 if ( StringUtils.isEmpty( user.getUsername() ) )
162 throw new RedbackServiceException( new ErrorMessage( "username cannot be empty" ) );
165 if ( StringUtils.isEmpty( user.getFullName() ) )
167 throw new RedbackServiceException( new ErrorMessage( "fullName cannot be empty" ) );
170 if ( StringUtils.isEmpty( user.getEmail() ) )
172 throw new RedbackServiceException( new ErrorMessage( "email cannot be empty" ) );
175 org.apache.archiva.redback.users.User u =
176 userManager.createUser( user.getUsername(), user.getFullName(), user.getEmail() );
177 u.setPassword( user.getPassword() );
178 u.setLocked( user.isLocked() );
179 u.setPasswordChangeRequired( user.isPasswordChangeRequired() );
180 u.setPermanent( user.isPermanent() );
181 u.setValidated( user.isValidated() );
182 u = userManager.addUser( u );
183 if ( !user.isPasswordChangeRequired() )
185 u.setPasswordChangeRequired( false );
188 u = userManager.updateUser( u );
189 log.debug( "user {} created", u.getUsername() );
191 catch ( UserNotFoundException e )
193 throw new RedbackServiceException( e.getMessage() );
198 roleManager.assignRole( RedbackRoleConstants.REGISTERED_USER_ROLE_ID, u.getPrincipal().toString() );
200 catch ( RoleManagerException rpe )
202 log.error( "RoleProfile Error: " + rpe.getMessage(), rpe );
203 throw new RedbackServiceException( new ErrorMessage( "assign.role.failure", null ) );
208 public Boolean deleteUser( String username )
209 throws RedbackServiceException
215 if ( rbacManager.userAssignmentExists( username ) )
217 UserAssignment assignment = rbacManager.getUserAssignment( username );
218 rbacManager.removeUserAssignment( assignment );
222 catch ( RbacManagerException e )
224 log.error( e.getMessage(), e );
225 throw new RedbackServiceException( e.getMessage() );
229 userManager.deleteUser( username );
232 catch ( UserNotFoundException e )
234 log.error( e.getMessage(), e );
235 throw new RedbackServiceException( e.getMessage() );
239 removeFromCache( username );
244 public User getUser( String username )
245 throws RedbackServiceException
249 org.apache.archiva.redback.users.User user = userManager.findUser( username );
250 return getSimpleUser( user );
252 catch ( UserNotFoundException e )
258 public List<User> getUsers()
259 throws RedbackServiceException
261 List<org.apache.archiva.redback.users.User> users = userManager.getUsers();
262 List<User> simpleUsers = new ArrayList<User>( users.size() );
264 for ( org.apache.archiva.redback.users.User user : users )
266 simpleUsers.add( getSimpleUser( user ) );
272 public Boolean updateMe( User user )
273 throws RedbackServiceException
275 // check username == one in the session
276 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get();
277 if ( redbackRequestInformation == null || redbackRequestInformation.getUser() == null )
279 throw new RedbackServiceException( new ErrorMessage( "you must be logged to update your profile" ),
280 Response.Status.FORBIDDEN.getStatusCode() );
284 throw new RedbackServiceException( new ErrorMessage( "user parameter is mandatory" ),
285 Response.Status.BAD_REQUEST.getStatusCode() );
287 if ( !StringUtils.equals( redbackRequestInformation.getUser().getUsername(), user.getUsername() ) )
289 throw new RedbackServiceException( new ErrorMessage( "you can update only your profile" ),
290 Response.Status.FORBIDDEN.getStatusCode() );
293 if ( StringUtils.isEmpty( user.getPreviousPassword() ) )
295 throw new RedbackServiceException( new ErrorMessage( "previous password is empty" ),
296 Response.Status.BAD_REQUEST.getStatusCode() );
299 User realUser = getUser( user.getUsername() );
302 String previousEncodedPassword =
303 securitySystem.getUserManager().findUser( user.getUsername() ).getEncodedPassword();
305 // check oldPassword with the current one
307 PasswordEncoder encoder = securitySystem.getPolicy().getPasswordEncoder();
309 if ( !encoder.isPasswordValid( previousEncodedPassword, user.getPreviousPassword() ) )
312 throw new RedbackServiceException( new ErrorMessage( "password.provided.does.not.match.existing" ),
313 Response.Status.BAD_REQUEST.getStatusCode() );
316 catch ( UserNotFoundException e )
318 throw new RedbackServiceException( new ErrorMessage( "user not found" ),
319 Response.Status.BAD_REQUEST.getStatusCode() );
321 // only 3 fields to update
322 realUser.setFullName( user.getFullName() );
323 realUser.setEmail( user.getEmail() );
324 // ui can limit to not update password
325 if ( StringUtils.isNotBlank( user.getPassword() ) )
327 passwordValidator.validatePassword( user.getPassword(), user.getUsername() );
329 realUser.setPassword( user.getPassword() );
332 updateUser( realUser );
337 public Boolean updateUser( User user )
338 throws RedbackServiceException
342 org.apache.archiva.redback.users.User rawUser = userManager.findUser( user.getUsername() );
343 rawUser.setFullName( user.getFullName() );
344 rawUser.setEmail( user.getEmail() );
345 rawUser.setValidated( user.isValidated() );
346 rawUser.setLocked( user.isLocked() );
347 rawUser.setPassword( user.getPassword() );
348 rawUser.setPasswordChangeRequired( user.isPasswordChangeRequired() );
349 rawUser.setPermanent( user.isPermanent() );
351 userManager.updateUser( rawUser );
354 catch ( UserNotFoundException e )
356 throw new RedbackServiceException( e.getMessage() );
360 public int removeFromCache( String userName )
361 throws RedbackServiceException
363 if ( userAssignmentsCache != null )
365 userAssignmentsCache.remove( userName );
367 if ( userPermissionsCache != null )
369 userPermissionsCache.remove( userName );
371 if ( usersCache != null )
373 usersCache.remove( userName );
376 CacheManager cacheManager = CacheManager.getInstance();
377 String[] caches = cacheManager.getCacheNames();
378 for ( String cacheName : caches )
380 if ( StringUtils.startsWith( cacheName, "org.codehaus.plexus.redback.rbac.jdo" ) )
382 cacheManager.getCache( cacheName ).removeAll();
389 public User getGuestUser()
390 throws RedbackServiceException
394 org.apache.archiva.redback.users.User user = userManager.getGuestUser();
395 return getSimpleUser( user );
397 catch ( Exception e )
403 public User createGuestUser()
404 throws RedbackServiceException
406 User u = getGuestUser();
411 // temporary disable policy during guest creation as no password !
414 securitySystem.getPolicy().setEnabled( false );
415 org.apache.archiva.redback.users.User user = userManager.createGuestUser();
416 user.setPasswordChangeRequired( false );
417 user = userManager.updateUser( user, false );
418 roleManager.assignRole( "guest", user.getPrincipal().toString() );
419 return getSimpleUser( user );
421 catch ( RoleManagerException e )
423 log.error( e.getMessage(), e );
424 throw new RedbackServiceException( e.getMessage() );
426 catch ( UserNotFoundException e )
428 // olamy I wonder how this can happen :-)
429 log.error( e.getMessage(), e );
430 throw new RedbackServiceException( e.getMessage() );
435 if ( !securitySystem.getPolicy().isEnabled() )
437 securitySystem.getPolicy().setEnabled( true );
442 public Boolean ping()
443 throws RedbackServiceException
448 private User getSimpleUser( org.apache.archiva.redback.users.User user )
454 return new User( user );
457 public Boolean createAdminUser( User adminUser )
458 throws RedbackServiceException
460 if ( isAdminUserExists() )
462 return Boolean.FALSE;
465 org.apache.archiva.redback.users.User user =
466 userManager.createUser( RedbackRoleConstants.ADMINISTRATOR_ACCOUNT_NAME, adminUser.getFullName(),
467 adminUser.getEmail() );
468 user.setPassword( adminUser.getPassword() );
470 user.setLocked( false );
471 user.setPasswordChangeRequired( false );
472 user.setPermanent( true );
473 user.setValidated( true );
475 userManager.addUser( user );
479 roleManager.assignRole( "system-administrator", user.getPrincipal().toString() );
481 catch ( RoleManagerException e )
483 throw new RedbackServiceException( e.getMessage() );
488 public Boolean isAdminUserExists()
489 throws RedbackServiceException
493 userManager.findUser( config.getString( "redback.default.admin" ) );
496 catch ( UserNotFoundException e )
500 return Boolean.FALSE;
503 public Boolean resetPassword( String username )
504 throws RedbackServiceException
506 if ( StringUtils.isEmpty( username ) )
508 throw new RedbackServiceException( new ErrorMessage( "username.cannot.be.empty" ) );
511 UserManager userManager = securitySystem.getUserManager();
512 KeyManager keyManager = securitySystem.getKeyManager();
513 UserSecurityPolicy policy = securitySystem.getPolicy();
517 org.apache.archiva.redback.users.User user = userManager.findUser( username );
519 AuthenticationKey authkey = keyManager.createKey( username, "Password Reset Request",
520 policy.getUserValidationSettings().getEmailValidationTimeout() );
522 mailer.sendPasswordResetEmail( Arrays.asList( user.getEmail() ), authkey, getBaseUrl() );
524 log.info( "password reset request for username {}", username );
526 catch ( UserNotFoundException e )
528 log.info( "Password Reset on non-existant user [{}].", username );
529 throw new RedbackServiceException( new ErrorMessage( "password.reset.failure" ) );
531 catch ( KeyManagerException e )
533 log.info( "Unable to issue password reset.", e );
534 throw new RedbackServiceException( new ErrorMessage( "password.reset.email.generation.failure" ) );
540 public RegistrationKey registerUser( User user )
541 throws RedbackServiceException
545 throw new RedbackServiceException( new ErrorMessage( "invalid.user.credentials", null ) );
549 UserSecurityPolicy securityPolicy = securitySystem.getPolicy();
551 boolean emailValidationRequired = securityPolicy.getUserValidationSettings().isEmailValidationRequired();
553 if ( emailValidationRequired )
555 validateCredentialsLoose( user );
559 validateCredentialsStrict( user );
562 // NOTE: Do not perform Password Rules Validation Here.
564 if ( userManager.userExists( user.getUsername() ) )
566 throw new RedbackServiceException(
567 new ErrorMessage( "user.already.exists", new String[]{ user.getUsername() } ) );
570 org.apache.archiva.redback.users.User u =
571 userManager.createUser( user.getUsername(), user.getFullName(), user.getEmail() );
572 u.setPassword( user.getPassword() );
573 u.setValidated( false );
574 u.setLocked( false );
578 roleManager.assignRole( RedbackRoleConstants.REGISTERED_USER_ROLE_ID, u.getPrincipal().toString() );
580 catch ( RoleManagerException rpe )
582 log.error( "RoleProfile Error: " + rpe.getMessage(), rpe );
583 throw new RedbackServiceException( new ErrorMessage( "assign.role.failure", null ) );
586 if ( emailValidationRequired )
592 AuthenticationKey authkey =
593 securitySystem.getKeyManager().createKey( u.getPrincipal().toString(), "New User Email Validation",
594 securityPolicy.getUserValidationSettings().getEmailValidationTimeout() );
596 mailer.sendAccountValidationEmail( Arrays.asList( u.getEmail() ), authkey, getBaseUrl() );
598 securityPolicy.setEnabled( false );
599 userManager.addUser( u );
600 return new RegistrationKey( authkey.getKey() );
603 catch ( KeyManagerException e )
605 log.error( "Unable to register a new user.", e );
606 throw new RedbackServiceException( new ErrorMessage( "cannot.register.user", null ) );
610 securityPolicy.setEnabled( true );
615 userManager.addUser( u );
616 return new RegistrationKey( "-1" );
619 // FIXME log this event
621 AuditEvent event = new AuditEvent( getText( "log.account.create" ) );
622 event.setAffectedUser( username );
628 public Boolean validateUserFromKey( String key )
629 throws RedbackServiceException
631 String principal = null;
634 AuthenticationKey authkey = securitySystem.getKeyManager().findKey( key );
636 org.apache.archiva.redback.users.User user =
637 securitySystem.getUserManager().findUser( authkey.getForPrincipal() );
639 user.setValidated( true );
640 user.setLocked( false );
641 user.setPasswordChangeRequired( true );
642 user.setEncodedPassword( "" );
644 principal = user.getPrincipal().toString();
646 TokenBasedAuthenticationDataSource authsource = new TokenBasedAuthenticationDataSource();
647 authsource.setPrincipal( principal );
648 authsource.setToken( authkey.getKey() );
649 authsource.setEnforcePasswordChange( false );
651 securitySystem.getUserManager().updateUser( user );
653 httpAuthenticator.authenticate( authsource, httpServletRequest.getSession( true ) );
655 log.info( "account validated for user {}", user.getUsername() );
659 catch ( MustChangePasswordException e )
661 throw new RedbackServiceException( e.getMessage(), Response.Status.FORBIDDEN.getStatusCode() );
663 catch ( KeyNotFoundException e )
665 log.info( "Invalid key requested: {}", key );
666 throw new RedbackServiceException( new ErrorMessage( "cannot.find.key" ) );
668 catch ( KeyManagerException e )
670 throw new RedbackServiceException( new ErrorMessage( "cannot.find.key.at.the.momment" ) );
673 catch ( UserNotFoundException e )
675 throw new RedbackServiceException( new ErrorMessage( "cannot.find.user", new String[]{ principal } ) );
678 catch ( AccountLockedException e )
680 throw new RedbackServiceException( e.getMessage(), Response.Status.FORBIDDEN.getStatusCode() );
682 catch ( AuthenticationException e )
684 throw new RedbackServiceException( e.getMessage(), Response.Status.FORBIDDEN.getStatusCode() );
688 public Collection<Permission> getCurrentUserPermissions()
689 throws RedbackServiceException
691 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get();
692 String userName = UserManager.GUEST_USERNAME;
693 if ( redbackRequestInformation != null && redbackRequestInformation.getUser() != null )
695 userName = redbackRequestInformation.getUser().getUsername();
698 return getUserPermissions( userName );
701 public Collection<Operation> getCurrentUserOperations()
702 throws RedbackServiceException
704 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get();
705 String userName = UserManager.GUEST_USERNAME;
706 if ( redbackRequestInformation != null && redbackRequestInformation.getUser() != null )
708 userName = redbackRequestInformation.getUser().getUsername();
711 return getUserOperations( userName );
714 public Collection<Operation> getUserOperations( String userName )
715 throws RedbackServiceException
717 Collection<Permission> permissions = getUserPermissions( userName );
718 List<Operation> operations = new ArrayList<Operation>( permissions.size() );
719 for ( Permission permission : permissions )
721 if ( permission.getOperation() != null )
723 Operation operation = new Operation();
724 operation.setName( permission.getOperation().getName() );
725 operations.add( operation );
731 public Collection<Permission> getUserPermissions( String userName )
732 throws RedbackServiceException
736 Set<org.codehaus.plexus.redback.rbac.Permission> permissions =
737 rbacManager.getAssignedPermissions( userName );
738 // FIXME return guest permissions !!
739 List<Permission> userPermissions = new ArrayList<Permission>( permissions.size() );
740 for ( org.codehaus.plexus.redback.rbac.Permission p : permissions )
742 Permission permission = new Permission();
743 permission.setName( p.getName() );
745 if ( p.getOperation() != null )
747 Operation operation = new Operation();
748 operation.setName( p.getOperation().getName() );
749 permission.setOperation( operation );
752 if ( p.getResource() != null )
754 Resource resource = new Resource();
755 resource.setIdentifier( p.getResource().getIdentifier() );
756 resource.setPattern( p.getResource().isPattern() );
757 permission.setResource( resource );
760 userPermissions.add( permission );
762 return userPermissions;
764 catch ( RbacObjectNotFoundException e )
766 log.error( e.getMessage(), e );
767 throw new RedbackServiceException( e.getMessage() );
769 catch ( RbacManagerException e )
771 log.error( e.getMessage(), e );
772 throw new RedbackServiceException( e.getMessage() );
776 public void validateCredentialsLoose( User user )
777 throws RedbackServiceException
779 RedbackServiceException redbackServiceException =
780 new RedbackServiceException( "issues during validating user" );
781 if ( org.codehaus.plexus.util.StringUtils.isEmpty( user.getUsername() ) )
783 redbackServiceException.addErrorMessage( new ErrorMessage( "username.required", null ) );
787 if ( !user.getUsername().matches( VALID_USERNAME_CHARS ) )
789 redbackServiceException.addErrorMessage( new ErrorMessage( "username.invalid.characters", null ) );
793 if ( org.codehaus.plexus.util.StringUtils.isEmpty( user.getFullName() ) )
795 redbackServiceException.addErrorMessage( new ErrorMessage( "fullName.required", null ) );
798 if ( org.codehaus.plexus.util.StringUtils.isEmpty( user.getEmail() ) )
800 redbackServiceException.addErrorMessage( new ErrorMessage( "email.required", null ) );
803 if ( !org.codehaus.plexus.util.StringUtils.equals( user.getPassword(), user.getConfirmPassword() ) )
805 redbackServiceException.addErrorMessage( new ErrorMessage( "passwords.does.not.match", null ) );
810 if ( !org.codehaus.plexus.util.StringUtils.isEmpty( user.getEmail() ) )
812 new InternetAddress( user.getEmail(), true );
815 catch ( AddressException e )
817 redbackServiceException.addErrorMessage( new ErrorMessage( "email.invalid", null ) );
819 if ( !redbackServiceException.getErrorMessages().isEmpty() )
821 throw redbackServiceException;
825 public void validateCredentialsStrict( User user )
826 throws RedbackServiceException
828 validateCredentialsLoose( user );
830 org.apache.archiva.redback.users.User tmpuser =
831 userManager.createUser( user.getUsername(), user.getFullName(), user.getEmail() );
833 user.setPassword( user.getPassword() );
835 securitySystem.getPolicy().validatePassword( tmpuser );
837 if ( ( org.codehaus.plexus.util.StringUtils.isEmpty( user.getPassword() ) ) )
839 throw new RedbackServiceException( new ErrorMessage( "password.required", null ) );
843 private String getBaseUrl()
845 if ( httpServletRequest != null )
847 if ( httpServletRequest != null )
849 return httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName() + (
850 httpServletRequest.getServerPort() == 80
852 : ":" + httpServletRequest.getServerPort() ) + httpServletRequest.getContextPath();
858 public Boolean unlockUser( String username )
859 throws RedbackServiceException
861 User user = getUser( username );
864 user.setLocked( false );
868 return Boolean.FALSE;
871 public Boolean lockUser( String username )
872 throws RedbackServiceException
874 User user = getUser( username );
877 user.setLocked( true );
881 return Boolean.FALSE;
884 public Boolean passwordChangeRequired( String username )
885 throws RedbackServiceException
887 User user = getUser( username );
890 user.setPasswordChangeRequired( true );
894 return Boolean.FALSE;
897 public Boolean passwordChangeNotRequired( String username )
898 throws RedbackServiceException
900 User user = getUser( username );
903 user.setPasswordChangeRequired( false );
907 return Boolean.FALSE;