Created
January 12, 2016 20:40
-
-
Save bholagabbar/b4022f9e4bfcf75b33d6 to your computer and use it in GitHub Desktop.
LUI-45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This Source Code Form is subject to the terms of the Mozilla Public License, | |
* v. 2.0. If a copy of the MPL was not distributed with this file, You can | |
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under | |
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license. | |
* | |
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS | |
* graphic logo is a trademark of OpenMRS Inc. | |
*/ | |
package org.openmrs.web.controller; | |
import java.util.*; | |
import javax.servlet.ServletException; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import javax.servlet.http.HttpSession; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.openmrs.User; | |
import org.openmrs.api.context.Context; | |
import org.openmrs.api.context.UserContext; | |
import org.openmrs.util.PrivilegeConstants; | |
import org.openmrs.web.WebConstants; | |
import org.springframework.validation.BindException; | |
import org.springframework.web.servlet.ModelAndView; | |
import org.springframework.web.servlet.mvc.SimpleFormController; | |
import org.springframework.web.servlet.view.RedirectView; | |
/** | |
* Controls the forgotten password form Initially a form with just a username box is shown Then a | |
* box for the answer to the secret question is shown | |
*/ | |
public class ForgotPasswordFormController extends SimpleFormController { | |
/** Logger for this class and subclasses */ | |
protected static final Log log = LogFactory.getLog(ForgotPasswordFormController.class); | |
/** | |
* Not used with the forgot password form controller. | |
* | |
* @see org.springframework.web.servlet.mvc.AbstractFormController#formBackingObject(javax.servlet.http.HttpServletRequest) | |
*/ | |
protected Object formBackingObject(HttpServletRequest request) throws ServletException { | |
return ""; | |
} | |
/** | |
* The mapping from user's IP address to the number of attempts at logging in from that IP | |
*/ | |
private Map<String, Integer> loginAttemptsByIP = new HashMap<String, Integer>(); | |
/** | |
* The mapping from user's IP address to the time that they were locked out | |
*/ | |
private Map<String, Date> lockoutDateByIP = new HashMap<String, Date>(); | |
/** | |
* This takes in the form twice. The first time when the input their username and the second | |
* when they submit both their username and their secret answer | |
* | |
* @see org.springframework.web.servlet.mvc.SimpleFormController#onSubmit(javax.servlet.http.HttpServletRequest, | |
* javax.servlet.http.HttpServletResponse, java.lang.Object, | |
* org.springframework.validation.BindException) | |
*/ | |
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object obj, | |
BindException errors) throws Exception { | |
HttpSession httpSession = request.getSession(); | |
String username = request.getParameter("uname"); | |
String ipAddress = request.getRemoteAddr(); | |
Integer forgotPasswordAttempts = loginAttemptsByIP.get(ipAddress); | |
if (forgotPasswordAttempts == null) { | |
forgotPasswordAttempts = 1; | |
} | |
boolean lockedOut = false; | |
if (forgotPasswordAttempts > 5) { | |
lockedOut = true; | |
Date lockedOutTime = lockoutDateByIP.get(ipAddress); | |
if (lockedOutTime != null && System.currentTimeMillis() - lockedOutTime.getTime() > 300000) { | |
lockedOut = false; | |
forgotPasswordAttempts = 0; | |
lockoutDateByIP.put(ipAddress, null); | |
} else { | |
// they haven't been locked out before, or they're trying again | |
// within the time limit. Set the locked-out date to right now | |
lockoutDateByIP.put(ipAddress, new Date()); | |
} | |
} | |
if (lockedOut) { | |
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "auth.forgotPassword.tooManyAttempts"); | |
} else { | |
// if the previous logic didn't determine that the user should be locked out, | |
// then continue with the check | |
forgotPasswordAttempts++; | |
String secretAnswer = request.getParameter("secretAnswer"); | |
if (secretAnswer == null) { | |
// if they are seeing this page for the first time | |
User user = null; | |
try { | |
Context.addProxyPrivilege(PrivilegeConstants.GET_USERS); | |
// only search if they actually put in a username | |
if (username != null && username.length() > 0) { | |
user = Context.getUserService().getUserByUsername(username); | |
} | |
} | |
finally { | |
Context.removeProxyPrivilege(PrivilegeConstants.GET_USERS); | |
} | |
String secretQuestion = null; | |
if(user != null) { | |
secretQuestion = Context.getUserService().getSecretQuestion(user); | |
} | |
if (user == null) { | |
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "auth.question.fill"); | |
request.setAttribute("secretQuestion", getRandomFakeSecretQuestion(username)); | |
} else if (secretQuestion == null || secretQuestion.equals("")) { | |
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "auth.question.empty"); | |
} else { | |
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "auth.question.fill"); | |
request.setAttribute("secretQuestion", secretQuestion); | |
// reset the forgotPasswordAttempts because they have a right user. | |
// they will now have 5 more chances to get the question right | |
forgotPasswordAttempts = 0; | |
} | |
} else { | |
// if they've filled in the username and entered their secret answer | |
User user = null; | |
try { | |
Context.addProxyPrivilege(PrivilegeConstants.GET_USERS); | |
user = Context.getUserService().getUserByUsername(username); | |
} | |
finally { | |
Context.removeProxyPrivilege(PrivilegeConstants.GET_USERS); | |
} | |
String secretQuestion= null; | |
if(user != null) { | |
secretQuestion = Context.getUserService().getSecretQuestion(user); | |
} | |
// check the secret question again in case the user got here "illegally" | |
if (user == null) { | |
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "auth.answer.invalid"); | |
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "auth.question.fill"); | |
request.setAttribute("secretQuestion", getRandomFakeSecretQuestion(username)); | |
} else if (secretQuestion == null || secretQuestion.equals("")) { | |
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "auth.question.empty"); | |
} else if (secretQuestion != null && Context.getUserService().isSecretAnswer(user, secretAnswer)) { | |
StringBuilder randomPassword = new StringBuilder(); | |
ArrayList<Character> buildRandomPassword = new ArrayList<Character>(); | |
//Ensure atleast 1 uppercase, 1 lowercase as well as 1 digit in password (Otherwise, exception is thrown) | |
buildRandomPassword.add((char) (65 + (int) Math.abs(Math.random()) % 26)); | |
buildRandomPassword.add((char) (97 + (int) Math.abs(Math.random()) % 26)); | |
buildRandomPassword.add((char) (48 + (int) Math.abs(Math.random()) % 10)); | |
//Build rest of the password | |
for (int i = 3; i < 8; i++) { | |
buildRandomPassword.add((char) (Math.random() * (127 - 48) + 48)); | |
} | |
//Shuffle it | |
Collections.shuffle(buildRandomPassword); | |
//Finally, append it to the string | |
for (char characterInPassword : buildRandomPassword) { | |
randomPassword.append(characterInPassword); | |
} | |
try { | |
Context.addProxyPrivilege(PrivilegeConstants.EDIT_USER_PASSWORDS); | |
Context.getUserService().changePassword(user, randomPassword.toString()); | |
} | |
finally { | |
Context.removeProxyPrivilege(PrivilegeConstants.EDIT_USER_PASSWORDS); | |
} | |
httpSession.setAttribute("resetPassword", randomPassword); | |
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "auth.password.reset"); | |
Context.authenticate(username, randomPassword.toString()); | |
httpSession.setAttribute("loginAttempts", 0); | |
return new ModelAndView(new RedirectView(request.getContextPath() + "/options.form#Change Login Info")); | |
} else { | |
httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "auth.answer.invalid"); | |
httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "auth.question.fill"); | |
request.setAttribute("secretQuestion", secretQuestion); | |
} | |
} | |
} | |
loginAttemptsByIP.put(ipAddress, forgotPasswordAttempts); | |
User testUser = Context.getUserService().getUserByUsername(username); | |
String testSecretQuestion; | |
if (testUser != null) { | |
testSecretQuestion = Context.getUserService().getSecretQuestion(testUser); | |
} else { | |
testSecretQuestion = getRandomFakeSecretQuestion(username); | |
} | |
request.setAttribute("uname", username); | |
request.setAttribute("secretQuestion", testSecretQuestion); | |
return showForm(request, response, errors); | |
} | |
public String getRandomFakeSecretQuestion(String username) { | |
List<String> questions = new ArrayList<String>(); | |
questions.add(Context.getMessageSourceService().getMessage("What is your best friend's name?")); | |
questions.add(Context.getMessageSourceService().getMessage("What is your grandfather's home town?")); | |
questions.add(Context.getMessageSourceService().getMessage("What is your mother's maiden name?")); | |
questions.add(Context.getMessageSourceService().getMessage("What is your favorite band?")); | |
questions.add(Context.getMessageSourceService().getMessage("What is your first pet's name?")); | |
questions.add(Context.getMessageSourceService().getMessage("What is your brother's middle name?")); | |
questions.add(Context.getMessageSourceService().getMessage("Which city were you born in?")); | |
int hashValueForName = username.hashCode(); | |
//Converting this value to something between 0 and 6 | |
if (hashValueForName < 0) { | |
hashValueForName *= -1; | |
} | |
hashValueForName %= 7; | |
//Return random question | |
return questions.get(hashValueForName); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This Source Code Form is subject to the terms of the Mozilla Public License, | |
* v. 2.0. If a copy of the MPL was not distributed with this file, You can | |
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under | |
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license. | |
* | |
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS | |
* graphic logo is a trademark of OpenMRS Inc. | |
*/ | |
package org.openmrs.web.controller; | |
import org.openmrs.User; | |
import org.openmrs.test.BaseContextSensitiveTest; | |
import org.openmrs.web.test.BaseModuleWebContextSensitiveTest; | |
import org.openmrs.web.test.BaseWebContextSensitiveTest; | |
import javax.servlet.http.HttpServletResponse; | |
import org.apache.log4j.Level; | |
import org.apache.log4j.LogManager; | |
import org.apache.log4j.Logger; | |
import org.junit.After; | |
import org.junit.AfterClass; | |
import org.junit.Assert; | |
import org.junit.Before; | |
import org.junit.BeforeClass; | |
import org.junit.Test; | |
import org.openmrs.api.context.Context; | |
import org.openmrs.web.controller.ForgotPasswordFormController; | |
import org.openmrs.web.test.BaseWebContextSensitiveTest; | |
import org.springframework.mock.web.MockHttpServletRequest; | |
import org.springframework.mock.web.MockHttpServletResponse; | |
import org.springframework.web.servlet.ModelAndView; | |
import java.net.BindException; | |
/** | |
* Test the different aspects of | |
* {@link org.openmrs.web.controller.ForgotPasswordFormController} | |
*/ | |
public class ForgotPasswordFormControllerTest extends BaseModuleWebContextSensitiveTest { | |
private static Level initialLogLevel; | |
private static final String CLASS_TO_LOG = "org.openmrs.api.db.hibernate.HibernateUserDAO"; | |
protected static final String TEST_DATA= "org/openmrs/web/controller/include/ForgotPasswordFormControllerTest.xml"; | |
/** | |
* Check to see if the admin's secret question comes back | |
* | |
* @throws Exception | |
*/ | |
@Test | |
public void shouldNotNotFailOnNotFoundUsernameTEST() throws Exception { | |
initializeInMemoryDatabase(); | |
executeDataSet(TEST_DATA); | |
authenticate(); | |
ForgotPasswordFormController controller = new ForgotPasswordFormController(); | |
MockHttpServletRequest request = new MockHttpServletRequest(); | |
request.setParameter("uname", "bruno"); | |
request.setParameter("secretAnswer", "valid secret answer"); | |
request.setMethod("POST"); | |
HttpServletResponse response = new MockHttpServletResponse(); | |
controller.handleRequest(request, response); | |
Assert.assertEquals("what is your name bruno?", request.getAttribute("secretQuestion")); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8'?> | |
<!-- | |
This Source Code Form is subject to the terms of the Mozilla Public License, | |
v. 2.0. If a copy of the MPL was not distributed with this file, You can | |
obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under | |
the terms of the Healthcare Disclaimer located at http://openmrs.org/license. | |
Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS | |
graphic logo is a trademark of OpenMRS Inc. | |
--> | |
<dataset> | |
<users user_id="501" person_id="501" system_id="2-6" username="bruno" password="ff6d1655327d385e11a04e1632b4f33ceb8fafd2" salt="5d32c5fd9fde391c755f1f4dfd5e1d6e3debe6" secret_question="what is your name bruno?" creator="1" date_created="2008-08-15 15:46:47.0" changed_by="1" date_changed="2008-08-15 15:47:07.0" retired="true" retire_reason="Test purposes" uuid="c1d8f5c2-e131-11de-babe-001e378eb67e"/> | |
<users user_id="502" person_id="502" system_id="3-4" username="butch" password="eeeda5c0cc3837151b2d61cfeab54a91fb0c27d" salt="42af4c437a47cd778a54f6564d71b3cd6e8e5ca" secret_question="" creator="1" date_created="2008-08-15 15:57:09.0" changed_by="1" date_changed="2008-08-18 11:51:56.0" retired="false" retire_reason="" uuid="c98a1558-e131-11de-babe-001e378eb67e"/> | |
</dataset> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Have a look at file 2 where I try and execute the tests. The test fails and the error is:
Failed tests: shouldNotNotFailOnNotFoundUsernameTEST(org.openmrs.web.controller.ForgotPasswordFormControllerTest): expected:<[check]> but was:<[What is your grandfather's home town?]>
This means that the user is not being detected and a secret question is being randomly assigned. However, "bruno" is there in the test data. How do I overcome this?