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