001package 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 021import org.apache.archiva.admin.model.RepositoryAdminException; 022import org.apache.archiva.admin.model.runtime.RedbackRuntimeConfigurationAdmin; 023import org.apache.archiva.redback.authentication.AbstractAuthenticator; 024import org.apache.archiva.redback.authentication.AuthenticationConstants; 025import org.apache.archiva.redback.authentication.AuthenticationDataSource; 026import org.apache.archiva.redback.authentication.AuthenticationException; 027import org.apache.archiva.redback.authentication.AuthenticationFailureCause; 028import org.apache.archiva.redback.authentication.AuthenticationResult; 029import org.apache.archiva.redback.authentication.Authenticator; 030import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource; 031import org.apache.archiva.redback.policy.AccountLockedException; 032import org.apache.archiva.redback.policy.MustChangePasswordException; 033import org.apache.archiva.redback.policy.PasswordEncoder; 034import org.apache.archiva.redback.policy.UserSecurityPolicy; 035import org.apache.archiva.redback.users.User; 036import org.apache.archiva.redback.users.UserManager; 037import org.apache.archiva.redback.users.UserNotFoundException; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.springframework.context.ApplicationContext; 041import org.springframework.stereotype.Service; 042 043import javax.annotation.PostConstruct; 044import javax.inject.Inject; 045import java.util.ArrayList; 046import java.util.List; 047 048/** 049 * @author Olivier Lamy 050 * @since 1.4-M4 051 */ 052@Service("authenticator#archiva") 053public class ArchivaUserManagerAuthenticator 054 extends AbstractAuthenticator 055 implements Authenticator 056{ 057 private Logger log = LoggerFactory.getLogger( getClass() ); 058 059 @Inject 060 private UserSecurityPolicy securityPolicy; 061 062 @Inject 063 private ApplicationContext applicationContext; 064 065 @Inject 066 private RedbackRuntimeConfigurationAdmin redbackRuntimeConfigurationAdmin; 067 068 private List<UserManager> userManagers; 069 070 private boolean valid = false; 071 072 @PostConstruct 073 @Override 074 public void initialize() 075 throws AuthenticationException 076 { 077 try 078 { 079 List<String> userManagerImpls = 080 redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration().getUserManagerImpls(); 081 082 userManagers = new ArrayList<>( userManagerImpls.size() ); 083 084 for ( String beanId : userManagerImpls ) 085 { 086 userManagers.add( applicationContext.getBean( "userManager#" + beanId, UserManager.class ) ); 087 } 088 valid=true; 089 } 090 catch ( RepositoryAdminException e ) 091 { 092 log.error( "Error during repository initialization {}", e.getMessage(), e ); 093 // throw new AuthenticationException( e.getMessage(), e ); 094 } 095 } 096 097 098 @Override 099 public AuthenticationResult authenticate( AuthenticationDataSource ds ) 100 throws AuthenticationException, AccountLockedException, MustChangePasswordException 101 { 102 boolean authenticationSuccess = false; 103 String username = null; 104 Exception resultException = null; 105 PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) ds; 106 List<AuthenticationFailureCause> authnResultErrors = new ArrayList<>(); 107 108 for ( UserManager userManager : userManagers ) 109 { 110 try 111 { 112 log.debug( "Authenticate: {} with userManager: {}", source, userManager.getId() ); 113 User user = userManager.findUser( source.getUsername() ); 114 username = user.getUsername(); 115 116 if ( user.isLocked() ) 117 { 118 //throw new AccountLockedException( "Account " + source.getUsername() + " is locked.", user ); 119 AccountLockedException e = 120 new AccountLockedException( "Account " + source.getUsername() + " is locked.", user ); 121 log.warn( "{}", e.getMessage() ); 122 resultException = e; 123 authnResultErrors.add( 124 new AuthenticationFailureCause( AuthenticationConstants.AUTHN_LOCKED_USER_EXCEPTION, 125 e.getMessage() ) ); 126 } 127 128 if ( user.isPasswordChangeRequired() && source.isEnforcePasswordChange() ) 129 { 130 //throw new MustChangePasswordException( "Password expired.", user ); 131 MustChangePasswordException e = new MustChangePasswordException( "Password expired.", user ); 132 log.warn( "{}", e.getMessage() ); 133 resultException = e; 134 authnResultErrors.add( 135 new AuthenticationFailureCause( AuthenticationConstants.AUTHN_MUST_CHANGE_PASSWORD_EXCEPTION, 136 e.getMessage() ) ); 137 } 138 139 PasswordEncoder encoder = securityPolicy.getPasswordEncoder(); 140 log.debug( "PasswordEncoder: {}", encoder.getClass().getName() ); 141 142 boolean isPasswordValid = encoder.isPasswordValid( user.getEncodedPassword(), source.getPassword() ); 143 if ( isPasswordValid ) 144 { 145 log.debug( "User {} provided a valid password", source.getUsername() ); 146 147 try 148 { 149 securityPolicy.extensionPasswordExpiration( user ); 150 151 authenticationSuccess = true; 152 153 //REDBACK-151 do not make unnessesary updates to the user object 154 if ( user.getCountFailedLoginAttempts() > 0 ) 155 { 156 user.setCountFailedLoginAttempts( 0 ); 157 if ( !userManager.isReadOnly() ) 158 { 159 userManager.updateUser( user ); 160 } 161 } 162 163 return new AuthenticationResult( true, source.getUsername(), null ); 164 } 165 catch ( MustChangePasswordException e ) 166 { 167 user.setPasswordChangeRequired( true ); 168 //throw e; 169 resultException = e; 170 authnResultErrors.add( new AuthenticationFailureCause( 171 AuthenticationConstants.AUTHN_MUST_CHANGE_PASSWORD_EXCEPTION, e.getMessage() ).user( user ) ); 172 } 173 } 174 else 175 { 176 log.warn( "Password is Invalid for user {} and userManager '{}'.", source.getUsername(), 177 userManager.getId() ); 178 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER, 179 "Password is Invalid for user " 180 + source.getUsername() + "." ).user( user ) ); 181 182 try 183 { 184 185 securityPolicy.extensionExcessiveLoginAttempts( user ); 186 187 } 188 finally 189 { 190 if ( !userManager.isReadOnly() ) 191 { 192 userManager.updateUser( user ); 193 } 194 } 195 196 //return new AuthenticationResult( false, source.getUsername(), null, authnResultExceptionsMap ); 197 } 198 } 199 catch ( UserNotFoundException e ) 200 { 201 log.warn( "Login for user {} and userManager {} failed. user not found.", source.getUsername(), 202 userManager.getId() ); 203 resultException = e; 204 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_NO_SUCH_USER, 205 "Login for user " + source.getUsername() 206 + " failed. user not found." ) ); 207 } 208 catch ( Exception e ) 209 { 210 log.warn( "Login for user {} and userManager {} failed, message: {}", source.getUsername(), 211 userManager.getId(), e.getMessage() ); 212 e.printStackTrace(); 213 resultException = e; 214 authnResultErrors.add( new AuthenticationFailureCause( AuthenticationConstants.AUTHN_RUNTIME_EXCEPTION, 215 "Login for user " + source.getUsername() 216 + " failed, message: " + e.getMessage() ) ); 217 } 218 } 219 return new AuthenticationResult( authenticationSuccess, username, resultException, authnResultErrors ); 220 } 221 222 @Override 223 public boolean supportsDataSource( AuthenticationDataSource source ) 224 { 225 return ( source instanceof PasswordBasedAuthenticationDataSource ); 226 } 227 228 @Override 229 public String getId() 230 { 231 return "ArchivaUserManagerAuthenticator"; 232 } 233 234 public boolean isValid() { 235 return valid; 236 } 237}