From 2ab8fdf8848fd35018ea04d4657734eac4bbdf0c Mon Sep 17 00:00:00 2001 From: Priyata Agrawal Date: Tue, 8 Nov 2016 16:32:45 -0800 Subject: [PATCH] fix mysql timezone issue [#131105231] https://www.pivotaltracker.com/story/show/131105231 Signed-off-by: Bharath Sekar --- .../identity/uaa/util/UaaDateUtils.java | 25 ++++++++++++++++++ .../uaa/scim/ScimUserProvisioning.java | 2 +- .../uaa/scim/endpoints/ScimUserEndpoints.java | 26 ++++++++++++------- .../scim/jdbc/JdbcScimUserProvisioning.java | 4 +-- .../remote/RemoteScimUserProvisioning.java | 2 +- .../endpoints/ScimUserEndpointsTests.java | 11 ++++++++ .../jdbc/JdbcScimUserProvisioningTests.java | 7 ++--- .../scim/endpoints/ScimUserEndpointDocs.java | 2 +- .../ScimUserEndpointsMockMvcTests.java | 20 ++++++++++++++ 9 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaDateUtils.java diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaDateUtils.java b/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaDateUtils.java new file mode 100644 index 00000000000..74de3e5f905 --- /dev/null +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/util/UaaDateUtils.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.util; + +import java.util.Calendar; + +public class UaaDateUtils { + + public static long getMinDate() { + Calendar calendar = Calendar.getInstance(); + calendar.set(1970, Calendar.JANUARY, 1, 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTimeInMillis(); + } +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserProvisioning.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserProvisioning.java index fed4819b052..35912b32752 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserProvisioning.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserProvisioning.java @@ -27,7 +27,7 @@ public interface ScimUserProvisioning extends ResourceManager, Queryab void changePassword(String id, String oldPassword, String newPassword) throws ScimResourceNotFoundException; - void updatePasswordLastModified(String id, Date passwordLastModified) throws ScimResourceNotFoundException; + void updatePasswordLastModified(String id, long passwordLastModified) throws ScimResourceNotFoundException; ScimUser verifyUser(String id, int version) throws ScimResourceNotFoundException, InvalidScimResourceException; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java index fb00cf47a75..aad86468669 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpoints.java @@ -19,6 +19,7 @@ import org.cloudfoundry.identity.uaa.account.event.UserAccountUnlockedEvent; import org.cloudfoundry.identity.uaa.approval.Approval; import org.cloudfoundry.identity.uaa.approval.ApprovalStore; +import org.cloudfoundry.identity.uaa.authentication.Origin; import org.cloudfoundry.identity.uaa.codestore.ExpiringCode; import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore; import org.cloudfoundry.identity.uaa.constants.OriginKeys; @@ -41,6 +42,7 @@ import org.cloudfoundry.identity.uaa.scim.exception.UserAlreadyVerifiedException; import org.cloudfoundry.identity.uaa.scim.util.ScimUtils; import org.cloudfoundry.identity.uaa.scim.validate.PasswordValidator; +import org.cloudfoundry.identity.uaa.util.UaaDateUtils; import org.cloudfoundry.identity.uaa.util.UaaPagingUtils; import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.web.ConvertingExceptionView; @@ -415,21 +417,27 @@ public UserAccountStatus updateAccountStatus(@RequestBody UserAccountStatus stat 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."); + validatePasswordExpiry(user, status); + try{ + dao.updatePasswordLastModified(userId, UaaDateUtils.getMinDate()); + scimUpdates.incrementAndGet(); + } catch (OptimisticLockingFailureException e) { + throw new ScimResourceConflictException(e.getMessage()); } } return status; } + private void validatePasswordExpiry(ScimUser user, UserAccountStatus status) throws IllegalArgumentException{ + if(!user.getOrigin().equals(OriginKeys.UAA)) { + throw new IllegalArgumentException("Cannot force password expiry on external users."); + } + if(!status.getPasswordExpires()) { + throw new IllegalArgumentException("Cannot set user passwordExpires to false."); + } + } + private ScimUser syncGroups(ScimUser user) { if (user == null) { return user; diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioning.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioning.java index 80b84052c9f..1b543baada5 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioning.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioning.java @@ -295,11 +295,11 @@ public void setValues(PreparedStatement ps) throws SQLException { } @Override - public void updatePasswordLastModified(final String id, final Date passwordLastModified) + public void updatePasswordLastModified(final String id, final long 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.setTimestamp(1, new Timestamp(passwordLastModified)); ps.setString(2, id); ps.setString(3, zoneId); }); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/remote/RemoteScimUserProvisioning.java b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/remote/RemoteScimUserProvisioning.java index 5d3f5f6b490..c4d1bc59e90 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/scim/remote/RemoteScimUserProvisioning.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/scim/remote/RemoteScimUserProvisioning.java @@ -110,7 +110,7 @@ public void changePassword(String id, String oldPassword, String newPassword) } @Override - public void updatePasswordLastModified(String id, Date passwordLastModified) throws ScimResourceNotFoundException { + public void updatePasswordLastModified(String id, long passwordLastModified) throws ScimResourceNotFoundException { throw new UnsupportedOperationException(); } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsTests.java index a18ff824412..e2da39dd395 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsTests.java @@ -1025,4 +1025,15 @@ public void testPatchUserStatusWithPasswordExpiryFalse() { userAccountStatus.setPasswordExpires(false); endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); } + + @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.setPasswordExpires(true); + endpoints.updateAccountStatus(userAccountStatus, createdUser.getId()); + } } diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioningTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioningTests.java index 280946bb9f0..867aa9bbdc9 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioningTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/scim/jdbc/JdbcScimUserProvisioningTests.java @@ -28,6 +28,7 @@ import org.cloudfoundry.identity.uaa.scim.test.TestUtils; import org.cloudfoundry.identity.uaa.test.JdbcTestBase; import org.cloudfoundry.identity.uaa.user.UaaAuthority; +import org.cloudfoundry.identity.uaa.util.UaaDateUtils; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.JdbcIdentityZoneProvisioning; @@ -330,9 +331,9 @@ 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)); + db.updatePasswordLastModified(created.getId(), UaaDateUtils.getMinDate()); ScimUser updated = db.retrieve(created.getId()); - assertEquals(0, updated.getPasswordLastModified().getTime()); + assertEquals(UaaDateUtils.getMinDate(), updated.getPasswordLastModified().getTime()); } @Test (expected=ScimResourceNotFoundException.class) @@ -340,7 +341,7 @@ 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)); + db.updatePasswordLastModified("1234", UaaDateUtils.getMinDate()); } @Test diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointDocs.java index 7b67e615eb4..f458d6867d0 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointDocs.java @@ -458,7 +458,7 @@ public void test_Create_User() throws Exception { IDENTITY_ZONE_ID_HEADER, IDENTITY_ZONE_SUBDOMAIN_HEADER ), - requestFields(fieldWithPath("passwordExpires").optional(null).description("Set to `true` in order to force user’s password to expire").type(BOOLEAN)), + requestFields(fieldWithPath("passwordExpires").optional(null).description("Set to `true` in order to force internal user’s password to expire").type(BOOLEAN)), responseFields(fieldWithPath("passwordExpires").description("The `passwordExpires` value given in the request.").type(BOOLEAN)) ) ); diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java index 0e286d1d273..623cf12c9a5 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointsMockMvcTests.java @@ -689,6 +689,26 @@ public void testForcePasswordExpireAccountInvalid() throws Exception { .andExpect(status().isBadRequest()); } + @Test + public void testForcePasswordExpireAccountExternalUser() throws Exception { + ScimUser user = createUser(uaaAdminToken); + user.setOrigin("NOT_UAA"); + updateUser(uaaAdminToken, HttpStatus.OK.value(), user); + 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().isBadRequest()); + } + @Test public void testForcePasswordExpireAccount() throws Exception { ScimUser user = createUser(uaaAdminToken);