001 package org.apache.archiva.web.security; 002 /* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 021 import org.apache.archiva.admin.model.RepositoryAdminException; 022 import org.apache.archiva.admin.model.runtime.RedbackRuntimeConfigurationAdmin; 023 import org.apache.archiva.redback.authentication.AbstractAuthenticator; 024 import org.apache.archiva.redback.authentication.AuthenticationConstants; 025 import org.apache.archiva.redback.authentication.AuthenticationDataSource; 026 import org.apache.archiva.redback.authentication.AuthenticationException; 027 import org.apache.archiva.redback.authentication.AuthenticationFailureCause; 028 import org.apache.archiva.redback.authentication.AuthenticationResult; 029 import org.apache.archiva.redback.authentication.Authenticator; 030 import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource; 031 import org.apache.archiva.redback.policy.AccountLockedException; 032 import org.apache.archiva.redback.policy.MustChangePasswordException; 033 import org.apache.archiva.redback.policy.PasswordEncoder; 034 import org.apache.archiva.redback.policy.UserSecurityPolicy; 035 import org.apache.archiva.redback.users.User; 036 import org.apache.archiva.redback.users.UserManager; 037 import org.apache.archiva.redback.users.UserManagerException; 038 import org.apache.archiva.redback.users.UserNotFoundException; 039 import org.slf4j.Logger; 040 import org.slf4j.LoggerFactory; 041 import org.springframework.context.ApplicationContext; 042 import org.springframework.stereotype.Service; 043 044 import javax.annotation.PostConstruct; 045 import javax.inject.Inject; 046 import java.util.ArrayList; 047 import java.util.List; 048 049 /** 050 * @author Olivier Lamy 051 * @since 1.4-M4 052 */ 053 @Service("authenticator#archiva") 054 public class ArchivaUserManagerAuthenticator 055 extends AbstractAuthenticator 056 implements Authenticator 057 { 058 private Logger log = LoggerFactory.getLogger( getClass() ); 059 060 @Inject 061 private UserSecurityPolicy securityPolicy; 062 063 @Inject 064 private ApplicationContext applicationContext; 065 066 @Inject 067 private RedbackRuntimeConfigurationAdmin redbackRuntimeConfigurationAdmin; 068 069 private List<UserManager> userManagers; 070 071 @PostConstruct 072 @Override 073 public void initialize() 074 throws AuthenticationException 075 { 076 try 077 { 078 List<String> userManagerImpls = 079 redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration().getUserManagerImpls(); 080 081 userManagers = new ArrayList<UserManager>( userManagerImpls.size() ); 082 083 for ( String beanId : userManagerImpls ) 084 { 085 userManagers.add( applicationContext.getBean( "userManager#" + beanId, UserManager.class ) ); 086 } 087 } 088 catch ( RepositoryAdminException e ) 089 { 090 throw new AuthenticationException( e.getMessage(), e ); 091 } 092 } 093 094 095 public AuthenticationResult authenticate( AuthenticationDataSource ds ) 096 throws AuthenticationException, AccountLockedException, MustChangePasswordException 097 { 098 boolean authenticationSuccess = false; 099 String username = null; 100 Exception resultException = null; 101 PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) ds; 102 List<AuthenticationFailureCause> authnResultErrors = new ArrayList<AuthenticationFailureCause>(); 103 104 for ( UserManager userManager : userManagers ) 105 { 106 try 107 { 108 log.debug( "Authenticate: {} with userManager: {}", source, userManager.getId() ); 109 User user = userManager.findUser( source.getUsername() ); 110 username = user.getUsername(); 111 112 if ( user.isLocked() ) 113 { 114 //throw new AccountLockedException( "Account " + source.getUsername() + " is locked.", user ); 115 AccountLockedException e = 116 new AccountLockedException( "Account " + source.getUsername() + " is locked.", user ); 117 log.warn( "{}", e.getMessage() ); 118 resultException = e; 119 authnResultErrors.add( 120 new AuthenticationFailureCause( AuthenticationConstants.AUTHN_LOCKED_USER_EXCEPTION, 121 e.getMessage() ) ); 122 } 123 124 if ( user.isPasswordChangeRequired() && source.isEnforcePasswordChange() ) 125 { 126 //throw new MustChangePasswordException( "Password expired.", user ); 127 MustChangePasswordException e = new MustChangePasswordException( "Password expired.", user ); 128 log.warn( "{}", e.getMessage() ); 129 resultException = e; 130 authnResultErrors.add( 131 new AuthenticationFailureCause( AuthenticationConstants.AUTHN_MUST_CHANGE_PASSWORD_EXCEPTION, 132 e.getMessage() ) ); 133 } 134 135 PasswordEncoder encoder = securityPolicy.getPasswordEncoder(); 136 log.debug( "PasswordEncoder: {}", encoder.getClass().getName() ); 137 138 boolean isPasswordValid = encoder.isPasswordValid( user.getEncodedPassword(), source.getPassword() ); 139 if ( isPasswordValid ) 140 { 141 log.debug( "User {} provided a valid password", source.getUsername() ); 142 143 try 144 { 145 securityPolicy.extensionPasswordExpiration( user ); 146 147 authenticationSuccess = true; 148 149 //REDBACK-151 do not make unnessesary updates to the user object 150 if ( user.getCountFailedLoginAttempts() > 0 ) 151 { 152 user.setCountFailedLoginAttempts( 0 ); 153 if ( !userManager.isReadOnly() ) 154 { 155 userManager.updateUser( user ); 156 } 157 } 158 159 return new AuthenticationResult( true, source.getUsername(), null ); 160 } 161 catch ( MustChangePasswordException e ) 162 { 163 user.setPasswordChangeRequired( true ); 164 //throw e; 165 resultException = e; 166 authnResultErrors.add( new AuthenticationFailureCause( 167 AuthenticationConstants.AUTHN_MUST_CHANGE_PASSWORD_EXCEPTION, e.getMessage() ).user( user ) ); 168 } 169 } 170 else 171 { 172 log.warn( "Password is Invalid for user {} and userManager '{}'.", source.getUsername(), 173 userManager.getId() ); 174 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER, 175 "Password is Invalid for user " 176 + source.getUsername() + "." ).user( user ) ); 177 178 try 179 { 180 181 securityPolicy.extensionExcessiveLoginAttempts( user ); 182 183 } 184 finally 185 { 186 if ( !userManager.isReadOnly() ) 187 { 188 userManager.updateUser( user ); 189 } 190 } 191 192 //return new AuthenticationResult( false, source.getUsername(), null, authnResultExceptionsMap ); 193 } 194 } 195 catch ( UserNotFoundException e ) 196 { 197 log.warn( "Login for user {} and userManager {} failed. user not found.", source.getUsername(), 198 userManager.getId() ); 199 resultException = e; 200 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER, 201 "Login for user " + source.getUsername() 202 + " failed. user not found." ) ); 203 } 204 catch ( Exception e ) 205 { 206 log.warn( "Login for user {} and userManager {} failed, message: {}", source.getUsername(), 207 userManager.getId(), e.getMessage() ); 208 resultException = e; 209 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_RUNTIME_EXCEPTION, 210 "Login for user " + source.getUsername() 211 + " failed, message: " + e.getMessage() ) ); 212 } 213 } 214 return new AuthenticationResult( authenticationSuccess, username, resultException, authnResultErrors ); 215 } 216 217 public boolean supportsDataSource( AuthenticationDataSource source ) 218 { 219 return ( source instanceof PasswordBasedAuthenticationDataSource ); 220 } 221 222 public String getId() 223 { 224 return "ArchivaUserManagerAuthenticator"; 225 } 226 }