/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved.
* <p/>
* 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.
* <p/>
* 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.mock.password;
import org.cloudfoundry.identity.uaa.account.PasswordChangeRequest;
import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf;
import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.utils;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.TEXT_HTML;
import static org.springframework.http.MediaType.TEXT_HTML_VALUE;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class PasswordChangeEndpointMockMvcTests extends InjectedMockContextTest {
private RandomValueStringGenerator generator = new RandomValueStringGenerator();
private String passwordWriteToken;
private String adminToken;
@Before
public void setUp() throws Exception {
adminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "adminsecret",
"clients.read clients.write clients.secret scim.write clients.admin");
String clientId = generator.generate().toLowerCase();
String clientSecret = generator.generate().toLowerCase();
BaseClientDetails clientDetails = new BaseClientDetails(clientId, null, null, "client_credentials", "password.write");
clientDetails.setClientSecret(clientSecret);
utils().createClient(getMockMvc(), adminToken, clientDetails);
passwordWriteToken = testClient.getClientCredentialsOAuthAccessToken(clientId, clientSecret,"password.write");
}
@Test
public void changePassword_withInvalidPassword_returnsErrorJson() throws Exception {
ScimUser user = createUser();
PasswordChangeRequest request = new PasswordChangeRequest();
request.setOldPassword("secr3T");
request.setPassword(new RandomValueStringGenerator(260).generate());
getMockMvc().perform(put("/Users/" + user.getId() + "/password").header("Authorization", "Bearer " + passwordWriteToken)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(request)))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.error").value("invalid_password"))
.andExpect(jsonPath("$.message").value("Password must be no more than 255 characters in length."));
}
@Test
public void changePassword_NewPasswordSameAsOld_ReturnsUnprocessableEntityWithJsonError() throws Exception {
ScimUser user = createUser();
PasswordChangeRequest request = new PasswordChangeRequest();
request.setOldPassword("secr3T");
request.setPassword("secr3T");
getMockMvc().perform(put("/Users/" + user.getId() + "/password").header("Authorization", "Bearer " + passwordWriteToken)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(request)))
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.error").value("invalid_password"))
.andExpect(jsonPath("$.message").value("Your new password cannot be the same as the old password."));
}
@Test
public void changePassword_SuccessfullyChangePassword() throws Exception {
ScimUser user = createUser();
PasswordChangeRequest request = new PasswordChangeRequest();
request.setOldPassword("secr3T");
request.setPassword("n3wAw3som3Passwd");
MockHttpServletRequestBuilder put = put("/Users/" + user.getId() +"/password")
.header("Authorization", "Bearer " + passwordWriteToken)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(request))
.accept(APPLICATION_JSON);
getMockMvc().perform(put)
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("ok"))
.andExpect(jsonPath("$.message").value("password updated"));
}
@Test
public void changePassword_Resets_Session() throws Exception {
ScimUser user = createUser();
MockHttpSession session = new MockHttpSession();
session.invalidate();
MockHttpSession afterLoginSession = (MockHttpSession) getMockMvc().perform(post("/login.do")
.with(cookieCsrf())
.session(session)
.accept(TEXT_HTML_VALUE)
.param("username", user.getUserName())
.param("password", "secr3T"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"))
.andReturn().getRequest().getSession(false);
assertNotNull(afterLoginSession);
assertNotNull(afterLoginSession.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
MockHttpSession afterPasswordChange = (MockHttpSession) getMockMvc().perform(post("/change_password.do")
.session(afterLoginSession)
.with(csrf())
.accept(TEXT_HTML_VALUE)
.param("current_password", "secr3T")
.param("new_password", "secr3T1")
.param("confirm_password", "secr3T1"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("profile"))
.andReturn().getRequest().getSession(false);
assertTrue(afterLoginSession.isInvalid());
assertNotNull(afterPasswordChange);
assertNotNull(afterPasswordChange.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
assertNotSame(afterLoginSession, afterPasswordChange);
}
@Test
public void changePassword_Resets_All_Sessions() throws Exception {
ScimUser user = createUser();
MockHttpSession session = new MockHttpSession();
MockHttpSession afterLoginSessionA = (MockHttpSession) getMockMvc().perform(post("/login.do")
.with(cookieCsrf())
.session(session)
.accept(TEXT_HTML_VALUE)
.param("username", user.getUserName())
.param("password", "secr3T"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"))
.andReturn().getRequest().getSession(false);
session = new MockHttpSession();
MockHttpSession afterLoginSessionB = (MockHttpSession) getMockMvc().perform(post("/login.do")
.with(cookieCsrf())
.session(session)
.accept(TEXT_HTML_VALUE)
.param("username", user.getUserName())
.param("password", "secr3T"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"))
.andReturn().getRequest().getSession(false);
assertNotNull(afterLoginSessionA.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
assertNotNull(afterLoginSessionB.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
getMockMvc().perform(get("/profile").session(afterLoginSessionB))
.andExpect(status().isOk());
Thread.sleep(1000 - (System.currentTimeMillis() % 1000) + 1);
MockHttpSession afterPasswordChange = (MockHttpSession) getMockMvc().perform(post("/change_password.do")
.session(afterLoginSessionA)
.with(csrf())
.accept(TEXT_HTML_VALUE)
.param("current_password", "secr3T")
.param("new_password", "secr3T1")
.param("confirm_password", "secr3T1"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("profile"))
.andReturn().getRequest().getSession(false);
assertTrue(afterLoginSessionA.isInvalid());
assertNotNull(afterPasswordChange);
assertNotNull(afterPasswordChange.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY));
assertNotSame(afterLoginSessionA, afterPasswordChange);
getMockMvc().perform(
get("/profile")
.session(afterLoginSessionB)
.accept(TEXT_HTML))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login"));
}
private ScimUser createUser() throws Exception {
String id = generator.generate();
ScimUser user = new ScimUser(id, id + "user@example.com", "name", "familyname");
user.addEmail(id + "user@example.com");
user.setPassword("secr3T");
return utils().createUser(getMockMvc(), adminToken, user);
}
}