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.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}