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