Skip to content

Instantly share code, notes, and snippets.

@bholagabbar
Created January 12, 2016 20:40
Show Gist options
  • Save bholagabbar/b4022f9e4bfcf75b33d6 to your computer and use it in GitHub Desktop.
Save bholagabbar/b4022f9e4bfcf75b33d6 to your computer and use it in GitHub Desktop.
LUI-45
/**
* 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 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"));
}
}
<?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>
@bholagabbar
Copy link
Author

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment