dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #26920
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 13326: implemented user account expiration
------------------------------------------------------------
revno: 13326
committer: Morten Olav Hansen <mortenoh@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2013-12-19 12:50:58 +0100
message:
implemented user account expiration
added:
dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/useraccount/expired.js
dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/useraccount/expired.vm
dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/security/CustomExceptionMappingAuthenticationFailureHandler.java
dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/useraccount/action/ExpiredAccountAction.java
modified:
dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java
dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml
dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml
dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java 2013-08-23 16:00:30 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/AccountController.java 2013-12-19 11:50:58 +0000
@@ -28,13 +28,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import java.util.Collection;
-import java.util.HashSet;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -65,6 +59,17 @@
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
/**
* @author Lars Helge Overland
*/
@@ -73,35 +78,37 @@
public class AccountController
{
private static final Log log = LogFactory.getLog( AccountController.class );
-
+
private static final String RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/verify";
protected static final String PUB_KEY = "6LcM6tcSAAAAANwYsFp--0SYtcnze_WdYn8XwMMk";
private static final String KEY = "6LcM6tcSAAAAAFnHo1f3lLstk3rZv3EVinNROfRq";
private static final String TRUE = "true";
private static final String SPLIT = "\n";
private static final int MAX_LENGTH = 80;
-
+
@Autowired
private RestTemplate restTemplate;
-
+
@Autowired
private UserService userService;
-
+
@Autowired
private AuthenticationManager authenticationManager;
-
+
@Autowired
private ConfigurationService configurationService;
-
+
@Autowired
private PasswordManager passwordManager;
-
+
@Autowired
private SecurityService securityService;
-
+
@Autowired
private SystemSettingManager systemSettingManager;
-
+
+ private ObjectMapper objectMapper = new ObjectMapper();
+
@RequestMapping( value = "/recovery", method = RequestMethod.POST, produces = ContextUtils.CONTENT_TYPE_TEXT )
public @ResponseBody String recoverAccount(
@RequestParam String username,
@@ -109,15 +116,15 @@
HttpServletResponse response )
{
String rootPath = ContextUtils.getContextPath( request );
-
+
if ( !systemSettingManager.accountRecoveryEnabled() )
{
response.setStatus( HttpServletResponse.SC_CONFLICT );
return "Account recovery is not enabled";
}
-
+
boolean recover = securityService.sendRestoreMessage( username, rootPath );
-
+
if ( !recover )
{
response.setStatus( HttpServletResponse.SC_CONFLICT );
@@ -125,11 +132,11 @@
}
log.info( "Recovery message sent for user: " + username );
-
+
response.setStatus( HttpServletResponse.SC_OK );
return "Recovery message sent";
}
-
+
@RequestMapping( value = "/restore", method = RequestMethod.POST, produces = ContextUtils.CONTENT_TYPE_TEXT )
public @ResponseBody String restoreAccount(
@RequestParam String username,
@@ -137,42 +144,42 @@
@RequestParam String code,
@RequestParam String password,
HttpServletRequest request,
- HttpServletResponse response )
+ HttpServletResponse response )
{
if ( !systemSettingManager.accountRecoveryEnabled() )
{
response.setStatus( HttpServletResponse.SC_CONFLICT );
return "Account recovery is not enabled";
}
-
+
if ( password == null || !ValidationUtils.passwordIsValid( password ) )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Password is not specified or invalid";
}
-
+
if ( password.trim().equals( username.trim() ) )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Password cannot be equal to username";
}
-
+
boolean restore = securityService.restore( username, token, code, password );
-
+
if ( !restore )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Account could not be restored";
- }
+ }
log.info( "Account restored for user: " + username );
-
+
response.setStatus( HttpServletResponse.SC_OK );
return "Account restored";
}
-
+
@RequestMapping( method = RequestMethod.POST, produces = ContextUtils.CONTENT_TYPE_TEXT )
- public @ResponseBody String createAccount(
+ public @ResponseBody String createAccount(
@RequestParam String username,
@RequestParam String firstName,
@RequestParam String surname,
@@ -186,17 +193,17 @@
HttpServletResponse response )
{
boolean allowed = configurationService.getConfiguration().selfRegistrationAllowed();
-
+
if ( !allowed )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "User self registration is not allowed";
}
-
+
// ---------------------------------------------------------------------
// Trim input
// ---------------------------------------------------------------------
-
+
username = StringUtils.trimToNull( username );
firstName = StringUtils.trimToNull( firstName );
surname = StringUtils.trimToNull( surname );
@@ -210,21 +217,21 @@
// ---------------------------------------------------------------------
// Validate input, return 400 if invalid
// ---------------------------------------------------------------------
-
+
if ( username == null || username.trim().length() > MAX_LENGTH )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "User name is not specified or invalid";
}
-
+
UserCredentials credentials = userService.getUserCredentialsByUsername( username );
-
+
if ( credentials != null )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "User name is alread taken";
}
-
+
if ( firstName == null || firstName.trim().length() > MAX_LENGTH )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
@@ -242,19 +249,19 @@
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Password is not specified or invalid";
}
-
+
if ( password.trim().equals( username.trim() ) )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Password cannot be equal to username";
}
-
+
if ( email == null || !ValidationUtils.emailIsValid( email ) )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Email is not specified or invalid";
}
-
+
if ( phoneNumber == null || phoneNumber.trim().length() > 30 )
{
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
@@ -278,11 +285,11 @@
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "Recaptcha response must be specified";
}
-
+
// ---------------------------------------------------------------------
// Check result from API, return 500 if not
// ---------------------------------------------------------------------
-
+
String[] results = checkRecaptcha( KEY, request.getRemoteAddr(), recapChallenge, recapResponse );
if ( results == null || results.length == 0 )
@@ -294,10 +301,10 @@
// ---------------------------------------------------------------------
// Check if verification was successful, return 400 if not
// ---------------------------------------------------------------------
-
+
if ( !TRUE.equalsIgnoreCase( results[0] ) )
- {
- log.info( "Recaptcha failed with code: " + ( results.length > 0 ? results[1] : "" ) );
+ {
+ log.info( "Recaptcha failed with code: " + (results.length > 0 ? results[1] : "") );
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
return "The characters you entered did not match the word verification, try again";
@@ -306,10 +313,10 @@
// ---------------------------------------------------------------------
// Create and save user, return 201
// ---------------------------------------------------------------------
-
+
UserAuthorityGroup userRole = configurationService.getConfiguration().getSelfRegistrationRole();
OrganisationUnit orgUnit = configurationService.getConfiguration().getSelfRegistrationOrgUnit();
-
+
User user = new User();
user.setFirstName( firstName );
user.setSurname( surname );
@@ -317,38 +324,84 @@
user.setPhoneNumber( phoneNumber );
user.setEmployer( employer );
user.getOrganisationUnits().add( orgUnit );
-
+
credentials = new UserCredentials();
credentials.setUsername( username );
credentials.setPassword( passwordManager.encodePassword( username, password ) );
credentials.setSelfRegistered( true );
credentials.setUser( user );
credentials.getUserAuthorityGroups().add( userRole );
-
+
user.setUserCredentials( credentials );
-
+
userService.addUser( user );
userService.addUserCredentials( credentials );
authenticate( username, password, userRole, request );
-
+
log.info( "Created user with username: " + username );
-
+
response.setStatus( HttpServletResponse.SC_CREATED );
return "Account created";
}
-
+
+ @RequestMapping( method = RequestMethod.PUT, produces = ContextUtils.CONTENT_TYPE_TEXT )
+ public @ResponseBody String updatePassword(
+ @RequestParam String oldPassword,
+ @RequestParam String password,
+ HttpServletRequest request,
+ HttpServletResponse response ) throws IOException
+ {
+ String username = (String) request.getSession().getAttribute( "username" );
+ UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+
+ Map<String, String> result = new HashMap<String, String>();
+ result.put( "status", "OK" );
+
+ if ( userService.credentialsNonExpired( credentials ) )
+ {
+ response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+ result.put( "status", "NON_EXPIRED" );
+ result.put( "message", "Account is not expired, redirecting to login." );
+
+ return objectMapper.writeValueAsString( result );
+ }
+
+ String oldPasswordEncoded = passwordManager.encodePassword( username, oldPassword );
+
+ if ( !credentials.getPassword().equals( oldPasswordEncoded ) )
+ {
+ response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
+ result.put( "status", "NON_MATCHING_PASSWORD" );
+ result.put( "message", "Old password is wrong, please correct and try again." );
+
+ return objectMapper.writeValueAsString( result );
+ }
+
+ String passwordEncoded = passwordManager.encodePassword( username, password );
+
+ credentials.setPassword( passwordEncoded );
+ credentials.setPasswordLastUpdated( new Date() );
+ userService.updateUserCredentials( credentials );
+
+ authenticate( username, password, getAuthorities( credentials.getUserAuthorityGroups() ), request );
+
+ result.put( "message", "Account was updated." );
+
+ return objectMapper.writeValueAsString( result );
+ }
+
@RequestMapping( value = "/username", method = RequestMethod.GET, produces = ContextUtils.CONTENT_TYPE_JSON )
public @ResponseBody String validateUserName( @RequestParam String username )
{
boolean valid = username != null && userService.getUserCredentialsByUsername( username ) == null;
-
+
// Custom code required because of our hacked jQuery validation
-
+
return valid ? "{ \"response\": \"success\", \"message\": \"\" }" :
"{ \"response\": \"error\", \"message\": \"Username is already taken\" }";
}
-
+
// ---------------------------------------------------------------------
// Supportive methods
// ---------------------------------------------------------------------
@@ -356,7 +409,7 @@
private String[] checkRecaptcha( String privateKey, String remoteIp, String challenge, String response )
{
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
-
+
params.add( "privatekey", privateKey );
params.add( "remoteip", remoteIp );
params.add( "challenge", challenge );
@@ -365,33 +418,59 @@
String result = restTemplate.postForObject( RECAPTCHA_VERIFY_URL, params, String.class );
log.info( "Recaptcha result: " + result );
-
+
return result != null ? result.split( SPLIT ) : null;
}
-
+
+ private void authenticate( String username, String rawPassword, Collection<GrantedAuthority> authorities, HttpServletRequest request )
+ {
+ UsernamePasswordAuthenticationToken token =
+ new UsernamePasswordAuthenticationToken( username, rawPassword, authorities );
+
+ Authentication auth = authenticationManager.authenticate( token );
+
+ SecurityContextHolder.getContext().setAuthentication( auth );
+
+ HttpSession session = request.getSession();
+
+ session.setAttribute( "SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext() );
+ }
+
private void authenticate( String username, String rawPassword, UserAuthorityGroup userRole, HttpServletRequest request )
{
- UsernamePasswordAuthenticationToken token =
+ UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken( username, rawPassword, getAuthorities( userRole ) );
Authentication auth = authenticationManager.authenticate( token );
-
+
SecurityContextHolder.getContext().setAuthentication( auth );
HttpSession session = request.getSession();
-
+
session.setAttribute( "SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext() );
}
-
+
+ private Collection<GrantedAuthority> getAuthorities( Set<UserAuthorityGroup> userRoles )
+ {
+ Collection<GrantedAuthority> auths = new HashSet<GrantedAuthority>();
+
+ for ( UserAuthorityGroup userRole : userRoles )
+ {
+ auths.addAll( getAuthorities( userRole ) );
+ }
+
+ return auths;
+ }
+
private Collection<GrantedAuthority> getAuthorities( UserAuthorityGroup userRole )
{
Collection<GrantedAuthority> auths = new HashSet<GrantedAuthority>();
-
+
for ( String auth : userRole.getAuthorities() )
{
auths.add( new SimpleGrantedAuthority( auth ) );
}
-
+
return auths;
}
}
=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/useraccount/expired.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/useraccount/expired.js 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/useraccount/expired.js 2013-12-19 11:50:58 +0000
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2004-2013, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var validationRules = {
+ rules: {
+ oldPassword: {
+ required: true
+ },
+ password: {
+ required: true,
+ rangelength: [ 8, 80 ],
+ password: true,
+ notequalto : "#oldPassword"
+ },
+ retypePassword: {
+ required: true,
+ equalTo: "#password"
+ }
+ }
+};
+
+$( document ).ready( function() {
+ $( "#accountForm" ).validate( {
+ rules: validationRules.rules,
+ submitHandler: accountSubmitHandler,
+ errorPlacement: function( error, element ) {
+ element.parent( "td" ).append( "<br>" ).append( error );
+ }
+ } );
+} );
+
+function accountSubmitHandler()
+{
+ $( "#submitButton" ).attr( "disabled", "disabled" );
+
+ $.ajax( {
+ url: '../../api/account',
+ data: $( "#accountForm" ).serialize(),
+ type: 'put',
+ success: function( data ) {
+ window.location.href = "../../dhis-web-commons-about/redirect.action";
+ },
+ error: function( jqXHR, textStatus, errorThrown ) {
+ var data = JSON.parse(jqXHR.responseText);
+
+ if( data.status === 'NON_EXPIRED' ) {
+ window.location.href = "login.action";
+ }
+
+ $( "#messageSpan" ).show().text( data.message );
+ $( "#submitButton" ).removeAttr( "disabled" );
+ }
+ } );
+}
=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/useraccount/expired.vm'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/useraccount/expired.vm 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/useraccount/expired.vm 2013-12-19 11:50:58 +0000
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>$encoder.htmlEncode( $applicationTitle )</title>
+ <script type="text/javascript" src="../javascripts/jQuery/jquery.min.js"></script>
+ <script type="text/javascript" src="../javascripts/jQuery/jquery.validate.js"></script>
+ <script type="text/javascript" src="../javascripts/jQuery/jquery.validate.ext.js"></script>
+ <script type="text/javascript" src="../javascripts/useraccount/expired.js"></script>
+ <script type="text/javascript" src="../i18nJavaScript.action"></script>
+ <link type="text/css" rel="stylesheet" href="../css/account.css">
+</head>
+<body>
+
+<div id="accountHeader"></div>
+
+<div id="accountContainer">
+
+<div id="bannerArea"><a href="http://dhis2.org"><img src="../security/logo_front.png" style="border:none"></a></div>
+
+<div id="accountInput">
+
+<h3><span id="create_new_account">$i18n.getString( "change_password" )</span></h3>
+
+<form id="accountForm">
+
+<table>
+ <tr>
+ <td><label id="label_username" for="username">$i18n.getString( "user_name" )</label></td>
+ <td><input type="text" id="username" name="username" value="$username" disabled autocomplete}="off"></td>
+ </tr>
+ <tr>
+ <td><label id="label_oldpassword" for="oldPassword">$i18n.getString( "old_password" )</label></td>
+ <td><input type="password" id="oldPassword" name="oldPassword" autocomplete="off" placeholder="$i18n.getString( 'password_hint' )"></td>
+ </tr>
+ <tr>
+ <td><label id="label_password" for="password">$i18n.getString( "new_password" )</label></td>
+ <td><input type="password" id="password" name="password" autocomplete="off" placeholder="$i18n.getString( 'password_hint' )"></td>
+ </tr>
+ <tr>
+ <td><label id="label_retypePassword" for="retypePassword">$i18n.getString( "confirm_password" )</label></td>
+ <td><input type="password" id="retypePassword" name="retypePassword" autocomplete="off"></td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td><label id="messageSpan" class="error" style="display:none"></label></td>
+ </tr>
+
+ <tr>
+ <td></td>
+ <td><input id="submitButton" type="submit" value="$i18n.getString( 'save' )" style="width:10em"></td>
+ </tr>
+</table>
+
+</form>
+
+</div>
+
+</div>
+
+</body>
+</html>
=== added file 'dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/security/CustomExceptionMappingAuthenticationFailureHandler.java'
--- dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/security/CustomExceptionMappingAuthenticationFailureHandler.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/security/CustomExceptionMappingAuthenticationFailureHandler.java 2013-12-19 11:50:58 +0000
@@ -0,0 +1,51 @@
+package org.hisp.dhis.security;
+
+/*
+ * Copyright (c) 2004-2013, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class CustomExceptionMappingAuthenticationFailureHandler extends ExceptionMappingAuthenticationFailureHandler
+{
+ @Override
+ public void onAuthenticationFailure( HttpServletRequest request, HttpServletResponse response, AuthenticationException exception ) throws IOException, ServletException
+ {
+ request.getSession().setAttribute( "username", request.getParameter( "j_username" ) );
+
+ super.onAuthenticationFailure( request, response, exception );
+ }
+}
=== added file 'dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/useraccount/action/ExpiredAccountAction.java'
--- dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/useraccount/action/ExpiredAccountAction.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/java/org/hisp/dhis/useraccount/action/ExpiredAccountAction.java 2013-12-19 11:50:58 +0000
@@ -0,0 +1,84 @@
+package org.hisp.dhis.useraccount.action;
+
+/*
+ * Copyright (c) 2004-2013, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of the HISP project nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import com.opensymphony.xwork2.Action;
+import org.apache.struts2.ServletActionContext;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class ExpiredAccountAction implements Action
+{
+ // -------------------------------------------------------------------------
+ // Dependencies
+ // -------------------------------------------------------------------------
+
+ private UserService userService;
+
+ @Autowired
+ public void setUserService( UserService userService )
+ {
+ this.userService = userService;
+ }
+
+ // -------------------------------------------------------------------------
+ // Getters and Setters
+ // -------------------------------------------------------------------------
+
+ private String username;
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ // -------------------------------------------------------------------------
+ // Action Impl
+ // -------------------------------------------------------------------------
+
+ @Override
+ public String execute() throws Exception
+ {
+ username = (String) ServletActionContext.getRequest().getSession().getAttribute( "username" );
+
+ UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+
+ // check that the user is actually expired
+ if ( credentials != null && !userService.credentialsNonExpired( credentials ) )
+ {
+ return SUCCESS;
+ }
+
+ return ERROR;
+ }
+}
=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml 2013-12-18 08:26:45 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/beans.xml 2013-12-19 11:50:58 +0000
@@ -551,6 +551,9 @@
<!-- User Account -->
+ <bean id="org.hisp.dhis.useraccount.action.ExpiredAccountAction" class="org.hisp.dhis.useraccount.action.ExpiredAccountAction"
+ scope="prototype" />
+
<bean id="org.hisp.dhis.useraccount.action.GetCurrentUserAction" class="org.hisp.dhis.useraccount.action.GetCurrentUserAction"
scope="prototype">
<property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml 2013-12-18 15:28:59 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml 2013-12-19 11:50:58 +0000
@@ -45,10 +45,10 @@
</sec:http>
<bean id="securityExceptionTranslationHandler"
- class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
+ class="org.hisp.dhis.security.CustomExceptionMappingAuthenticationFailureHandler">
<property name="exceptionMappings">
<props>
- <prop key="org.springframework.security.authentication.CredentialsExpiredException">/dhis-web-commons/security/login.action?expired=true</prop>
+ <prop key="org.springframework.security.authentication.CredentialsExpiredException">/dhis-web-commons/security/expired.action</prop>
</props>
</property>
<property name="defaultFailureUrl" value="/dhis-web-commons/security/login.action?failed=true" />
=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml 2013-12-18 10:48:39 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/dhis-web-commons.xml 2013-12-19 11:50:58 +0000
@@ -135,6 +135,11 @@
<result name="error" type="redirect">login.action</result>
</action>
+ <action name="expired" class="org.hisp.dhis.useraccount.action.ExpiredAccountAction">
+ <result name="success" type="velocity">/dhis-web-commons/useraccount/expired.vm</result>
+ <result name="error" type="redirect">login.action</result>
+ </action>
+
<action name="recovery" class="org.hisp.dhis.useraccount.action.IsAccountRecoveryAllowedAction">
<result name="success" type="velocity">/dhis-web-commons/useraccount/recovery.vm</result>
<result name="error" type="redirect">login.action</result>