public static void updateUserToForcePasswordChange(RestTemplate restTemplate, String baseUrl, String adminToken, String userId, String zoneId) { HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Bearer " + adminToken); if (StringUtils.hasText(zoneId)) { headers.add(IdentityZoneSwitchingFilter.HEADER, zoneId); } 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(); }
@Test public void testPatchUserStatus() { ScimUser user = new ScimUser(null, "uname", "gname", "fname"); user.setPassword("password"); user.addEmail("test@example.org"); ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse()); UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setLocked(false); UserAccountStatus updatedStatus = endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); assertEquals(false, updatedStatus.getLocked()); }
@RequestMapping(value = "/Users/{userId}/status", method = RequestMethod.PATCH) public UserAccountStatus updateAccountStatus(@RequestBody UserAccountStatus status, @PathVariable String userId) { ScimUser user = scimUserProvisioning.retrieve(userId, IdentityZoneHolder.get().getId()); if(!user.getOrigin().equals(OriginKeys.UAA)) { throw new IllegalArgumentException("Can only manage users from the internal user store."); } if(status.getLocked() != null && status.getLocked()) { throw new IllegalArgumentException("Cannot set user account to locked. User accounts only become locked through exceeding the allowed failed login attempts."); } if(status.isPasswordChangeRequired() != null && !status.isPasswordChangeRequired()) { throw new IllegalArgumentException("The requirement that this user change their password cannot be removed via API."); } if(status.getLocked() != null && !status.getLocked()) { publish(new UserAccountUnlockedEvent(user)); } if(status.isPasswordChangeRequired() != null && status.isPasswordChangeRequired()) { scimUserProvisioning.updatePasswordChangeRequired(userId, true, IdentityZoneHolder.get().getId()); } return status; }
@Test(expected = IllegalArgumentException.class) public void testPatchUserInvalidStatus() { ScimUser user = new ScimUser(null, "uname", "gname", "fname"); user.setPassword("password"); user.addEmail("test@example.org"); ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse()); UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setLocked(true); endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); }
@Test void testTryMultipleStatusUpdatesWithInvalidLock() throws Exception { ScimUser user = createUser(uaaAdminToken); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(true); alteredAccountStatus.setLocked(true); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isBadRequest()); assertFalse(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); attemptLogin(user) .andExpect(redirectedUrl("/")); }
@Test void testAccountStatusEmptyPatchDoesNotUnlock() throws Exception { ScimUser userToLockout = createUser(uaaAdminToken); attemptUnsuccessfulLogin(5, userToLockout.getUserName(), ""); updateAccountStatus(userToLockout, new UserAccountStatus()) .andExpect(status().isOk()) .andExpect(content().json("{}")); attemptLogin(userToLockout) .andExpect(redirectedUrl("/login?error=account_locked")); }
@Test void testUpdateStatusCannotLock() throws Exception { ScimUser user = createUser(uaaAdminToken); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setLocked(true); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isBadRequest()); attemptLogin(user) .andExpect(redirectedUrl("/")); }
@Test void testTryMultipleStatusUpdatesWithInvalidRemovalOfPasswordChange() throws Exception { ScimUser user = createUser(uaaAdminToken); attemptUnsuccessfulLogin(5, user.getUserName(), ""); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(false); alteredAccountStatus.setLocked(false); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isBadRequest()); assertFalse(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); attemptLogin(user) .andExpect(redirectedUrl("/login?error=account_locked")); }
@Test(expected = IllegalArgumentException.class) public void testPatchUserStatusWithPasswordExpiryExternalUser() { ScimUser user = new ScimUser(null, "uname", "gname", "fname"); user.addEmail("test@example.org"); user.setOrigin("NOT_UAA"); ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse()); UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setPasswordChangeRequired(true); endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); }
@Test void testUnlockAccountWhenNotLocked() throws Exception { ScimUser userToLockout = createUser(uaaAdminToken); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) .andExpect(content().json(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) .andExpect(redirectedUrl("/")); }
@Test(expected = IllegalArgumentException.class) public void testPatchUserStatusWithPasswordExpiryFalse() { ScimUser user = new ScimUser(null, "uname", "gname", "fname"); user.setPassword("password"); user.addEmail("test@example.org"); ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse()); UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setPasswordChangeRequired(false); endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); }
@Test void testUnlockAccount() throws Exception { ScimUser userToLockout = createUser(uaaAdminToken); attemptUnsuccessfulLogin(5, userToLockout.getUserName(), ""); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setLocked(false); updateAccountStatus(userToLockout, alteredAccountStatus) .andExpect(status().isOk()) .andExpect(content().json(JsonUtils.writeValueAsString(alteredAccountStatus))); attemptLogin(userToLockout) .andExpect(redirectedUrl("/")); }
@Test void testForcePasswordExpireAccountInvalid() throws Exception { ScimUser user = createUser(uaaAdminToken); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(false); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isBadRequest()); assertFalse(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); }
@Test void test_status_unlock_user() throws Exception { UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setLocked(false); String jsonStatus = JsonUtils.writeValueAsString(alteredAccountStatus); mockMvc .perform( RestDocumentationRequestBuilders.patch("/Users/{userId}/status", user.getId()) .header("Authorization", "Bearer " + scimWriteToken) .accept(APPLICATION_JSON) .contentType(APPLICATION_JSON) .content(jsonStatus) ) .andExpect(status().isOk()) .andExpect(content().json(jsonStatus)) .andDo( document("{ClassName}/{methodName}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters(parameterWithName("userId").description(userIdDescription)), requestHeaders( headerWithName("Authorization").description("Access token with `scim.write`, `uaa.account_status.write`, or `uaa.admin` required"), IDENTITY_ZONE_ID_HEADER, IDENTITY_ZONE_SUBDOMAIN_HEADER ), requestFields(fieldWithPath("locked").optional(null).description("Set to `false` in order to unlock the user when they have been locked out according to the password lock-out policy. Setting to `true` will produce an error, as the user cannot be locked out via the API.").type(BOOLEAN)), responseFields(fieldWithPath("locked").description("The `locked` value given in the request.").type(BOOLEAN)) ) ); }
@Test void testForcePasswordExpireAccountExternalUser() throws Exception { ScimUser user = createUser(uaaAdminToken); user.setOrigin("NOT_UAA"); updateUser(uaaAdminToken, HttpStatus.OK.value(), user); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(true); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isBadRequest()); assertFalse(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); }
@BeforeEach void setup() throws Exception { UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setPasswordChangeRequired(true); String jsonStatus = JsonUtils.writeValueAsString(userAccountStatus); mockMvc.perform( patch("/Users/" + user.getId() + "/status") .header("Authorization", "Bearer " + token) .accept(APPLICATION_JSON) .contentType(APPLICATION_JSON) .content(jsonStatus)) .andExpect(status().isOk()); }
@Test void testForcePasswordChange() throws Exception { ScimUser user = createUser(uaaAdminToken); assertFalse(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(true); updateAccountStatus(user, alteredAccountStatus) .andExpect(status().isOk()) .andExpect(content().json(JsonUtils.writeValueAsString(alteredAccountStatus))); assertTrue(usersRepository.checkPasswordChangeIndividuallyRequired(user.getId(), IdentityZoneHolder.get().getId())); }
@Test void submit_password_change_when_not_authenticated() throws Exception { UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setPasswordChangeRequired(true); String jsonStatus = JsonUtils.writeValueAsString(userAccountStatus); mockMvc.perform( patch("/Users/" + user.getId() + "/status") .header("Authorization", "Bearer " + token) .accept(APPLICATION_JSON) .contentType(APPLICATION_JSON) .content(jsonStatus)) .andExpect(status().isOk()); MockHttpServletRequestBuilder validPost = post("/force_password_change") .param("password", "test") .param("password_confirmation", "test"); validPost.with(cookieCsrf()); mockMvc.perform(validPost) .andExpect(status().isFound()) .andExpect(redirectedUrl(("http://localhost/login"))); }
@Test void test_status_password_expire_user() throws Exception { UserAccountStatus alteredAccountStatus = new UserAccountStatus(); alteredAccountStatus.setPasswordChangeRequired(true); String jsonStatus = JsonUtils.writeValueAsString(alteredAccountStatus); mockMvc .perform( RestDocumentationRequestBuilders.patch("/Users/{userId}/status", user.getId()) .header("Authorization", "Bearer " + scimWriteToken) .accept(APPLICATION_JSON) .contentType(APPLICATION_JSON) .content(jsonStatus) ) .andExpect(status().isOk()) .andExpect(content().json(jsonStatus)) .andDo( document("{ClassName}/{methodName}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), pathParameters(parameterWithName("userId").description(userIdDescription)), requestHeaders( headerWithName("Authorization").description("Access token with `scim.write`, `uaa.account_status.write`, or `uaa.admin` required"), IDENTITY_ZONE_ID_HEADER, IDENTITY_ZONE_SUBDOMAIN_HEADER ), requestFields(fieldWithPath("passwordChangeRequired").optional(null).description("Set to `true` in order to force internal user’s password to expire").type(BOOLEAN)), responseFields(fieldWithPath("passwordChangeRequired").description("The `passwordChangeRequired` value given in the request.").type(BOOLEAN)) ) ); }
@ParameterizedTest @MethodSource("org.cloudfoundry.identity.uaa.login.ForcePasswordChangeControllerMockMvcTest#authenticationTestParams") void force_password_change_with_invalid_password(PasswordPolicyWithInvalidPassword passwordPolicyWithInvalidPassword) throws Exception { UserAccountStatus userAccountStatus = new UserAccountStatus(); userAccountStatus.setPasswordChangeRequired(true); String jsonStatus = JsonUtils.writeValueAsString(userAccountStatus); mockMvc.perform(