1 package org.apache.archiva.redback.struts2.action;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.redback.keys.AuthenticationKey;
23 import org.apache.archiva.redback.policy.AccountLockedException;
24 import org.apache.archiva.redback.users.User;
25 import org.apache.struts2.ServletActionContext;
26 import org.apache.archiva.redback.authentication.AuthenticationConstants;
27 import org.apache.archiva.redback.authentication.AuthenticationDataSource;
28 import org.apache.archiva.redback.authentication.AuthenticationException;
29 import org.apache.archiva.redback.authentication.AuthenticationResult;
30 import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
31 import org.apache.archiva.redback.authentication.TokenBasedAuthenticationDataSource;
32 import org.apache.archiva.redback.configuration.UserConfiguration;
33 import org.apache.archiva.redback.keys.KeyManagerException;
34 import org.apache.archiva.redback.keys.KeyNotFoundException;
35 import org.apache.archiva.redback.policy.MustChangePasswordException;
36 import org.apache.archiva.redback.system.SecuritySession;
37 import org.apache.archiva.redback.system.SecuritySystem;
38 import org.apache.archiva.redback.users.UserNotFoundException;
39 import org.codehaus.plexus.util.StringUtils;
40 import org.apache.archiva.redback.integration.interceptor.SecureActionBundle;
41 import org.apache.archiva.redback.integration.interceptor.SecureActionException;
42 import org.apache.archiva.redback.integration.util.AutoLoginCookies;
43 import org.springframework.context.annotation.Scope;
44 import org.springframework.stereotype.Controller;
46 import javax.inject.Inject;
47 import java.util.Arrays;
48 import java.util.Date;
53 * @author Jesse McConnell <jmcconnell@apache.org>
54 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
57 @Controller( "redback-login" )
59 public class LoginAction
60 extends AbstractSecurityAction
61 implements CancellableAction
63 private static final String LOGIN_SUCCESS = "security-login-success";
65 private static final String PASSWORD_CHANGE = "security-must-change-password";
67 private static final String ACCOUNT_LOCKED = "security-login-locked";
69 // ------------------------------------------------------------------
70 // Component Requirements
71 // ------------------------------------------------------------------
77 protected SecuritySystem securitySystem;
79 private String username;
81 private String password;
83 private String validateMe;
85 private String resetPassword;
87 private boolean rememberMe;
93 private AutoLoginCookies autologinCookies;
99 private UserConfiguration config;
101 // ------------------------------------------------------------------
102 // Action Entry Points - (aka Names)
103 // ------------------------------------------------------------------
111 * 1) check if this is a validation authentication action
112 * 2) check if this is a reset password authentication action
113 * 3) sets up a password based authentication and passes on to webLogin()
117 public String login()
119 if ( StringUtils.isNotEmpty( validateMe ) )
121 // Process a login / validate request.
125 if ( StringUtils.isNotEmpty( resetPassword ) )
127 // Process a login / reset password request.
128 return resetPassword();
131 if ( StringUtils.isEmpty( username ) )
133 addFieldError( "username", getText( "username.required" ) );
137 PasswordBasedAuthenticationDataSource authdatasource = new PasswordBasedAuthenticationDataSource();
138 authdatasource.setPrincipal( username );
139 authdatasource.setPassword( password );
141 return webLogin( authdatasource, rememberMe );
145 * 1) sets up a token based authentication
146 * 2) forces a password change requirement to the user
147 * 3) passes on to webLogin()
151 public String resetPassword()
153 if ( StringUtils.isEmpty( resetPassword ) )
155 addActionError( getText( "reset.password.missing" ) );
161 AuthenticationKey authkey = securitySystem.getKeyManager().findKey( resetPassword );
163 User user = securitySystem.getUserManager().findUser( authkey.getForPrincipal() );
165 user.setPasswordChangeRequired( true );
166 user.setEncodedPassword( "" );
168 TokenBasedAuthenticationDataSource authsource = new TokenBasedAuthenticationDataSource();
169 authsource.setPrincipal( user.getPrincipal().toString() );
170 authsource.setToken( authkey.getKey() );
171 authsource.setEnforcePasswordChange( false );
173 securitySystem.getUserManager().updateUser( user );
175 AuditEvent event = new AuditEvent( getText( "log.password.change" ) );
176 event.setAffectedUser( username );
179 return webLogin( authsource, false );
181 catch ( KeyNotFoundException e )
183 log.info( "Invalid key requested: {}", resetPassword );
184 addActionError( getText( "cannot.find.key" ) );
187 catch ( KeyManagerException e )
189 addActionError( getText( "cannot.find.key.at.the.moment" ) );
190 log.warn( "Key Manager error: ", e );
193 catch ( UserNotFoundException e )
195 addActionError( getText( "cannot.find.user" ) );
201 * 1) sets up a token based authentication
202 * 2) forces a password change requirement to the user
203 * 3) passes on to webLogin()
207 public String validated()
209 if ( StringUtils.isEmpty( validateMe ) )
211 addActionError( getText( "validation.failure.key.missing" ) );
217 AuthenticationKey authkey = securitySystem.getKeyManager().findKey( validateMe );
219 User user = securitySystem.getUserManager().findUser( authkey.getForPrincipal() );
221 user.setValidated( true );
222 user.setLocked( false );
223 user.setPasswordChangeRequired( true );
224 user.setEncodedPassword( "" );
226 TokenBasedAuthenticationDataSource authsource = new TokenBasedAuthenticationDataSource();
227 authsource.setPrincipal( user.getPrincipal().toString() );
228 authsource.setToken( authkey.getKey() );
229 authsource.setEnforcePasswordChange( false );
231 securitySystem.getUserManager().updateUser( user );
232 String currentUser = getCurrentUser();
234 AuditEvent event = new AuditEvent( getText( "log.account.validation" ) );
235 event.setAffectedUser( username );
236 event.setCurrentUser( currentUser );
239 return webLogin( authsource, false );
241 catch ( KeyNotFoundException e )
243 log.info( "Invalid key requested: {}", validateMe );
244 addActionError( getText( "cannot.find.key" ) );
247 catch ( KeyManagerException e )
249 addActionError( getText( "cannot.find.key.at.the.momment" ) );
252 catch ( UserNotFoundException e )
254 addActionError( getText( "cannot.find.user" ) );
259 public String cancel()
264 public String getUsername()
269 public void setUsername( String username )
271 this.username = username;
274 public String getPassword()
279 public void setPassword( String password )
281 this.password = password;
284 public String getValidateMe()
289 public void setValidateMe( String validateMe )
291 this.validateMe = validateMe;
294 public SecureActionBundle initSecureActionBundle()
295 throws SecureActionException
297 return SecureActionBundle.OPEN;
300 public String getResetPassword()
302 return resetPassword;
305 public void setResetPassword( String resetPassword )
307 this.resetPassword = resetPassword;
310 public boolean isRememberMe()
315 public void setRememberMe( boolean rememberMe )
317 this.rememberMe = rememberMe;
322 * 1) attempts to authentication based on the passed in data source
323 * 2) if successful sets cookies and returns LOGIN_SUCCESS
324 * 3) if failure then check what kinda failure and return error
326 * @param authdatasource
330 private String webLogin( AuthenticationDataSource authdatasource, boolean rememberMe )
332 // An attempt should log out your authentication tokens first!
333 setAuthTokens( null );
335 clearErrorsAndMessages();
337 // TODO: share this section with AutoLoginInterceptor
340 SecuritySession securitySession = securitySystem.authenticate( authdatasource );
342 if ( securitySession.isAuthenticated() )
344 // Success! Create tokens.
345 setAuthTokens( securitySession );
347 if ( securitySystem.getPolicy().getUserValidationSettings().isEmailValidationRequired() )
349 if ( !securitySession.getUser().getUsername().equals(
350 config.getString( "redback.default.admin" ) ) )
352 if ( !securitySession.getUser().isValidated() )
354 setAuthTokens( null );
355 // NOTE: this text is the same as incorrect.username.password to avoid exposing actual account existence
356 addActionError( getText( "account.validation.required" ) );
362 setCookies( authdatasource, rememberMe );
364 AuditEvent event = new AuditEvent( getText( "log.login.success" ) );
365 event.setAffectedUser( username );
368 User user = securitySession.getUser();
369 user.setLastLoginDate( new Date() );
370 securitySystem.getUserManager().updateUser( user );
372 if ( StringUtils.isNotEmpty( validateMe ) )
376 //REDBACK-146: delete key after validating so user won't be able to use it the second time around
377 securitySystem.getKeyManager().deleteKey( validateMe );
379 catch ( KeyManagerException e )
381 addActionError( getText( "cannot.find.key.at.the.momment" ) );
386 return LOGIN_SUCCESS;
390 log.debug( "Login Action failed against principal : {}",
391 securitySession.getAuthenticationResult().getPrincipal(),
392 securitySession.getAuthenticationResult().getException() );
394 AuthenticationResult result = securitySession.getAuthenticationResult();
395 if ( result.getExceptionsMap() != null && !result.getExceptionsMap().isEmpty() )
397 if ( result.getExceptionsMap().get( AuthenticationConstants.AUTHN_NO_SUCH_USER ) != null )
399 addActionError( getText( "incorrect.username.password" ) );
403 addActionError( getText( "authentication.failed" ) );
408 addActionError( getText( "authentication.failed" ) );
411 AuditEvent event = new AuditEvent( getText( "log.login.fail" ) );
412 event.setAffectedUser( username );
418 catch ( AuthenticationException ae )
420 addActionError( getText( "authentication.exception", Arrays.asList( (Object) ae.getMessage() ) ) );
423 catch ( UserNotFoundException ue )
426 getText( "user.not.found.exception", Arrays.asList( (Object) username, ue.getMessage() ) ) );
428 AuditEvent event = new AuditEvent( getText( "log.login.fail" ) );
429 event.setAffectedUser( username );
433 catch ( AccountLockedException e )
435 addActionError( getText( "account.locked" ) );
437 AuditEvent event = new AuditEvent( getText( "log.login.fail.locked" ) );
438 event.setAffectedUser( username );
440 return ACCOUNT_LOCKED;
442 catch ( MustChangePasswordException e )
444 // TODO: preferably we would not set the cookies for this "partial" login state
445 setCookies( authdatasource, rememberMe );
447 AuditEvent event = new AuditEvent( getText( "log.login.fail.locked" ) );
448 event.setAffectedUser( username );
450 return PASSWORD_CHANGE;
454 private void setCookies( AuthenticationDataSource authdatasource, boolean rememberMe )
458 autologinCookies.setRememberMeCookie( authdatasource.getPrincipal(), ServletActionContext.getResponse(),
459 ServletActionContext.getRequest() );
461 autologinCookies.setSignonCookie( authdatasource.getPrincipal(), ServletActionContext.getResponse(),
462 ServletActionContext.getRequest() );