Skip to content

Commit

Permalink
Status API to support forcePasswordExpire on user
Browse files Browse the repository at this point in the history
[#131105231] https://www.pivotaltracker.com/story/show/131105231

Signed-off-by: Mikhail Vyshegorodtsev <medvedzver@inbox.ru>
  • Loading branch information
Bharath Sekar authored and jeaniejung committed Nov 10, 2016
1 parent bb3061c commit d7d12c6
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 5 deletions.
@@ -1,13 +1,25 @@
package org.cloudfoundry.identity.uaa.account;

public class UserAccountStatus {
public Boolean isLocked() {

private Boolean locked;

private Boolean passwordExpires;

public Boolean getLocked() {
return locked;
}

public void setLocked(Boolean locked) {
this.locked = locked;
}

private Boolean locked;

public Boolean getPasswordExpires() {
return passwordExpires;
}

public void setPasswordExpires(Boolean passwordExpires) {
this.passwordExpires = passwordExpires;
}
}
Expand Up @@ -18,13 +18,17 @@
import org.cloudfoundry.identity.uaa.scim.exception.InvalidScimResourceException;
import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException;

import java.util.Date;


public interface ScimUserProvisioning extends ResourceManager<ScimUser>, Queryable<ScimUser> {

ScimUser createUser(ScimUser user, String password) throws InvalidPasswordException, InvalidScimResourceException;

void changePassword(String id, String oldPassword, String newPassword) throws ScimResourceNotFoundException;

void updatePasswordLastModified(String id, Date passwordLastModified) throws ScimResourceNotFoundException;

ScimUser verifyUser(String id, int version) throws ScimResourceNotFoundException, InvalidScimResourceException;

boolean checkPasswordMatches(String id, String password) throws ScimResourceNotFoundException;
Expand Down
Expand Up @@ -80,6 +80,7 @@
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -407,12 +408,23 @@ public SearchResults<?> findUsers(
@RequestMapping(value = "/Users/{userId}/status", method = RequestMethod.PATCH)
public UserAccountStatus updateAccountStatus(@RequestBody UserAccountStatus status, @PathVariable String userId) {
ScimUser user = dao.retrieve(userId);
if(status.isLocked() != null) {
if(!status.isLocked()) {
if(status.getLocked() != null) {
if(!status.getLocked()) {
publish(new UserAccountUnlockedEvent(user));
} else {
throw new IllegalArgumentException("Cannot set user account to locked. User accounts only become locked through exceeding the allowed failed login attempts.");
}
} else if(status.getPasswordExpires() != null) {
if(status.getPasswordExpires()) {
try{
dao.updatePasswordLastModified(userId, new Date(0));
scimUpdates.incrementAndGet();
} catch (OptimisticLockingFailureException e) {
throw new ScimResourceConflictException(e.getMessage());
}
} else {
throw new IllegalArgumentException("Cannot set user passwordExpires to false.");
}
}

return status;
Expand Down
Expand Up @@ -84,6 +84,8 @@ public Log getLogger() {

public static final String DELETE_USER_SQL = "delete from users where id=? and identity_zone_id=?";

public static final String UPDATE_PASSWD_LASTMODIFIED_SQL = "update users set passwd_lastmodified=? where id=? and identity_zone_id=?";

public static final String CHANGE_PASSWORD_SQL = "update users set lastModified=?, password=?, passwd_lastmodified=? where id=? and identity_zone_id=?";

public static final String READ_PASSWORD_SQL = "select password from users where id=? and identity_zone_id=?";
Expand Down Expand Up @@ -292,6 +294,20 @@ public void setValues(PreparedStatement ps) throws SQLException {
return result;
}

@Override
public void updatePasswordLastModified(final String id, final Date passwordLastModified)
throws ScimResourceNotFoundException{
final String zoneId = IdentityZoneHolder.get().getId();
int updated = jdbcTemplate.update(UPDATE_PASSWD_LASTMODIFIED_SQL, ps -> {
ps.setTimestamp(1, new Timestamp(passwordLastModified.getTime()));
ps.setString(2, id);
ps.setString(3, zoneId);
});
if (updated == 0) {
throw new ScimResourceNotFoundException("User " + id + " does not exist");
}
}

@Override
public void changePassword(final String id, String oldPassword, final String newPassword)
throws ScimResourceNotFoundException {
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.List;

/**
Expand Down Expand Up @@ -108,6 +109,11 @@ public void changePassword(String id, String oldPassword, String newPassword)
restTemplate.put(baseUrl + "/User/{id}/password", request, id);
}

@Override
public void updatePasswordLastModified(String id, Date passwordLastModified) throws ScimResourceNotFoundException {
throw new UnsupportedOperationException();
}

@Override
public ScimUser verifyUser(String id, int version) throws ScimResourceNotFoundException,
InvalidScimResourceException {
Expand Down
Expand Up @@ -991,7 +991,7 @@ public void testPatchUserStatus() {
UserAccountStatus userAccountStatus = new UserAccountStatus();
userAccountStatus.setLocked(false);
UserAccountStatus updatedStatus = endpoints.updateAccountStatus(userAccountStatus, createdUser.getId());
assertEquals(false, updatedStatus.isLocked());
assertEquals(false, updatedStatus.getLocked());
}

@Test(expected = IllegalArgumentException.class)
Expand All @@ -1004,4 +1004,25 @@ public void testPatchUserInvalidStatus() {
endpoints.updateAccountStatus(userAccountStatus, createdUser.getId());
}

@Test
public void testPatchUserStatusWithPasswordExpiry() {
ScimUser user = new ScimUser(null, "uname", "gname", "fname");
user.addEmail("test@example.org");
ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse());
UserAccountStatus userAccountStatus = new UserAccountStatus();
userAccountStatus.setPasswordExpires(true);
UserAccountStatus updatedStatus = endpoints.updateAccountStatus(userAccountStatus, createdUser.getId());
ScimUser updatedUser = endpoints.getUser(createdUser.getId(), new MockHttpServletResponse());
assertEquals(0, updatedUser.getPasswordLastModified().getTime());
}

@Test(expected = IllegalArgumentException.class)
public void testPatchUserStatusWithPasswordExpiryFalse() {
ScimUser user = new ScimUser(null, "uname", "gname", "fname");
user.addEmail("test@example.org");
ScimUser createdUser = endpoints.createUser(user, new MockHttpServletRequest(), new MockHttpServletResponse());
UserAccountStatus userAccountStatus = new UserAccountStatus();
userAccountStatus.setPasswordExpires(false);
endpoints.updateAccountStatus(userAccountStatus, createdUser.getId());
}
}
Expand Up @@ -48,6 +48,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -324,6 +325,24 @@ public void canModifyPassword() throws Exception {
assertEquals((user.getMeta().getLastModified().getTime() / 1000l) * 1000l, user.getPasswordLastModified().getTime());
}

@Test
public void testChangePasswordLastModified() {
ScimUser user = new ScimUser(null, generator.generate()+ "@foo.com", "Jo", "User");
user.addEmail(user.getUserName());
ScimUser created = db.createUser(user, "j7hyqpassX");
db.updatePasswordLastModified(created.getId(), new Date(0));
ScimUser updated = db.retrieve(created.getId());
assertEquals(0, updated.getPasswordLastModified().getTime());
}

@Test (expected=ScimResourceNotFoundException.class)
public void testChangePasswordLastModifiedForInvalidUser() {
ScimUser user = new ScimUser(null, generator.generate()+ "@foo.com", "Jo", "User");
user.addEmail(user.getUserName());
ScimUser created = db.createUser(user, "j7hyqpassX");
db.updatePasswordLastModified("1234", new Date(0));
}

@Test
public void canCreateUserInOtherIdentityZone() {
String otherZoneId = "my-zone-id";
Expand Down
Expand Up @@ -671,6 +671,42 @@ public void testUnlockAccountWhenNotLocked() throws Exception {
.andExpect(redirectedUrl("/"));
}

@Test
public void testForcePasswordExpireAccountInvalid() throws Exception {
ScimUser user = createUser(uaaAdminToken);
UserAccountStatus alteredAccountStatus = new UserAccountStatus();
alteredAccountStatus.setPasswordExpires(false);

String jsonStatus = JsonUtils.writeValueAsString(alteredAccountStatus);
getMockMvc()
.perform(
patch("/Users/"+user.getId()+"/status")
.header("Authorization", "Bearer " + uaaAdminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(jsonStatus)
)
.andExpect(status().isBadRequest());
}

@Test
public void testForcePasswordExpireAccount() throws Exception {
ScimUser user = createUser(uaaAdminToken);
UserAccountStatus alteredAccountStatus = new UserAccountStatus();
alteredAccountStatus.setPasswordExpires(true);

String jsonStatus = JsonUtils.writeValueAsString(alteredAccountStatus);
getMockMvc()
.perform(
patch("/Users/"+user.getId()+"/status")
.header("Authorization", "Bearer " + uaaAdminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(jsonStatus)
)
.andExpect(status().isOk());
}

private void attemptFailedLogin(int numberOfAttempts, String username, String subdomain) throws Exception {
String requestDomain = subdomain.equals("") ? "localhost" : subdomain + ".localhost";
MockHttpServletRequestBuilder post = post("/login.do")
Expand Down

0 comments on commit d7d12c6

Please sign in to comment.