Skip to content

Commit

Permalink
Integration tests for forcePasswordChange
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekar authored and Identity Service committed Nov 29, 2016
1 parent 7c6a1eb commit 3d78413
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 48 deletions.
Expand Up @@ -133,6 +133,7 @@ public Authentication authenticate(Authentication req) throws AuthenticationExce
success.setAuthenticationMethods(Collections.singleton("pwd"));

if(user.isPasswordChangeRequired()){
logger.info("Password change required for user: "+user.getEmail());
throw new PasswordChangeRequiredException(success, "User password needs to be changed");
}
publish(new UserAuthenticationSuccessEvent(user, success));
Expand Down
Expand Up @@ -12,11 +12,15 @@
*******************************************************************************/
package org.cloudfoundry.identity.uaa.login;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.account.PasswordConfirmationValidation;
import org.cloudfoundry.identity.uaa.account.ResetPasswordService;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationDetails;
import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.savedrequest.SavedRequest;
Expand All @@ -29,17 +33,29 @@
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.LinkedList;

import static java.util.Optional.ofNullable;
import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE;

@Controller
public class ForcePasswordChangeController {

public static final String FORCE_PASSWORD_EXPIRED_USER = "FORCE_PASSWORD_EXPIRED_USER";
private Log logger = LogFactory.getLog(getClass());

public void setSuccessHandler(AccountSavingAuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
}

@Autowired
@Qualifier("accountSavingAuthenticationSuccessHandler")
private AccountSavingAuthenticationSuccessHandler successHandler;

@Autowired
@Qualifier("resetPasswordService")
private ResetPasswordService resetPasswordService;

@RequestMapping(value="/force_password_change", method= GET)
Expand All @@ -62,9 +78,9 @@ public String handleForcePasswordChange(Model model,
if(session.getAttribute(FORCE_PASSWORD_EXPIRED_USER) == null) {
return "redirect:" + request.getContextPath()+"/login";
}
UaaPrincipal principal = ((UaaAuthentication)session
.getAttribute(FORCE_PASSWORD_EXPIRED_USER))
.getPrincipal();
UaaAuthentication authentication = ((UaaAuthentication)session
.getAttribute(FORCE_PASSWORD_EXPIRED_USER));
UaaPrincipal principal = authentication.getPrincipal();

String email = principal.getEmail();

Expand All @@ -73,12 +89,25 @@ public String handleForcePasswordChange(Model model,
if(!validation.valid()) {
return handleUnprocessableEntity(model, response, email);
}

logger.debug("Processing handleForcePasswordChange for user: "+ email);
resetPasswordService.resetUserPassword(principal.getId(), password);
SecurityContextHolder.getContext().setAuthentication(((UaaAuthentication)session
.getAttribute(FORCE_PASSWORD_EXPIRED_USER)));

SavedRequest savedRequest = (SavedRequest) request.getSession().getAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE);

request.getSession().invalidate();
request.getSession(true);
if (authentication instanceof UaaAuthentication) {
UaaAuthentication uaaAuthentication = (UaaAuthentication)authentication;
authentication = new UaaAuthentication(
uaaAuthentication.getPrincipal(),
new LinkedList<>(uaaAuthentication.getAuthorities()),
new UaaAuthenticationDetails(request)
);
ofNullable(successHandler).ifPresent(handler ->
handler.setSavedAccountOptionCookie(request, response, uaaAuthentication)
);
}
SecurityContextHolder.getContext().setAuthentication(authentication);

if(savedRequest != null) {
return "redirect:" + savedRequest.getRedirectUrl();
} else {
Expand Down
3 changes: 2 additions & 1 deletion server/src/main/resources/login-ui.xml
Expand Up @@ -276,7 +276,8 @@
<property name="logoutRequestMatcher" ref="uiLogoutRequestMatcher"/>
</bean>

<bean name="forcePasswordChangeController" class="org.cloudfoundry.identity.uaa.login.ForcePasswordChangeController"/>
<bean name="forcePasswordChangeController" class="org.cloudfoundry.identity.uaa.login.ForcePasswordChangeController">
</bean>

<bean name="clientRedirectStateCache" class="org.cloudfoundry.identity.uaa.web.UaaSavedRequestCache">
<property name="requestMatcher" ref="uiAuthorizeRequestMatcher"/>
Expand Down
Expand Up @@ -32,12 +32,15 @@ public class ForcePasswordChangeControllerTest extends TestClassNullifier {

private MockMvc mockMvc;
private ResetPasswordService resetPasswordService;
private AccountSavingAuthenticationSuccessHandler successHandler = new AccountSavingAuthenticationSuccessHandler();

@Before
public void setUp() throws Exception {
ForcePasswordChangeController controller = new ForcePasswordChangeController();
resetPasswordService = mock(ResetPasswordService.class);
controller.setResetPasswordService(resetPasswordService);
successHandler = mock(AccountSavingAuthenticationSuccessHandler.class);
controller.setSuccessHandler(successHandler);
mockMvc = MockMvcBuilders
.standaloneSetup(controller)
.setViewResolvers(getResolver())
Expand Down
@@ -0,0 +1,176 @@
/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.integration.feature;

import org.cloudfoundry.identity.uaa.ServerRunning;
import org.cloudfoundry.identity.uaa.account.UserAccountStatus;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;

import java.security.SecureRandom;
import java.util.List;
import java.util.Map;

import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DefaultIntegrationTestConfig.class)
public class ForcedPasswordChangeIT {

@Autowired @Rule
public IntegrationTestRule integrationTestRule;

@Autowired
WebDriver webDriver;

@Autowired
TestClient testClient;

@Autowired
RestTemplate restTemplate;

@Value("${integration.test.base_url}")
String baseUrl;

@Rule
public ServerRunning serverRunning = ServerRunning.isRunning();

private String userId;

private String userEmail;

private String adminAccessToken;

@Before
@After
public void logout_and_clear_cookies() {
try {
webDriver.get(baseUrl + "/logout.do");
}catch (org.openqa.selenium.TimeoutException x) {
//try again - this should not be happening - 20 second timeouts
webDriver.get(baseUrl + "/logout.do");
}
webDriver.manage().deleteAllCookies();
}

@Before
public void setUp() throws Exception {
restTemplate = (RestTemplate)serverRunning.createRestTemplate();
int randomInt = new SecureRandom().nextInt();
adminAccessToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "clients.read clients.write clients.secret clients.admin scim.write scim.read");
userEmail = "user" + randomInt + "@example.com";
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+adminAccessToken);
testClient.createUser(adminAccessToken, userEmail, userEmail, "secr3T", true);
ResponseEntity<Map> response = restTemplate.exchange(baseUrl + "/Users?filter=userName eq '{user-name}'", HttpMethod.GET,
new HttpEntity<>(headers), Map.class, userEmail);
Map results = response.getBody();
assertTrue("There should be more than zero users", (Integer) results.get("totalResults") > 0);
Map firstUser = (Map) ((List) results.get("resources")).get(0);
userId = (String)firstUser.get("id");
}

@After
public void tearDown() throws Exception {
webDriver.get(baseUrl + "/logout.do");
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+adminAccessToken);
restTemplate.exchange(baseUrl + "/Users/{user-id}", HttpMethod.DELETE,
new HttpEntity<>(headers), Object.class, userId);
}

@Test
public void testHandleForceChangingPassword() throws Exception {

navigateToForcePasswordChange();
webDriver.findElement(By.name("password")).sendKeys("newsecr3T");
webDriver.findElement(By.name("password_confirmation")).sendKeys("newsecr3T");
webDriver.findElement(By.xpath("//input[@value='Create new password']")).click();
assertEquals(baseUrl+"/", webDriver.getCurrentUrl());
}

@Test
public void testHandleForcePasswordChangeInvalidConfirmation() throws Exception {

navigateToForcePasswordChange();
webDriver.findElement(By.name("password")).sendKeys("newsecr3T");
webDriver.findElement(By.name("password_confirmation")).sendKeys("invalid");
webDriver.findElement(By.xpath("//input[@value='Create new password']")).click();
assertEquals(baseUrl+"/force_password_change", webDriver.getCurrentUrl());
assertThat(webDriver.findElement(By.cssSelector(".error-message")).getText(),
containsString("Passwords must match and not be empty."));
}

@Test
public void testHandleForcePasswordChangeEmptyConfirmation() throws Exception {

navigateToForcePasswordChange();
webDriver.findElement(By.name("password")).sendKeys("newsecr3T");
webDriver.findElement(By.xpath("//input[@value='Create new password']")).click();
assertEquals(baseUrl+"/force_password_change", webDriver.getCurrentUrl());
assertThat(webDriver.findElement(By.cssSelector(".error-message")).getText(),
containsString("Passwords must match and not be empty."));
}

@Test
public void testRedirectForHandleForcePasswordChange() throws Exception {

updateUserToForcePasswordChange();
webDriver.get(baseUrl+"/profile");
assertEquals(baseUrl+"/login", webDriver.getCurrentUrl());
webDriver.findElement(By.name("username")).sendKeys(userEmail);
webDriver.findElement(By.name("password")).sendKeys("secr3T");
webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();
webDriver.findElement(By.name("password")).sendKeys("newsecr3T");
webDriver.findElement(By.name("password_confirmation")).sendKeys("newsecr3T");
webDriver.findElement(By.xpath("//input[@value='Create new password']")).click();
assertEquals(baseUrl+"/profile", webDriver.getCurrentUrl());
}

private void navigateToForcePasswordChange() {
updateUserToForcePasswordChange();
webDriver.get(baseUrl+"/login");
webDriver.findElement(By.name("username")).sendKeys(userEmail);
webDriver.findElement(By.name("password")).sendKeys("secr3T");
webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();
assertThat(webDriver.findElement(By.cssSelector("h1")).getText(),
containsString("Force Change Password"));
assertEquals(baseUrl+"/force_password_change", webDriver.getCurrentUrl());
}

private void updateUserToForcePasswordChange() {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer "+adminAccessToken);
UserAccountStatus userAccountStatus = new UserAccountStatus();
userAccountStatus.setPasswordChangeRequired(true);
ResponseEntity<UserAccountStatus> response = restTemplate.exchange(baseUrl + "/Users/{user-id}/status", HttpMethod.PATCH, new HttpEntity<UserAccountStatus>(userAccountStatus, headers), UserAccountStatus.class, userId);
response.toString();
}
}

0 comments on commit 3d78413

Please sign in to comment.