dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #19762
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 8751: Server side implementation of user account restore function
------------------------------------------------------------
revno: 8751
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Mon 2012-10-29 17:34:57 +0300
message:
Server side implementation of user account restore function
added:
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultSecurityService.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/SecurityService.java
dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message1.vm
dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message2.vm
dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security/
dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security/SecurityServiceTest.java
modified:
dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/CodeGenerator.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java
dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml
dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.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-api/src/main/java/org/hisp/dhis/common/CodeGenerator.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/CodeGenerator.java 2011-11-25 18:32:06 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/CodeGenerator.java 2012-10-29 14:34:57 +0000
@@ -43,21 +43,34 @@
public static final int CODESIZE = 11;
/**
- * Generates a pseudo random string using the allowed characters
+ * Generates a pseudo random string using the allowed characters. Code is
+ * 11 characters long.
*
- * @return the Code
+ * @param codeSize the number of characters in the code.
+ * @return the code.
*/
public static String generateCode()
{
+ return generateCode( CODESIZE );
+ }
+
+ /**
+ * Generates a pseudo random string using the allowed characters.
+ *
+ * @param codeSize the number of characters in the code.
+ * @return the code.
+ */
+ public static String generateCode( int codeSize )
+ {
// Using the system default algorithm and seed
SecureRandom sr = new SecureRandom();
- char[] randomChars = new char[CODESIZE];
+ char[] randomChars = new char[codeSize];
// first char should be a letter
randomChars[0] = letters.charAt( sr.nextInt( letters.length() ) );
- for ( int i = 1; i < CODESIZE; ++i )
+ for ( int i = 1; i < codeSize; ++i )
{
randomChars[i] = allowedChars.charAt( sr.nextInt( NUMBER_OF_CODEPOINTS ) );
}
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java 2012-10-07 19:07:53 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java 2012-10-29 14:34:57 +0000
@@ -27,24 +27,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hisp.dhis.common.BaseIdentifiableObject;
+import org.hisp.dhis.common.Dxf2Namespace;
+import org.hisp.dhis.common.IdentifiableObjectUtils;
+import org.hisp.dhis.common.view.DetailedView;
+import org.hisp.dhis.common.view.ExportView;
+import org.hisp.dhis.dataset.DataSet;
+
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
-import org.hisp.dhis.common.BaseIdentifiableObject;
-import org.hisp.dhis.common.Dxf2Namespace;
-import org.hisp.dhis.common.IdentifiableObjectUtils;
-import org.hisp.dhis.common.view.DetailedView;
-import org.hisp.dhis.common.view.ExportView;
-import org.hisp.dhis.dataset.DataSet;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
/**
* @author Nguyen Hong Duc
@@ -71,13 +72,34 @@
private String username;
/**
- * Required.
+ * Required. Will be stored as a hash.
*/
private String password;
+ /**
+ * Set of user roles.
+ */
private Set<UserAuthorityGroup> userAuthorityGroups = new HashSet<UserAuthorityGroup>();
+ /**
+ * Date of the user's last login.
+ */
private Date lastLogin;
+
+ /**
+ * The token used for a user account restore. Will be stored as a hash.
+ */
+ private String restoreToken;
+
+ /**
+ * The code used for a user account restore. Will be stored as a hash.
+ */
+ private String restoreCode;
+
+ /**
+ * The timestamp representing when the restore window expires.
+ */
+ private Date restoreExpiry;
// -------------------------------------------------------------------------
// Logic
@@ -200,6 +222,39 @@
return user != null ? user.getName() : username;
}
+ /**
+ * Tests whether the given input arguments can perform a valid restore of the
+ * user account for these credentials. Returns false if any of the input arguments
+ * are null, or any of the properties on the credentials are null. Returns false
+ * if the expiry date arguement is after the expiry date of the credentials.
+ * Returns false if any of the given token or code arguments are not equal to
+ * the respective properties the the credentials. Returns true otherwise.
+ *
+ * @param token the restore token.
+ * @param code the restore code.
+ * @param expiry the expiry date.
+ * @return true or false.
+ */
+ public boolean canRestore( String token, String code, Date date )
+ {
+ if ( this.restoreToken == null || this.restoreCode == null || this.restoreExpiry == null )
+ {
+ return false;
+ }
+
+ if ( token == null || code == null || date == null )
+ {
+ return false;
+ }
+
+ if ( date.after( this.restoreExpiry ) )
+ {
+ return false;
+ }
+
+ return token.equals( this.restoreToken ) && code.equals( this.restoreCode );
+ }
+
// -------------------------------------------------------------------------
// hashCode and equals
// -------------------------------------------------------------------------
@@ -313,4 +368,34 @@
{
this.lastLogin = lastLogin;
}
+
+ public String getRestoreToken()
+ {
+ return restoreToken;
+ }
+
+ public void setRestoreToken( String restoreToken )
+ {
+ this.restoreToken = restoreToken;
+ }
+
+ public String getRestoreCode()
+ {
+ return restoreCode;
+ }
+
+ public void setRestoreCode( String restoreCode )
+ {
+ this.restoreCode = restoreCode;
+ }
+
+ public Date getRestoreExpiry()
+ {
+ return restoreExpiry;
+ }
+
+ public void setRestoreExpiry( Date restoreExpiry )
+ {
+ this.restoreExpiry = restoreExpiry;
+ }
}
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultSecurityService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultSecurityService.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultSecurityService.java 2012-10-29 14:34:57 +0000
@@ -0,0 +1,190 @@
+package org.hisp.dhis.security;
+
+/*
+ * Copyright (c) 2004-2012, 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 java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.hisp.dhis.common.CodeGenerator;
+import org.hisp.dhis.message.MessageSender;
+import org.hisp.dhis.period.Cal;
+import org.hisp.dhis.system.velocity.VelocityManager;
+import org.hisp.dhis.user.User;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserService;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class DefaultSecurityService
+ implements SecurityService
+{
+ private static final String RESTORE_PATH = "/dhis-web-commons/security/restore.action";
+
+ // -------------------------------------------------------------------------
+ // Dependencies
+ // -------------------------------------------------------------------------
+
+ private PasswordManager passwordManager;
+
+ public void setPasswordManager( PasswordManager passwordManager )
+ {
+ this.passwordManager = passwordManager;
+ }
+
+ private MessageSender emailMessageSender;
+
+ public void setEmailMessageSender( MessageSender emailMessageSender )
+ {
+ this.emailMessageSender = emailMessageSender;
+ }
+
+ private UserService userService;
+
+ public void setUserService( UserService userService )
+ {
+ this.userService = userService;
+ }
+
+ // -------------------------------------------------------------------------
+ // SecurityService implementation
+ // -------------------------------------------------------------------------
+
+ public boolean sendRestoreMessage( String username, String rootPath )
+ {
+ if ( username == null || rootPath == null )
+ {
+ return false;
+ }
+
+ UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+
+ if ( credentials == null )
+ {
+ return false;
+ }
+
+ // TODO check if email is configured
+
+ String[] result = initRestore( credentials );
+
+ Set<User> users = new HashSet<User>();
+ users.add( credentials.getUser() );
+
+ Map<String, String> vars = new HashMap<String, String>();
+ vars.put( "rootPath", rootPath );
+ vars.put( "restorePath", rootPath + RESTORE_PATH );
+ vars.put( "token", result[0] );
+ vars.put( "code", result[1] );
+ vars.put( "username", username );
+
+ String text1 = new VelocityManager().render( vars, "restore_message1.vm" );
+ String text2 = new VelocityManager().render( vars, "restore_message2.vm" );
+
+ emailMessageSender.sendMessage( "User account restore confirmation (message 1 of 2)", text1, null, users );
+ emailMessageSender.sendMessage( "User account restore confirmation (message 2 of 2)", text2, null, users );
+
+ return true;
+ }
+
+ public String[] initRestore( UserCredentials credentials )
+ {
+ String token = CodeGenerator.generateCode( 40 );
+ String code = CodeGenerator.generateCode( 15 );
+
+ String hashedToken = passwordManager.encodePassword( credentials.getUsername(), token );
+ String hashedCode = passwordManager.encodePassword( credentials.getUsername(), code );
+
+ Date expiry = new Cal().now().add( Calendar.HOUR_OF_DAY, 1 ).time();
+
+ credentials.setRestoreToken( hashedToken );
+ credentials.setRestoreCode( hashedCode );
+ credentials.setRestoreExpiry( expiry );
+
+ userService.updateUserCredentials( credentials );
+
+ String[] result = { token, code };
+ return result;
+ }
+
+ public boolean restore( String username, String token, String code, String newPassword )
+ {
+ if ( username == null || token == null || code == null || newPassword == null )
+ {
+ return false;
+ }
+
+ UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+
+ if ( credentials == null )
+ {
+ return false;
+ }
+
+ token = passwordManager.encodePassword( username, token );
+ code = passwordManager.encodePassword( username, code );
+
+ Date date = new Cal().now().time();
+
+ if ( !credentials.canRestore( token, code, date ) )
+ {
+ return false;
+ }
+
+ newPassword = passwordManager.encodePassword( username, newPassword );
+
+ credentials.setPassword( newPassword );
+
+ userService.updateUserCredentials( credentials );
+
+ return true;
+ }
+
+ public boolean verifyToken( String username, String token )
+ {
+ if ( username == null || token == null )
+ {
+ return false;
+ }
+
+ UserCredentials credentials = userService.getUserCredentialsByUsername( username );
+
+ if ( credentials == null || credentials.getRestoreToken() == null )
+ {
+ return false;
+ }
+
+ token = passwordManager.encodePassword( username, token );
+
+ return credentials.getRestoreToken().equals( token );
+ }
+}
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/SecurityService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/SecurityService.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/SecurityService.java 2012-10-29 14:34:57 +0000
@@ -0,0 +1,87 @@
+package org.hisp.dhis.security;
+
+/*
+ * Copyright (c) 2004-2012, 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.hisp.dhis.user.UserCredentials;
+
+/**
+ * @author Lars Helge Overland
+ */
+public interface SecurityService
+{
+ /**
+ * Will invoke the initiateRestore method and dispatch email messages with
+ * restore information to the user.
+ *
+ * @param username the user name of the user to send restore messages.
+ * @param rootPath the root path of the request.
+ * @return false if any of the arguments are null or if the user credentials
+ * identified by the user name does not exist, true otherwise.
+ */
+ boolean sendRestoreMessage( String username, String rootPath );
+
+ /**
+ * Will populate the restoreToken and restoreCode property of the given
+ * credentials with a hashed version of auto-generated values. Will set the
+ * restoreExpiry property with a date time one hour from now. Changes will be
+ * persisted.
+ *
+ * @param credentials the user credentials.
+ * @return an array where index 0 is the clear-text token and index 1 the
+ * clear-text code.
+ */
+ String[] initRestore( UserCredentials credentials );
+
+ /**
+ * Tests whether the given token and code are valid for the given user name.
+ * If true, it will update the user credentials identified by the given user
+ * name with the new password. In order to succeed, the given token and code
+ * must match the ones on the credentials, and the current date must be before
+ * the expiry date time of the credentials.
+ *
+ * @param username the user name.
+ * @param token the token.
+ * @param code the code.
+ * @param newPassword the proposed new password.
+ * @return true or false.
+ */
+ boolean restore( String username, String token, String code, String newPassword );
+
+ /**
+ * Tests whether the given token in combination with the given user name is
+ * valid, i.e. whether the hashed version of the token matches the one on the
+ * user credentials identified by the given user name.
+ *
+ * @param username the user name.
+ * @param token the token.
+ * @return false if any of the arguments are null or if the user credentials
+ * identified by the user name does not exist, true if the arguments
+ * are valid.
+ */
+ boolean verifyToken( String username, String token );
+}
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml 2012-03-05 14:43:22 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml 2012-10-29 14:34:57 +0000
@@ -16,6 +16,12 @@
<property name="usernameSaltSource" ref="usernameSaltSource" />
</bean>
+ <bean id="org.hisp.dhis.security.SecurityService" class="org.hisp.dhis.security.DefaultSecurityService">
+ <property name="passwordManager" ref="org.hisp.dhis.security.PasswordManager" />
+ <property name="emailMessageSender" ref="emailMessageSender" />
+ <property name="userService" ref="org.hisp.dhis.user.UserService" />
+ </bean>
+
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="userDetailsService">
<sec:password-encoder hash="md5">
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml 2011-06-11 19:51:41 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml 2012-10-29 14:34:57 +0000
@@ -30,7 +30,13 @@
<many-to-many column="userroleid" class="org.hisp.dhis.user.UserAuthorityGroup" foreign-key="fk_userrolemembers_userroleid" />
</set>
- <property name="lastLogin"/>
+ <property name="lastLogin" />
+
+ <property name="restoreToken" />
+
+ <property name="restoreCode" />
+
+ <property name="restoreExpiry" type="timestamp" />
</class>
</hibernate-mapping>
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message1.vm'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message1.vm 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message1.vm 2012-10-29 14:34:57 +0000
@@ -0,0 +1,7 @@
+Someone, probably you, have asked us to restore your useraccount at ${basePath}.
+You have been sent two emails. This is the first email of those two. Please follow
+the link below this text. In the next step you will be asked to enter a code
+which is sent to you in the other email. You must complete the restore within 1
+hour.
+
+${restorePath}?username=${username}&token=${token}
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message2.vm'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message2.vm 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/restore_message2.vm 2012-10-29 14:34:57 +0000
@@ -0,0 +1,7 @@
+Someone, probably you, have asked us to restore your useraccount at ${basePath}.
+You have been sent two emails. This is the second email of those two. Please
+read the first email and follow the instructions. If you already have done that,
+please use the code below to complete the account restore form. You must complete
+the restore within 1 hour.
+
+${code}
=== added directory 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security'
=== added file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security/SecurityServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security/SecurityServiceTest.java 1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/security/SecurityServiceTest.java 2012-10-29 14:34:57 +0000
@@ -0,0 +1,94 @@
+package org.hisp.dhis.security;
+
+/*
+ * Copyright (c) 2004-2012, 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 static junit.framework.Assert.*;
+
+import org.hisp.dhis.DhisSpringTest;
+import org.hisp.dhis.user.User;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserService;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class SecurityServiceTest
+ extends DhisSpringTest
+{
+ private UserCredentials credentials;
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private PasswordManager passwordManager;
+
+ @Autowired
+ private SecurityService securityService;
+
+ @Override
+ public void setUpTest()
+ {
+ credentials = new UserCredentials();
+ credentials.setUsername( "johndoe" );
+ credentials.setPassword( "" );
+
+ User user = createUser( 'A' );
+ user.setUserCredentials( credentials );
+ credentials.setUser( user );
+ userService.addUserCredentials( credentials );
+ }
+
+ @Test
+ public void testRestore()
+ {
+ String[] result = securityService.initRestore( credentials );
+
+ assertNotNull( result[0] );
+ assertNotNull( result[1] );
+ assertNotNull( credentials.getRestoreToken() );
+ assertNotNull( credentials.getRestoreCode() );
+ assertNotNull( credentials.getRestoreExpiry() );
+
+ boolean verified = securityService.verifyToken( credentials.getUsername(), result[0] );
+
+ assertTrue( verified );
+
+ String password = "NewPassword1";
+
+ boolean restored = securityService.restore( credentials.getUsername(), result[0], result[1], password );
+
+ assertTrue( restored );
+
+ String hashedPassword = passwordManager.encodePassword( credentials.getUsername(), password );
+
+ assertEquals( hashedPassword, credentials.getPassword() );
+ }
+}