VYPR
High severity8.1NVD Advisory· Published May 25, 2017· Updated May 13, 2026

CVE-2016-3084

CVE-2016-3084

Description

The UAA reset password flow in Cloud Foundry release v236 and earlier versions, UAA release v3.3.0 and earlier versions, all versions of Login-server, UAA release v10 and earlier versions and Pivotal Elastic Runtime versions prior to 1.7.2 is vulnerable to a brute force attack due to multiple active codes at a given time. This vulnerability is applicable only when using the UAA internal user store for authentication. Deployments enabled for integration via SAML or LDAP are not affected.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven
< 3.3.0.13.3.0.1

Affected products

6

Patches

7
4a119d314744

Merge branch 'master' into develop

https://github.com/cloudfoundry/uaaMadhura BhaveMay 4, 2016via ghsa
17 files changed · +89 44
  • server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java+13 11 modified
    @@ -80,20 +80,22 @@ private ResetPasswordResponse changePasswordCodeAuthenticated(String code, Strin
                 throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
             String userId;
    -        String userName = null;
    -        Date passwordLastModified = null;
    -        String clientId = null;
    -        String redirectUri = null;
    +        String userName;
    +        Date passwordLastModified;
    +        String clientId;
    +        String redirectUri;
    +        PasswordChange change;
             try {
    -            PasswordChange change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
    -            userId = change.getUserId();
    -            userName = change.getUsername();
    -            passwordLastModified = change.getPasswordModifiedTime();
    -            clientId = change.getClientId();
    -            redirectUri = change.getRedirectUri();
    +            change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
             } catch (JsonUtils.JsonUtilException x) {
    -            userId = expiringCode.getData();
    +            throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
    +        userId = change.getUserId();
    +        userName = change.getUsername();
    +        passwordLastModified = change.getPasswordModifiedTime();
    +        clientId = change.getClientId();
    +        redirectUri = change.getRedirectUri();
    +
             ScimUser user = scimUserProvisioning.retrieve(userId);
             try {
                 if (isUserModified(user, expiringCode.getExpiresAt(), userName, passwordLastModified)) {
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/codestore/JdbcExpiringCodeStore.java+1 1 modified
    @@ -43,7 +43,7 @@ public class JdbcExpiringCodeStore implements ExpiringCodeStore {
     
         private Log logger = LogFactory.getLog(getClass());
     
    -    private RandomValueStringGenerator generator = new RandomValueStringGenerator(6);
    +    private RandomValueStringGenerator generator = new RandomValueStringGenerator(10);
     
         private JdbcTemplate jdbcTemplate;
     
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/invitations/EmailInvitationsService.java+1 1 modified
    @@ -54,7 +54,7 @@ public AcceptedInvitation acceptInvitation(String code, String password) {
             user = scimUserProvisioning.verifyUser(userId, user.getVersion());
     
     
    -        if (OriginKeys.UAA.equals(user.getOrigin())) {
    +        if (OriginKeys.UAA.equals(user.getOrigin()) && StringUtils.hasText(password)) {
                 PasswordChangeRequest request = new PasswordChangeRequest();
                 request.setPassword(password);
                 scimUserProvisioning.changePassword(userId, null, password);
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordChange.java+0 2 modified
    @@ -1,11 +1,9 @@
     package org.cloudfoundry.identity.uaa.scim.endpoints;
     
    -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
     import com.fasterxml.jackson.annotation.JsonProperty;
     
     import java.util.Date;
     
    -@JsonIgnoreProperties(ignoreUnknown = true)
     public class PasswordChange {
         public PasswordChange() {}
     
    
  • server/src/main/resources/login-ui.xml+1 1 modified
    @@ -151,7 +151,7 @@
               entry-point-ref="loginEntryPoint"
               use-expressions="false"
               xmlns="http://www.springframework.org/schema/security">
    -        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    +        <intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
             <csrf disabled="false"/>
             <access-denied-handler ref="loginEntryPoint"/>
         </http>
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/codestore/CodeStoreEndpointsTests.java+1 1 modified
    @@ -51,7 +51,7 @@ public void testGenerateCode() throws Exception {
             assertNotNull(result);
     
             assertNotNull(result.getCode());
    -        assertTrue(result.getCode().trim().length() > 0);
    +        assertTrue(result.getCode().trim().length() == 10);
     
             assertEquals(expiresAt, result.getExpiresAt());
     
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java+1 1 modified
    @@ -306,7 +306,7 @@ public void acceptInvitePage_for_verifiedUser() throws Exception {
     
             when(expiringCodeStore.retrieveCode("the_secret_code")).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
             when(expiringCodeStore.generateCode(anyString(), anyObject(), eq(null))).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
    -        when(invitationsService.acceptInvitation(anyString(), anyString())).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
    +        when(invitationsService.acceptInvitation(anyString(), eq(""))).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
             IdentityProvider provider = new IdentityProvider();
             provider.setType(OriginKeys.UAA);
             when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(provider);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/EmailInvitationsServiceTests.java+18 2 modified
    @@ -14,7 +14,6 @@
     import org.junit.Test;
     import org.junit.runner.RunWith;
     import org.springframework.beans.factory.annotation.Autowired;
    -import org.springframework.beans.factory.annotation.Qualifier;
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.annotation.Configuration;
     import org.springframework.context.annotation.Import;
    @@ -32,7 +31,6 @@
     import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
     import org.springframework.web.servlet.config.annotation.EnableWebMvc;
     import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    -import org.thymeleaf.spring4.SpringTemplateEngine;
     
     import java.sql.Timestamp;
     import java.util.HashMap;
    @@ -48,6 +46,7 @@
     import static org.mockito.Matchers.eq;
     import static org.mockito.Mockito.doThrow;
     import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.never;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.when;
     import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID;
    @@ -108,6 +107,23 @@ public void acceptInvitationNoClientId() throws Exception {
             assertEquals("/home", redirectLocation);
         }
     
    +    @Test
    +    public void acceptInvitation_withoutPasswordUpdate() throws Exception {
    +        ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    +        user.setOrigin(UAA);
    +        when(scimUserProvisioning.retrieve(eq("user-id-001"))).thenReturn(user);
    +        when(scimUserProvisioning.verifyUser(anyString(), anyInt())).thenReturn(user);
    +
    +        Map<String,String> userData = new HashMap<>();
    +        userData.put(USER_ID, "user-id-001");
    +        userData.put(EMAIL, "user@example.com");
    +        when(expiringCodeStore.retrieveCode(anyString())).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(userData), null));
    +
    +        emailInvitationsService.acceptInvitation("code", "").getRedirectUri();
    +        verify(scimUserProvisioning).verifyUser(user.getId(), user.getVersion());
    +        verify(scimUserProvisioning, never()).changePassword(anyString(), anyString(), anyString());
    +    }
    +
         @Test
         public void acceptInvitationWithClientNotFound() throws Exception {
             ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java+18 2 modified
    @@ -127,7 +127,7 @@ public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception
             ArgumentCaptor<ResetPasswordRequestEvent> captor = ArgumentCaptor.forClass(ResetPasswordRequestEvent.class);
             verify(publisher).publishEvent(captor.capture());
             ResetPasswordRequestEvent event = captor.getValue();
    -        assertThat((String) event.getSource(), equalTo("user@example.com"));
    +        assertThat(event.getSource(), equalTo("user@example.com"));
             assertThat(event.getCode(), equalTo("code"));
             assertThat(event.getAuthentication(), sameInstance(authentication));
         }
    @@ -191,7 +191,7 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             user.setMeta(new ScimMeta(new Date(), new Date(), 0));
             user.setPrimaryEmail("foo@example.com");
             ExpiringCode expiringCode = new ExpiringCode("good_code",
    -            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "{\"user_id\":\"user-id\",\"username\":\"username\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null);
             when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
             when(scimUserProvisioning.retrieve("user-id")).thenReturn(user);
             when(scimUserProvisioning.checkPasswordMatches("user-id", "Passwo3dAsOld"))
    @@ -208,6 +208,22 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             }
         }
     
    +    @Test
    +    public void resetPassword_InvalidCodeData() {
    +        ExpiringCode expiringCode = new ExpiringCode("good_code",
    +                new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +        when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
    +        SecurityContext securityContext = mock(SecurityContext.class);
    +        when(securityContext.getAuthentication()).thenReturn(new MockAuthentication());
    +        SecurityContextHolder.setContext(securityContext);
    +        try {
    +            emailResetPasswordService.resetPassword("good_code", "password");
    +            fail();
    +        } catch (InvalidCodeException e) {
    +            assertEquals("Sorry, your reset password link is no longer valid. Please request a new one", e.getMessage());
    +        }
    +    }
    +
         @Test
         public void resetPassword_WithInvalidClientId() {
             setupResetPassword("invalid_client", "redirect.example.com");
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointTest.java+8 3 modified
    @@ -237,7 +237,8 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
         @Test
         public void testChangingAPasswordWithAValidCode() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                        "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -281,7 +282,9 @@ public void changing_password_with_invalid_code() throws Exception {
         @Test
         public void testChangingAPasswordForUnverifiedUser() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -338,7 +341,9 @@ public void changePassword_Returns422UnprocessableEntity_NewPasswordSameAsOld()
             Mockito.reset(passwordValidator);
     
             when(expiringCodeStore.retrieveCode("emailed_code"))
    -            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis()-(1000*60*60*24)), new Date(System.currentTimeMillis()-(1000*60*60*24)), 0));
    
  • uaa/src/main/webapp/WEB-INF/spring/codestore-endpoints.xml+1 1 modified
    @@ -26,7 +26,7 @@
         <http  name="codeStoreSecurity" pattern="/Codes/**" create-session="stateless" authentication-manager-ref="emptyAuthenticationManager"
                entry-point-ref="oauthAuthenticationEntryPoint"
                xmlns="http://www.springframework.org/schema/security" use-expressions="true">
    -        <intercept-url pattern="/**" access="#oauth2.hasAnyScope('scim.create','scim.write','password.write')"/>
    +        <intercept-url pattern="/**" access="#oauth2.hasAnyScope('oauth.login')"/>
             <custom-filter ref="resourceAgnosticAuthenticationFilter" position="PRE_AUTH_FILTER" />
             <anonymous enabled="false" />
             <expression-handler ref="oauthWebExpressionHandler" />
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/AutologinIT.java+1 1 modified
    @@ -249,7 +249,7 @@ public void testFormEncodedAutologinRequest() throws Exception {
                     Map.class);
     
             String autologinCode = (String) autologinResponseEntity.getBody().get("code");
    -        assertEquals(6, autologinCode.length());
    +        assertEquals(10, autologinCode.length());
         }
     
         @Test
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/ChangePasswordIT.java+2 4 modified
    @@ -90,6 +90,7 @@ public void setUp() throws Exception {
     
         @Test
         public void testChangePassword() throws Exception {
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, NEW_PASSWORD, "new");
    @@ -108,7 +109,7 @@ public void displaysErrorWhenPasswordContravenesPolicy() {
             //the only policy we can contravene by default is the length
     
             String newPassword = new RandomValueStringGenerator(260).generate();
    -
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, newPassword, newPassword);
    @@ -134,11 +135,8 @@ private void signOut() {
         }
     
         private void signIn(String userName, String password) {
    -        webDriver.get(baseUrl + "/logout.do");
    -        webDriver.get(baseUrl + "/login");
             webDriver.findElement(By.name("username")).sendKeys(userName);
             webDriver.findElement(By.name("password")).sendKeys(password);
             webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();
    -        assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), containsString("Where to?"));
         }
     }
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/InvitationsIT.java+10 7 modified
    @@ -91,7 +91,7 @@ public class InvitationsIT {
         @Before
         public void setup() throws Exception {
             scimToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "scim.read,scim.write");
    -        loginToken = testClient.getOAuthAccessToken("login", "loginsecret", "client_credentials", "password.write,scim.write");
    +        loginToken = testClient.getOAuthAccessToken("login", "loginsecret", "client_credentials", "oauth.login");
             screenShootRule.setWebDriver(webDriver);
         }
     
    @@ -199,9 +199,9 @@ private String createInvitation(String username, String userEmail, String redire
             return createInvitation(baseUrl, uaaUrl, username, userEmail, origin, redirectUri, loginToken, scimToken);
         }
     
    -    public static String createInvitation(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String scimWriteToken, String scimReadToken) {
    +    public static String createInvitation(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String loginToken, String scimToken) {
             HttpHeaders headers = new HttpHeaders();
    -        headers.add("Authorization", "Bearer " + scimWriteToken);
    +        headers.add("Authorization", "Bearer " + scimToken);
             RestTemplate uaaTemplate = new RestTemplate();
             ScimUser scimUser = new ScimUser();
             scimUser.setUserName(username);
    @@ -211,8 +211,8 @@ public static String createInvitation(String baseUrl, String uaaUrl, String user
     
             String userId = null;
             try {
    -            userId = IntegrationTestUtils.getUserIdByField(scimReadToken, baseUrl, origin, "email", userEmail);
    -            scimUser = IntegrationTestUtils.getUser(scimReadToken, baseUrl, userId);
    +            userId = IntegrationTestUtils.getUserIdByField(scimToken, baseUrl, origin, "email", userEmail);
    +            scimUser = IntegrationTestUtils.getUser(scimToken, baseUrl, userId);
             } catch (RuntimeException x) {
             }
             if (userId == null) {
    @@ -224,12 +224,15 @@ public static String createInvitation(String baseUrl, String uaaUrl, String user
                 userId = response.getBody().getId();
             } else {
                 scimUser.setVerified(false);
    -            IntegrationTestUtils.updateUser(scimWriteToken, uaaUrl, scimUser);
    +            IntegrationTestUtils.updateUser(scimToken, uaaUrl, scimUser);
             }
     
    +        HttpHeaders invitationHeaders = new HttpHeaders();
    +        invitationHeaders.add("Authorization", "Bearer " + loginToken);
    +
             Timestamp expiry = new Timestamp(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(System.currentTimeMillis() + 24 * 3600, TimeUnit.MILLISECONDS));
             ExpiringCode expiringCode = new ExpiringCode(null, expiry, "{\"origin\":\"" + origin + "\", \"client_id\":\"app\", \"redirect_uri\":\"" + redirectUri + "\", \"user_id\":\"" + userId + "\", \"email\":\"" + userEmail + "\"}", null);
    -        HttpEntity<ExpiringCode> expiringCodeRequest = new HttpEntity<>(expiringCode, headers);
    +        HttpEntity<ExpiringCode> expiringCodeRequest = new HttpEntity<>(expiringCode, invitationHeaders);
             ResponseEntity<ExpiringCode> expiringCodeResponse = uaaTemplate.exchange(uaaUrl + "/Codes", HttpMethod.POST, expiringCodeRequest, ExpiringCode.class);
             expiringCode = expiringCodeResponse.getBody();
             return expiringCode.getCode();
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java+5 4 modified
    @@ -275,7 +275,8 @@ public void testResettingAPasswordUsingTimestampForUserModification() throws Exc
             List<ScimUser> users = getWebApplicationContext().getBean(ScimUserProvisioning.class).query("username eq \"marissa\"");
             assertNotNull(users);
             assertEquals(1, users.size());
    -        ExpiringCode code = codeStore.generateCode(users.get(0).getId(), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(users.get(0).getId(), users.get(0).getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
     
             MockHttpServletRequestBuilder post = createChangePasswordRequest(users.get(0), code,
                 true, "newpassw0rD", "newpassw0rD");
    @@ -302,11 +303,11 @@ public void resetPassword_ReturnsUnprocessableEntity_NewPasswordSameAsOld() thro
             assertNotNull(users);
             assertEquals(1, users.size());
             ScimUser user = users.get(0);
    -
    -        ExpiringCode code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(user.getId(), user.getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"));
     
    -        code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"))
                 .andExpect(status().isUnprocessableEntity())
                 .andExpect(view().name("forgot_password"))
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/codestore/ExpiringCodeStoreMockMvcTests.java+1 1 modified
    @@ -43,7 +43,7 @@ public class ExpiringCodeStoreMockMvcTests extends InjectedMockContextTest {
         @Before
         public void setUp() throws Exception {
             testClient = new TestClient(getMockMvc());
    -        loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", null);
    +        loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", "oauth.login");
             getWebApplicationContext().getBean(JdbcTemplate.class).update("DELETE FROM expiring_code_store ");
         }
     
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/DisableUserManagementSecurityFilterMockMvcTest.java+7 1 modified
    @@ -351,8 +351,14 @@ public void changeEmailControllerVerifyEmailNotAllowed() throws Exception {
     
         @Test
         public void changePasswordControllerChangePasswordPageNotAllowed() throws Exception {
    +        MockMvcUtils.setDisableInternalUserManagement(false, getWebApplicationContext());
    +        
    +        ResultActions result = createUser();
    +        ScimUser createdUser = JsonUtils.readValue(result.andReturn().getResponse().getContentAsString(), ScimUser.class);
             MockMvcUtils.setDisableInternalUserManagement(true, getWebApplicationContext());
    -        getMockMvc().perform(get("/change_password"))
    +
    +        getMockMvc().perform(get("/change_password")
    +            .session(getUserSession(createdUser.getUserName(), PASSWD)))
                 .andExpect(status().isForbidden())
                 .andExpect(content()
                                .string(JsonObjectMatcherUtils.matchesJsonObject(
    
5c2377487bef

Merge branch 'releases/3.3.0.1'

https://github.com/cloudfoundry/uaaCF-IdentityMay 4, 2016via ghsa
21 files changed · +168 60
  • gradle.properties+1 1 modified
    @@ -1 +1 @@
    -version=3.3.0
    +version=3.3.0.1
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java+17 12 modified
    @@ -49,6 +49,7 @@
     public class UaaResetPasswordService implements ResetPasswordService, ApplicationEventPublisherAware {
     
         public static final int PASSWORD_RESET_LIFETIME = 30 * 60 * 1000;
    +    public static final String FORGOT_PASSWORD_INTENT_PREFIX = "forgot_password_for_id:";
     
         private final ScimUserProvisioning scimUserProvisioning;
         private final ExpiringCodeStore expiringCodeStore;
    @@ -79,20 +80,22 @@ private ResetPasswordResponse changePasswordCodeAuthenticated(String code, Strin
                 throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
             String userId;
    -        String userName = null;
    -        Date passwordLastModified = null;
    -        String clientId = null;
    -        String redirectUri = null;
    +        String userName;
    +        Date passwordLastModified;
    +        String clientId;
    +        String redirectUri;
    +        PasswordChange change;
             try {
    -            PasswordChange change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
    -            userId = change.getUserId();
    -            userName = change.getUsername();
    -            passwordLastModified = change.getPasswordModifiedTime();
    -            clientId = change.getClientId();
    -            redirectUri = change.getRedirectUri();
    +            change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
             } catch (JsonUtils.JsonUtilException x) {
    -            userId = expiringCode.getData();
    +            throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
    +        userId = change.getUserId();
    +        userName = change.getUsername();
    +        passwordLastModified = change.getPasswordModifiedTime();
    +        clientId = change.getClientId();
    +        redirectUri = change.getRedirectUri();
    +
             ScimUser user = scimUserProvisioning.retrieve(userId);
             try {
                 if (isUserModified(user, expiringCode.getExpiresAt(), userName, passwordLastModified)) {
    @@ -141,7 +144,9 @@ public ForgotPasswordInfo forgotPassword(String email, String clientId, String r
             ScimUser scimUser = results.get(0);
     
             PasswordChange change = new PasswordChange(scimUser.getId(), scimUser.getUserName(), scimUser.getPasswordLastModified(), clientId, redirectUri);
    -        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), null);
    +        String intent = FORGOT_PASSWORD_INTENT_PREFIX+scimUser.getId();
    +        expiringCodeStore.expireByIntent(intent);
    +        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), intent);
             publish(new ResetPasswordRequestEvent(email, code.getCode(), SecurityContextHolder.getContext().getAuthentication()));
             return new ForgotPasswordInfo(scimUser.getId(), code);
         }
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/codestore/JdbcExpiringCodeStore.java+1 1 modified
    @@ -43,7 +43,7 @@ public class JdbcExpiringCodeStore implements ExpiringCodeStore {
     
         private Log logger = LogFactory.getLog(getClass());
     
    -    private RandomValueStringGenerator generator = new RandomValueStringGenerator(6);
    +    private RandomValueStringGenerator generator = new RandomValueStringGenerator(10);
     
         private JdbcTemplate jdbcTemplate;
     
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/invitations/EmailInvitationsService.java+1 1 modified
    @@ -54,7 +54,7 @@ public AcceptedInvitation acceptInvitation(String code, String password) {
             user = scimUserProvisioning.verifyUser(userId, user.getVersion());
     
     
    -        if (OriginKeys.UAA.equals(user.getOrigin())) {
    +        if (OriginKeys.UAA.equals(user.getOrigin()) && StringUtils.hasText(password)) {
                 PasswordChangeRequest request = new PasswordChangeRequest();
                 request.setPassword(password);
                 scimUserProvisioning.changePassword(userId, null, password);
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordChange.java+0 2 modified
    @@ -1,11 +1,9 @@
     package org.cloudfoundry.identity.uaa.scim.endpoints;
     
    -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
     import com.fasterxml.jackson.annotation.JsonProperty;
     
     import java.util.Date;
     
    -@JsonIgnoreProperties(ignoreUnknown = true)
     public class PasswordChange {
         public PasswordChange() {}
     
    
  • server/src/main/resources/login-ui.xml+1 1 modified
    @@ -151,7 +151,7 @@
               entry-point-ref="loginEntryPoint"
               use-expressions="false"
               xmlns="http://www.springframework.org/schema/security">
    -        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    +        <intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
             <csrf disabled="false"/>
             <access-denied-handler ref="loginEntryPoint"/>
         </http>
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/codestore/CodeStoreEndpointsTests.java+1 1 modified
    @@ -51,7 +51,7 @@ public void testGenerateCode() throws Exception {
             assertNotNull(result);
     
             assertNotNull(result.getCode());
    -        assertTrue(result.getCode().trim().length() > 0);
    +        assertTrue(result.getCode().trim().length() == 10);
     
             assertEquals(expiresAt, result.getExpiresAt());
     
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java+1 1 modified
    @@ -306,7 +306,7 @@ public void acceptInvitePage_for_verifiedUser() throws Exception {
     
             when(expiringCodeStore.retrieveCode("the_secret_code")).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
             when(expiringCodeStore.generateCode(anyString(), anyObject(), eq(null))).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
    -        when(invitationsService.acceptInvitation(anyString(), anyString())).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
    +        when(invitationsService.acceptInvitation(anyString(), eq(""))).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
             IdentityProvider provider = new IdentityProvider();
             provider.setType(OriginKeys.UAA);
             when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(provider);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/EmailInvitationsServiceTests.java+18 2 modified
    @@ -14,7 +14,6 @@
     import org.junit.Test;
     import org.junit.runner.RunWith;
     import org.springframework.beans.factory.annotation.Autowired;
    -import org.springframework.beans.factory.annotation.Qualifier;
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.annotation.Configuration;
     import org.springframework.context.annotation.Import;
    @@ -32,7 +31,6 @@
     import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
     import org.springframework.web.servlet.config.annotation.EnableWebMvc;
     import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    -import org.thymeleaf.spring4.SpringTemplateEngine;
     
     import java.sql.Timestamp;
     import java.util.HashMap;
    @@ -48,6 +46,7 @@
     import static org.mockito.Matchers.eq;
     import static org.mockito.Mockito.doThrow;
     import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.never;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.when;
     import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID;
    @@ -108,6 +107,23 @@ public void acceptInvitationNoClientId() throws Exception {
             assertEquals("/home", redirectLocation);
         }
     
    +    @Test
    +    public void acceptInvitation_withoutPasswordUpdate() throws Exception {
    +        ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    +        user.setOrigin(UAA);
    +        when(scimUserProvisioning.retrieve(eq("user-id-001"))).thenReturn(user);
    +        when(scimUserProvisioning.verifyUser(anyString(), anyInt())).thenReturn(user);
    +
    +        Map<String,String> userData = new HashMap<>();
    +        userData.put(USER_ID, "user-id-001");
    +        userData.put(EMAIL, "user@example.com");
    +        when(expiringCodeStore.retrieveCode(anyString())).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(userData), null));
    +
    +        emailInvitationsService.acceptInvitation("code", "").getRedirectUri();
    +        verify(scimUserProvisioning).verifyUser(user.getId(), user.getVersion());
    +        verify(scimUserProvisioning, never()).changePassword(anyString(), anyString(), anyString());
    +    }
    +
         @Test
         public void acceptInvitationWithClientNotFound() throws Exception {
             ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java+30 8 modified
    @@ -12,15 +12,15 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    -import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
     import org.cloudfoundry.identity.uaa.account.ConflictException;
     import org.cloudfoundry.identity.uaa.account.ForgotPasswordInfo;
     import org.cloudfoundry.identity.uaa.account.NotFoundException;
     import org.cloudfoundry.identity.uaa.account.ResetPasswordService.ResetPasswordResponse;
     import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.account.event.ResetPasswordRequestEvent;
    +import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
     import org.cloudfoundry.identity.uaa.scim.ScimMeta;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
    @@ -93,18 +93,24 @@ public void forgotPassword_ResetCodeIsReturnedSuccessfully() throws Exception {
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
     
    +        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
    +
             when(codeStore.generateCode(eq("{\"user_id\":\"user-id-001\",\"username\":\"user@example.com\",\"passwordModifiedTime\":1234,\"client_id\":\"example\",\"redirect_uri\":\"redirect.example.com\"}"),
    -                                    any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +                                    any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             ForgotPasswordInfo forgotPasswordInfo = emailResetPasswordService.forgotPassword("user@example.com", "example", "redirect.example.com");
    -        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
     
    +        verify(codeStore).expireByIntent(captor.capture());
    +        assertEquals(UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId(), captor.getValue());
    +        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
             ExpiringCode resetPasswordCode = forgotPasswordInfo.getResetPasswordCode();
             assertThat(resetPasswordCode.getCode(), equalTo("code"));
             assertThat(resetPasswordCode.getExpiresAt(), equalTo(expiresAt));
             assertThat(resetPasswordCode.getData(), equalTo("user-id-001"));
         }
     
    +
    +
         @Test
         public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception {
             ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
    @@ -115,13 +121,13 @@ public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception
             user.setPrimaryEmail("user@example.com");
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
    -        when(codeStore.generateCode(anyString(), any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +        when(codeStore.generateCode(anyString(), any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             emailResetPasswordService.forgotPassword("user@example.com", "", "");
             ArgumentCaptor<ResetPasswordRequestEvent> captor = ArgumentCaptor.forClass(ResetPasswordRequestEvent.class);
             verify(publisher).publishEvent(captor.capture());
             ResetPasswordRequestEvent event = captor.getValue();
    -        assertThat((String) event.getSource(), equalTo("user@example.com"));
    +        assertThat(event.getSource(), equalTo("user@example.com"));
             assertThat(event.getCode(), equalTo("code"));
             assertThat(event.getAuthentication(), sameInstance(authentication));
         }
    @@ -185,7 +191,7 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             user.setMeta(new ScimMeta(new Date(), new Date(), 0));
             user.setPrimaryEmail("foo@example.com");
             ExpiringCode expiringCode = new ExpiringCode("good_code",
    -            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "{\"user_id\":\"user-id\",\"username\":\"username\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null);
             when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
             when(scimUserProvisioning.retrieve("user-id")).thenReturn(user);
             when(scimUserProvisioning.checkPasswordMatches("user-id", "Passwo3dAsOld"))
    @@ -202,6 +208,22 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             }
         }
     
    +    @Test
    +    public void resetPassword_InvalidCodeData() {
    +        ExpiringCode expiringCode = new ExpiringCode("good_code",
    +                new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +        when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
    +        SecurityContext securityContext = mock(SecurityContext.class);
    +        when(securityContext.getAuthentication()).thenReturn(new MockAuthentication());
    +        SecurityContextHolder.setContext(securityContext);
    +        try {
    +            emailResetPasswordService.resetPassword("good_code", "password");
    +            fail();
    +        } catch (InvalidCodeException e) {
    +            assertEquals("Sorry, your reset password link is no longer valid. Please request a new one", e.getMessage());
    +        }
    +    }
    +
         @Test
         public void resetPassword_WithInvalidClientId() {
             setupResetPassword("invalid_client", "redirect.example.com");
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointTest.java+14 9 modified
    @@ -81,10 +81,10 @@ public void setUp() throws Exception {
     
             PasswordChange change = new PasswordChange("id001", "user@example.com", yesterday, null, null);
     
    -        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "id001", null));
     
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
            }
     
    @@ -100,7 +100,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
                     .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", email, yesterday, clientId, redirectUri);
    -        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    @@ -113,7 +113,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -137,7 +137,7 @@ public void password_reset_without_client_id_and_without_redirect_uri() throws E
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -206,7 +206,7 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
                 .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", "user\"'@example.com", yesterday, null, null);
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    @@ -237,7 +237,8 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
         @Test
         public void testChangingAPasswordWithAValidCode() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                        "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -281,7 +282,9 @@ public void changing_password_with_invalid_code() throws Exception {
         @Test
         public void testChangingAPasswordForUnverifiedUser() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -338,7 +341,9 @@ public void changePassword_Returns422UnprocessableEntity_NewPasswordSameAsOld()
             Mockito.reset(passwordValidator);
     
             when(expiringCodeStore.retrieveCode("emailed_code"))
    -            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis()-(1000*60*60*24)), new Date(System.currentTimeMillis()-(1000*60*60*24)), 0));
    
  • uaa/src/main/webapp/WEB-INF/spring/codestore-endpoints.xml+1 1 modified
    @@ -26,7 +26,7 @@
         <http  name="codeStoreSecurity" pattern="/Codes/**" create-session="stateless" authentication-manager-ref="emptyAuthenticationManager"
                entry-point-ref="oauthAuthenticationEntryPoint"
                xmlns="http://www.springframework.org/schema/security" use-expressions="true">
    -        <intercept-url pattern="/**" access="#oauth2.hasAnyScope('scim.create','scim.write','password.write')"/>
    +        <intercept-url pattern="/**" access="#oauth2.hasAnyScope('oauth.login')"/>
             <custom-filter ref="resourceAgnosticAuthenticationFilter" position="PRE_AUTH_FILTER" />
             <anonymous enabled="false" />
             <expression-handler ref="oauthWebExpressionHandler" />
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/AutologinIT.java+1 1 modified
    @@ -249,7 +249,7 @@ public void testFormEncodedAutologinRequest() throws Exception {
                     Map.class);
     
             String autologinCode = (String) autologinResponseEntity.getBody().get("code");
    -        assertEquals(6, autologinCode.length());
    +        assertEquals(10, autologinCode.length());
         }
     
         @Test
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/ChangePasswordIT.java+2 4 modified
    @@ -90,6 +90,7 @@ public void setUp() throws Exception {
     
         @Test
         public void testChangePassword() throws Exception {
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, NEW_PASSWORD, "new");
    @@ -108,7 +109,7 @@ public void displaysErrorWhenPasswordContravenesPolicy() {
             //the only policy we can contravene by default is the length
     
             String newPassword = new RandomValueStringGenerator(260).generate();
    -
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, newPassword, newPassword);
    @@ -134,11 +135,8 @@ private void signOut() {
         }
     
         private void signIn(String userName, String password) {
    -        webDriver.get(baseUrl + "/logout.do");
    -        webDriver.get(baseUrl + "/login");
             webDriver.findElement(By.name("username")).sendKeys(userName);
             webDriver.findElement(By.name("password")).sendKeys(password);
             webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();
    -        assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), containsString("Where to?"));
         }
     }
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/InvitationsIT.java+10 7 modified
    @@ -91,7 +91,7 @@ public class InvitationsIT {
         @Before
         public void setup() throws Exception {
             scimToken = testClient.getOAuthAccessToken("admin", "adminsecret", "client_credentials", "scim.read,scim.write");
    -        loginToken = testClient.getOAuthAccessToken("login", "loginsecret", "client_credentials", "password.write,scim.write");
    +        loginToken = testClient.getOAuthAccessToken("login", "loginsecret", "client_credentials", "oauth.login");
             screenShootRule.setWebDriver(webDriver);
         }
     
    @@ -199,9 +199,9 @@ private String createInvitation(String username, String userEmail, String redire
             return createInvitation(baseUrl, uaaUrl, username, userEmail, origin, redirectUri, loginToken, scimToken);
         }
     
    -    public static String createInvitation(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String scimWriteToken, String scimReadToken) {
    +    public static String createInvitation(String baseUrl, String uaaUrl, String username, String userEmail, String origin, String redirectUri, String loginToken, String scimToken) {
             HttpHeaders headers = new HttpHeaders();
    -        headers.add("Authorization", "Bearer " + scimWriteToken);
    +        headers.add("Authorization", "Bearer " + scimToken);
             RestTemplate uaaTemplate = new RestTemplate();
             ScimUser scimUser = new ScimUser();
             scimUser.setUserName(username);
    @@ -211,8 +211,8 @@ public static String createInvitation(String baseUrl, String uaaUrl, String user
     
             String userId = null;
             try {
    -            userId = IntegrationTestUtils.getUserIdByField(scimReadToken, baseUrl, origin, "email", userEmail);
    -            scimUser = IntegrationTestUtils.getUser(scimReadToken, baseUrl, userId);
    +            userId = IntegrationTestUtils.getUserIdByField(scimToken, baseUrl, origin, "email", userEmail);
    +            scimUser = IntegrationTestUtils.getUser(scimToken, baseUrl, userId);
             } catch (RuntimeException x) {
             }
             if (userId == null) {
    @@ -224,12 +224,15 @@ public static String createInvitation(String baseUrl, String uaaUrl, String user
                 userId = response.getBody().getId();
             } else {
                 scimUser.setVerified(false);
    -            IntegrationTestUtils.updateUser(scimWriteToken, uaaUrl, scimUser);
    +            IntegrationTestUtils.updateUser(scimToken, uaaUrl, scimUser);
             }
     
    +        HttpHeaders invitationHeaders = new HttpHeaders();
    +        invitationHeaders.add("Authorization", "Bearer " + loginToken);
    +
             Timestamp expiry = new Timestamp(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(System.currentTimeMillis() + 24 * 3600, TimeUnit.MILLISECONDS));
             ExpiringCode expiringCode = new ExpiringCode(null, expiry, "{\"origin\":\"" + origin + "\", \"client_id\":\"app\", \"redirect_uri\":\"" + redirectUri + "\", \"user_id\":\"" + userId + "\", \"email\":\"" + userEmail + "\"}", null);
    -        HttpEntity<ExpiringCode> expiringCodeRequest = new HttpEntity<>(expiringCode, headers);
    +        HttpEntity<ExpiringCode> expiringCodeRequest = new HttpEntity<>(expiringCode, invitationHeaders);
             ResponseEntity<ExpiringCode> expiringCodeResponse = uaaTemplate.exchange(uaaUrl + "/Codes", HttpMethod.POST, expiringCodeRequest, ExpiringCode.class);
             expiringCode = expiringCodeResponse.getBody();
             return expiringCode.getCode();
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/AccountsControllerMockMvcTests.java+7 1 modified
    @@ -3,6 +3,7 @@
     import com.dumbster.smtp.SimpleSmtpServer;
     import com.dumbster.smtp.SmtpMessage;
     import org.apache.commons.lang3.RandomStringUtils;
    +import org.cloudfoundry.identity.uaa.account.EmailAccountCreationService;
     import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
     import org.cloudfoundry.identity.uaa.codestore.JdbcExpiringCodeStore;
     import org.cloudfoundry.identity.uaa.constants.OriginKeys;
    @@ -13,7 +14,6 @@
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.PredictableGenerator;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimUserProvisioning;
    -import org.cloudfoundry.identity.uaa.account.EmailAccountCreationService;
     import org.cloudfoundry.identity.uaa.test.TestClient;
     import org.cloudfoundry.identity.uaa.test.UaaTestAccounts;
     import org.cloudfoundry.identity.uaa.util.JsonUtils;
    @@ -99,6 +99,12 @@ public void restoreMailSender() {
             getWebApplicationContext().getBean("emailService", EmailService.class).setMailSender(originalSender);
         }
     
    +    @After
    +    public void resetGenerator() {
    +        getWebApplicationContext().getBean(JdbcExpiringCodeStore.class).setGenerator(new RandomValueStringGenerator(24));
    +    }
    +
    +
         @AfterClass
         public static void stopMailServer() throws Exception {
             if (mailServer!=null) {
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/LoginMockMvcTests.java+5 0 modified
    @@ -142,6 +142,11 @@ public void setUpContext() throws Exception {
             identityZoneConfiguration = getWebApplicationContext().getBean(IdentityZoneProvisioning.class).retrieve(getUaa().getId()).getConfig();
         }
     
    +    @After
    +    public void resetGenerator() {
    +        getWebApplicationContext().getBean(JdbcExpiringCodeStore.class).setGenerator(new RandomValueStringGenerator(24));
    +    }
    +
         @After
         public void tearDown() throws Exception {
             //restore all properties
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java+42 5 modified
    @@ -12,6 +12,7 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    +import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    @@ -20,14 +21,15 @@
     import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.PredictableGenerator;
    -import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
     import org.cloudfoundry.identity.uaa.scim.endpoints.PasswordChange;
     import org.cloudfoundry.identity.uaa.util.JsonUtils;
    +import org.junit.After;
     import org.junit.Before;
     import org.junit.Test;
     import org.springframework.http.MediaType;
    +import org.springframework.jdbc.core.JdbcTemplate;
     import org.springframework.mock.web.MockHttpServletRequest;
     import org.springframework.mock.web.MockHttpSession;
     import org.springframework.security.core.Authentication;
    @@ -73,6 +75,12 @@ public void initResetPasswordTest() throws Exception {
             codeStore = getWebApplicationContext().getBean(ExpiringCodeStore.class);
         }
     
    +    @After
    +    public void resetGenerator() {
    +        getWebApplicationContext().getBean(JdbcExpiringCodeStore.class).setGenerator(new RandomValueStringGenerator(24));
    +    }
    +
    +
         @Test
         public void testResettingAPasswordUsingUsernameToEnsureNoModification() throws Exception {
     
    @@ -156,6 +164,34 @@ public void testResettingAPasswordChangesCodeInForm() throws Exception {
                 .andExpect(redirectedUrl("home"));
         }
     
    +    @Test
    +    public void create_new_code_for_repeated_request() throws Exception {
    +        String username = new RandomValueStringGenerator().generate() + "@test.org";
    +        ScimUser user = new ScimUser(null, username, "givenname","familyname");
    +        user.setPrimaryEmail(username);
    +        user.setPassword("secret");
    +        String token = MockMvcUtils.utils().getClientCredentialsOAuthAccessToken(getMockMvc(), "admin", "adminsecret", null, null);
    +        user = MockMvcUtils.utils().createUser(getMockMvc(), token, user);
    +
    +
    +        PredictableGenerator generator = new PredictableGenerator();
    +        JdbcExpiringCodeStore store = getWebApplicationContext().getBean(JdbcExpiringCodeStore.class);
    +        store.setGenerator(generator);
    +        JdbcTemplate template = getWebApplicationContext().getBean(JdbcTemplate.class);
    +        String intent = UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId();
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        assertEquals(1, (int)template.queryForObject("select count(*) from expiring_code_store where intent=?", new Object[] {intent}, Integer.class));
    +
    +    }
    +
         @Test
         public void redirectToSavedRequest_ifPresent() throws Exception {
             String username = new RandomValueStringGenerator().generate() + "@test.org";
    @@ -239,7 +275,8 @@ public void testResettingAPasswordUsingTimestampForUserModification() throws Exc
             List<ScimUser> users = getWebApplicationContext().getBean(ScimUserProvisioning.class).query("username eq \"marissa\"");
             assertNotNull(users);
             assertEquals(1, users.size());
    -        ExpiringCode code = codeStore.generateCode(users.get(0).getId(), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(users.get(0).getId(), users.get(0).getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
     
             MockHttpServletRequestBuilder post = createChangePasswordRequest(users.get(0), code,
                 true, "newpassw0rD", "newpassw0rD");
    @@ -266,11 +303,11 @@ public void resetPassword_ReturnsUnprocessableEntity_NewPasswordSameAsOld() thro
             assertNotNull(users);
             assertEquals(1, users.size());
             ScimUser user = users.get(0);
    -
    -        ExpiringCode code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(user.getId(), user.getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"));
     
    -        code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"))
                 .andExpect(status().isUnprocessableEntity())
                 .andExpect(view().name("forgot_password"))
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/codestore/ExpiringCodeStoreMockMvcTests.java+1 1 modified
    @@ -43,7 +43,7 @@ public class ExpiringCodeStoreMockMvcTests extends InjectedMockContextTest {
         @Before
         public void setUp() throws Exception {
             testClient = new TestClient(getMockMvc());
    -        loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", null);
    +        loginToken = testClient.getClientCredentialsOAuthAccessToken("login", "loginsecret", "oauth.login");
             getWebApplicationContext().getBean(JdbcTemplate.class).update("DELETE FROM expiring_code_store ");
         }
     
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/DisableUserManagementSecurityFilterMockMvcTest.java+7 1 modified
    @@ -351,8 +351,14 @@ public void changeEmailControllerVerifyEmailNotAllowed() throws Exception {
     
         @Test
         public void changePasswordControllerChangePasswordPageNotAllowed() throws Exception {
    +        MockMvcUtils.setDisableInternalUserManagement(false, getWebApplicationContext());
    +        
    +        ResultActions result = createUser();
    +        ScimUser createdUser = JsonUtils.readValue(result.andReturn().getResponse().getContentAsString(), ScimUser.class);
             MockMvcUtils.setDisableInternalUserManagement(true, getWebApplicationContext());
    -        getMockMvc().perform(get("/change_password"))
    +
    +        getMockMvc().perform(get("/change_password")
    +            .session(getUserSession(createdUser.getUserName(), PASSWD)))
                 .andExpect(status().isForbidden())
                 .andExpect(content()
                                .string(JsonObjectMatcherUtils.matchesJsonObject(
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointMockMvcTests.java+7 0 modified
    @@ -23,6 +23,7 @@
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.test.TestClient;
     import org.cloudfoundry.identity.uaa.util.JsonUtils;
    +import org.junit.After;
     import org.junit.Before;
     import org.junit.Test;
     import org.springframework.security.oauth2.common.util.OAuth2Utils;
    @@ -53,6 +54,7 @@ public class PasswordResetEndpointMockMvcTests extends InjectedMockContextTest {
     
         private String loginToken;
         private ScimUser user;
    +    private RandomValueStringGenerator originalGenerator;
     
         @Before
         public void setUp() throws Exception {
    @@ -65,6 +67,11 @@ public void setUp() throws Exception {
             user = MockMvcUtils.utils().createUser(getMockMvc(), adminToken, user);
         }
     
    +    @After
    +    public void resetGenerator() {
    +        getWebApplicationContext().getBean(JdbcExpiringCodeStore.class).setGenerator(new RandomValueStringGenerator(24));
    +    }
    +
         @Test
         public void changePassword_isSuccessful() throws Exception {
     
    
460627ed419e

Improve reset password experience

https://github.com/cloudfoundry/uaaFilip HanikMay 3, 2016via ghsa
4 files changed · +52 14
  • server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java+4 1 modified
    @@ -49,6 +49,7 @@
     public class UaaResetPasswordService implements ResetPasswordService, ApplicationEventPublisherAware {
     
         public static final int PASSWORD_RESET_LIFETIME = 30 * 60 * 1000;
    +    public static final String FORGOT_PASSWORD_INTENT_PREFIX = "forgot_password_for_id:";
     
         private final ScimUserProvisioning scimUserProvisioning;
         private final ExpiringCodeStore expiringCodeStore;
    @@ -143,7 +144,9 @@ public ForgotPasswordInfo forgotPassword(String email, String clientId, String r
             ScimUser scimUser = results.get(0);
     
             PasswordChange change = new PasswordChange(scimUser.getId(), scimUser.getUserName(), scimUser.getPasswordLastModified(), clientId, redirectUri);
    -        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), null);
    +        String intent = FORGOT_PASSWORD_INTENT_PREFIX+scimUser.getId();
    +        expiringCodeStore.expireByIntent(intent);
    +        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), intent);
             publish(new ResetPasswordRequestEvent(email, code.getCode(), SecurityContextHolder.getContext().getAuthentication()));
             return new ForgotPasswordInfo(scimUser.getId(), code);
         }
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java+12 6 modified
    @@ -12,15 +12,15 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    -import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
     import org.cloudfoundry.identity.uaa.account.ConflictException;
     import org.cloudfoundry.identity.uaa.account.ForgotPasswordInfo;
     import org.cloudfoundry.identity.uaa.account.NotFoundException;
     import org.cloudfoundry.identity.uaa.account.ResetPasswordService.ResetPasswordResponse;
     import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.account.event.ResetPasswordRequestEvent;
    +import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
     import org.cloudfoundry.identity.uaa.scim.ScimMeta;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
    @@ -93,18 +93,24 @@ public void forgotPassword_ResetCodeIsReturnedSuccessfully() throws Exception {
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
     
    +        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
    +
             when(codeStore.generateCode(eq("{\"user_id\":\"user-id-001\",\"username\":\"user@example.com\",\"passwordModifiedTime\":1234,\"client_id\":\"example\",\"redirect_uri\":\"redirect.example.com\"}"),
    -                                    any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +                                    any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             ForgotPasswordInfo forgotPasswordInfo = emailResetPasswordService.forgotPassword("user@example.com", "example", "redirect.example.com");
    -        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
     
    +        verify(codeStore).expireByIntent(captor.capture());
    +        assertEquals(UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId(), captor.getValue());
    +        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
             ExpiringCode resetPasswordCode = forgotPasswordInfo.getResetPasswordCode();
             assertThat(resetPasswordCode.getCode(), equalTo("code"));
             assertThat(resetPasswordCode.getExpiresAt(), equalTo(expiresAt));
             assertThat(resetPasswordCode.getData(), equalTo("user-id-001"));
         }
     
    +
    +
         @Test
         public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception {
             ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
    @@ -115,7 +121,7 @@ public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception
             user.setPrimaryEmail("user@example.com");
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
    -        when(codeStore.generateCode(anyString(), any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +        when(codeStore.generateCode(anyString(), any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             emailResetPasswordService.forgotPassword("user@example.com", "", "");
             ArgumentCaptor<ResetPasswordRequestEvent> captor = ArgumentCaptor.forClass(ResetPasswordRequestEvent.class);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointTest.java+6 6 modified
    @@ -81,10 +81,10 @@ public void setUp() throws Exception {
     
             PasswordChange change = new PasswordChange("id001", "user@example.com", yesterday, null, null);
     
    -        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "id001", null));
     
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
            }
     
    @@ -100,7 +100,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
                     .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", email, yesterday, clientId, redirectUri);
    -        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    @@ -113,7 +113,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -137,7 +137,7 @@ public void password_reset_without_client_id_and_without_redirect_uri() throws E
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -206,7 +206,7 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
                 .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", "user\"'@example.com", yesterday, null, null);
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java+30 1 modified
    @@ -12,6 +12,7 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    +import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    @@ -20,14 +21,14 @@
     import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.PredictableGenerator;
    -import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
     import org.cloudfoundry.identity.uaa.scim.endpoints.PasswordChange;
     import org.cloudfoundry.identity.uaa.util.JsonUtils;
     import org.junit.Before;
     import org.junit.Test;
     import org.springframework.http.MediaType;
    +import org.springframework.jdbc.core.JdbcTemplate;
     import org.springframework.mock.web.MockHttpServletRequest;
     import org.springframework.mock.web.MockHttpSession;
     import org.springframework.security.core.Authentication;
    @@ -156,6 +157,34 @@ public void testResettingAPasswordChangesCodeInForm() throws Exception {
                 .andExpect(redirectedUrl("home"));
         }
     
    +    @Test
    +    public void create_new_code_for_repeated_request() throws Exception {
    +        String username = new RandomValueStringGenerator().generate() + "@test.org";
    +        ScimUser user = new ScimUser(null, username, "givenname","familyname");
    +        user.setPrimaryEmail(username);
    +        user.setPassword("secret");
    +        String token = MockMvcUtils.utils().getClientCredentialsOAuthAccessToken(getMockMvc(), "admin", "adminsecret", null, null);
    +        user = MockMvcUtils.utils().createUser(getMockMvc(), token, user);
    +
    +
    +        PredictableGenerator generator = new PredictableGenerator();
    +        JdbcExpiringCodeStore store = getWebApplicationContext().getBean(JdbcExpiringCodeStore.class);
    +        store.setGenerator(generator);
    +        JdbcTemplate template = getWebApplicationContext().getBean(JdbcTemplate.class);
    +        String intent = UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId();
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        assertEquals(1, (int)template.queryForObject("select count(*) from expiring_code_store where intent=?", new Object[] {intent}, Integer.class));
    +
    +    }
    +
         @Test
         public void redirectToSavedRequest_ifPresent() throws Exception {
             String username = new RandomValueStringGenerator().generate() + "@test.org";
    
1d3ad7399d01

Improve reset password experience

https://github.com/cloudfoundry/uaaFilip HanikMay 3, 2016via ghsa
5 files changed · +55 15
  • server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java+4 1 modified
    @@ -49,6 +49,7 @@
     public class UaaResetPasswordService implements ResetPasswordService, ApplicationEventPublisherAware {
     
         public static final int PASSWORD_RESET_LIFETIME = 30 * 60 * 1000;
    +    public static final String FORGOT_PASSWORD_INTENT_PREFIX = "forgot_password_for_id:";
     
         private final ScimUserProvisioning scimUserProvisioning;
         private final ExpiringCodeStore expiringCodeStore;
    @@ -141,7 +142,9 @@ public ForgotPasswordInfo forgotPassword(String email, String clientId, String r
             ScimUser scimUser = results.get(0);
     
             PasswordChange change = new PasswordChange(scimUser.getId(), scimUser.getUserName(), scimUser.getPasswordLastModified(), clientId, redirectUri);
    -        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), null);
    +        String intent = FORGOT_PASSWORD_INTENT_PREFIX+scimUser.getId();
    +        expiringCodeStore.expireByIntent(intent);
    +        ExpiringCode code = expiringCodeStore.generateCode(JsonUtils.writeValueAsString(change), new Timestamp(System.currentTimeMillis() + PASSWORD_RESET_LIFETIME), intent);
             publish(new ResetPasswordRequestEvent(email, code.getCode(), SecurityContextHolder.getContext().getAuthentication()));
             return new ForgotPasswordInfo(scimUser.getId(), code);
         }
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java+12 6 modified
    @@ -12,15 +12,15 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    -import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    -import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
     import org.cloudfoundry.identity.uaa.account.ConflictException;
     import org.cloudfoundry.identity.uaa.account.ForgotPasswordInfo;
     import org.cloudfoundry.identity.uaa.account.NotFoundException;
     import org.cloudfoundry.identity.uaa.account.ResetPasswordService.ResetPasswordResponse;
     import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.account.event.ResetPasswordRequestEvent;
    +import org.cloudfoundry.identity.uaa.authentication.InvalidCodeException;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
    +import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
     import org.cloudfoundry.identity.uaa.scim.ScimMeta;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
    @@ -93,18 +93,24 @@ public void forgotPassword_ResetCodeIsReturnedSuccessfully() throws Exception {
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
     
    +        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
    +
             when(codeStore.generateCode(eq("{\"user_id\":\"user-id-001\",\"username\":\"user@example.com\",\"passwordModifiedTime\":1234,\"client_id\":\"example\",\"redirect_uri\":\"redirect.example.com\"}"),
    -                                    any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +                                    any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             ForgotPasswordInfo forgotPasswordInfo = emailResetPasswordService.forgotPassword("user@example.com", "example", "redirect.example.com");
    -        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
     
    +        verify(codeStore).expireByIntent(captor.capture());
    +        assertEquals(UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId(), captor.getValue());
    +        assertThat(forgotPasswordInfo.getUserId(), equalTo("user-id-001"));
             ExpiringCode resetPasswordCode = forgotPasswordInfo.getResetPasswordCode();
             assertThat(resetPasswordCode.getCode(), equalTo("code"));
             assertThat(resetPasswordCode.getExpiresAt(), equalTo(expiresAt));
             assertThat(resetPasswordCode.getData(), equalTo("user-id-001"));
         }
     
    +
    +
         @Test
         public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception {
             ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
    @@ -115,7 +121,7 @@ public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception
             user.setPrimaryEmail("user@example.com");
             when(scimUserProvisioning.query(contains("origin"))).thenReturn(Arrays.asList(user));
             Timestamp expiresAt = new Timestamp(System.currentTimeMillis());
    -        when(codeStore.generateCode(anyString(), any(Timestamp.class), eq(null))).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
    +        when(codeStore.generateCode(anyString(), any(Timestamp.class), anyString())).thenReturn(new ExpiringCode("code", expiresAt, "user-id-001", null));
     
             emailResetPasswordService.forgotPassword("user@example.com", "", "");
             ArgumentCaptor<ResetPasswordRequestEvent> captor = ArgumentCaptor.forClass(ResetPasswordRequestEvent.class);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointTest.java+6 6 modified
    @@ -81,10 +81,10 @@ public void setUp() throws Exception {
     
             PasswordChange change = new PasswordChange("id001", "user@example.com", yesterday, null, null);
     
    -        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq("id001"), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "id001", null));
     
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
            }
     
    @@ -100,7 +100,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
                     .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", email, yesterday, clientId, redirectUri);
    -        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(anyString(), any(Timestamp.class), anyString()))
                     .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    @@ -113,7 +113,7 @@ public void password_reset_with_client_id_and_redirect_uri() throws Exception {
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -137,7 +137,7 @@ public void password_reset_without_client_id_and_without_redirect_uri() throws E
             mockMvc.perform(post)
                     .andExpect(status().isCreated());
     
    -        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null));
    +        verify(expiringCodeStore).generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString());
         }
     
         @Test
    @@ -206,7 +206,7 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
                 .thenReturn(Arrays.asList(user));
     
             PasswordChange change = new PasswordChange("id001", "user\"'@example.com", yesterday, null, null);
    -        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), eq(null)))
    +        when(expiringCodeStore.generateCode(eq(JsonUtils.writeValueAsString(change)), any(Timestamp.class), anyString()))
                 .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), JsonUtils.writeValueAsString(change), null));
     
             MockHttpServletRequestBuilder post = post("/password_resets")
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java+30 1 modified
    @@ -12,6 +12,7 @@
      *******************************************************************************/
     package org.cloudfoundry.identity.uaa.login;
     
    +import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
     import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
    @@ -20,14 +21,14 @@
     import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
     import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.PredictableGenerator;
    -import org.cloudfoundry.identity.uaa.account.UaaResetPasswordService;
     import org.cloudfoundry.identity.uaa.scim.ScimUser;
     import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
     import org.cloudfoundry.identity.uaa.scim.endpoints.PasswordChange;
     import org.cloudfoundry.identity.uaa.util.JsonUtils;
     import org.junit.Before;
     import org.junit.Test;
     import org.springframework.http.MediaType;
    +import org.springframework.jdbc.core.JdbcTemplate;
     import org.springframework.mock.web.MockHttpServletRequest;
     import org.springframework.mock.web.MockHttpSession;
     import org.springframework.security.core.Authentication;
    @@ -156,6 +157,34 @@ public void testResettingAPasswordChangesCodeInForm() throws Exception {
                 .andExpect(redirectedUrl("home"));
         }
     
    +    @Test
    +    public void create_new_code_for_repeated_request() throws Exception {
    +        String username = new RandomValueStringGenerator().generate() + "@test.org";
    +        ScimUser user = new ScimUser(null, username, "givenname","familyname");
    +        user.setPrimaryEmail(username);
    +        user.setPassword("secret");
    +        String token = MockMvcUtils.utils().getClientCredentialsOAuthAccessToken(getMockMvc(), "admin", "adminsecret", null, null);
    +        user = MockMvcUtils.utils().createUser(getMockMvc(), token, user);
    +
    +
    +        PredictableGenerator generator = new PredictableGenerator();
    +        JdbcExpiringCodeStore store = getWebApplicationContext().getBean(JdbcExpiringCodeStore.class);
    +        store.setGenerator(generator);
    +        JdbcTemplate template = getWebApplicationContext().getBean(JdbcTemplate.class);
    +        String intent = UaaResetPasswordService.FORGOT_PASSWORD_INTENT_PREFIX+user.getId();
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        getMockMvc().perform(post("/forgot_password.do")
    +                                 .param("email", user.getUserName()))
    +            .andExpect(redirectedUrl("email_sent?code=reset_password"));
    +
    +        assertEquals(1, (int)template.queryForObject("select count(*) from expiring_code_store where intent=?", new Object[] {intent}, Integer.class));
    +
    +    }
    +
         @Test
         public void redirectToSavedRequest_ifPresent() throws Exception {
             String username = new RandomValueStringGenerator().generate() + "@test.org";
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/ScimUserEndpointDocs.java+3 1 modified
    @@ -57,6 +57,7 @@
     import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
     import static org.springframework.restdocs.snippet.Attributes.key;
     import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
     import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
     
     public class ScimUserEndpointDocs extends InjectedMockContextTest {
    @@ -458,7 +459,7 @@ public void test_Change_Password() throws Exception {
         public void getUserVerificationLink() throws Exception {
             String accessToken = testClient.getClientCredentialsOAuthAccessToken("admin", "adminsecret", "uaa.admin");
     
    -        String email = "joel@example.com";
    +        String email = "joel"+new RandomValueStringGenerator().generate()+"@example.com";
             ScimUser joel = new ScimUser(null, email, "Joel", "D'sa");
             joel.setVerified(false);
             joel.addEmail(email);
    @@ -477,6 +478,7 @@ public void getUserVerificationLink() throws Exception {
                 RequestDocumentation.parameterWithName("userId").description("The ID of the user to verify")
             );
             getMockMvc().perform(get)
    +            .andDo(print())
                 .andExpect(status().isOk())
                 .andDo(document("{ClassName}/{methodName}", preprocessResponse(prettyPrint()),
                                 pathParameters, requestHeaders, requestParameters, responseFields))
    
b3834364ab57

Do no update password when existing user accepts invite

https://github.com/cloudfoundry/uaaMadhura BhaveMay 3, 2016via ghsa
3 files changed · +20 4
  • server/src/main/java/org/cloudfoundry/identity/uaa/invitations/EmailInvitationsService.java+1 1 modified
    @@ -54,7 +54,7 @@ public AcceptedInvitation acceptInvitation(String code, String password) {
             user = scimUserProvisioning.verifyUser(userId, user.getVersion());
     
     
    -        if (OriginKeys.UAA.equals(user.getOrigin())) {
    +        if (OriginKeys.UAA.equals(user.getOrigin()) && StringUtils.hasText(password)) {
                 PasswordChangeRequest request = new PasswordChangeRequest();
                 request.setPassword(password);
                 scimUserProvisioning.changePassword(userId, null, password);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/invitations/InvitationsControllerTest.java+1 1 modified
    @@ -306,7 +306,7 @@ public void acceptInvitePage_for_verifiedUser() throws Exception {
     
             when(expiringCodeStore.retrieveCode("the_secret_code")).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
             when(expiringCodeStore.generateCode(anyString(), anyObject(), eq(null))).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(codeData), null));
    -        when(invitationsService.acceptInvitation(anyString(), anyString())).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
    +        when(invitationsService.acceptInvitation(anyString(), eq(""))).thenReturn(new InvitationsService.AcceptedInvitation("blah.test.com", new ScimUser()));
             IdentityProvider provider = new IdentityProvider();
             provider.setType(OriginKeys.UAA);
             when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(provider);
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/EmailInvitationsServiceTests.java+18 2 modified
    @@ -14,7 +14,6 @@
     import org.junit.Test;
     import org.junit.runner.RunWith;
     import org.springframework.beans.factory.annotation.Autowired;
    -import org.springframework.beans.factory.annotation.Qualifier;
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.annotation.Configuration;
     import org.springframework.context.annotation.Import;
    @@ -32,7 +31,6 @@
     import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
     import org.springframework.web.servlet.config.annotation.EnableWebMvc;
     import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    -import org.thymeleaf.spring4.SpringTemplateEngine;
     
     import java.sql.Timestamp;
     import java.util.HashMap;
    @@ -48,6 +46,7 @@
     import static org.mockito.Matchers.eq;
     import static org.mockito.Mockito.doThrow;
     import static org.mockito.Mockito.mock;
    +import static org.mockito.Mockito.never;
     import static org.mockito.Mockito.verify;
     import static org.mockito.Mockito.when;
     import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID;
    @@ -108,6 +107,23 @@ public void acceptInvitationNoClientId() throws Exception {
             assertEquals("/home", redirectLocation);
         }
     
    +    @Test
    +    public void acceptInvitation_withoutPasswordUpdate() throws Exception {
    +        ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    +        user.setOrigin(UAA);
    +        when(scimUserProvisioning.retrieve(eq("user-id-001"))).thenReturn(user);
    +        when(scimUserProvisioning.verifyUser(anyString(), anyInt())).thenReturn(user);
    +
    +        Map<String,String> userData = new HashMap<>();
    +        userData.put(USER_ID, "user-id-001");
    +        userData.put(EMAIL, "user@example.com");
    +        when(expiringCodeStore.retrieveCode(anyString())).thenReturn(new ExpiringCode("code", new Timestamp(System.currentTimeMillis()), JsonUtils.writeValueAsString(userData), null));
    +
    +        emailInvitationsService.acceptInvitation("code", "").getRedirectUri();
    +        verify(scimUserProvisioning).verifyUser(user.getId(), user.getVersion());
    +        verify(scimUserProvisioning, never()).changePassword(anyString(), anyString(), anyString());
    +    }
    +
         @Test
         public void acceptInvitationWithClientNotFound() throws Exception {
             ScimUser user = new ScimUser("user-id-001", "user@example.com", "first", "last");
    
14350228989e

Change password page required full authentication to access

https://github.com/cloudfoundry/uaaMadhura BhaveMay 3, 2016via ghsa
3 files changed · +10 6
  • server/src/main/resources/login-ui.xml+1 1 modified
    @@ -151,7 +151,7 @@
               entry-point-ref="loginEntryPoint"
               use-expressions="false"
               xmlns="http://www.springframework.org/schema/security">
    -        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    +        <intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
             <csrf disabled="false"/>
             <access-denied-handler ref="loginEntryPoint"/>
         </http>
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/ChangePasswordIT.java+2 4 modified
    @@ -90,6 +90,7 @@ public void setUp() throws Exception {
     
         @Test
         public void testChangePassword() throws Exception {
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, NEW_PASSWORD, "new");
    @@ -108,7 +109,7 @@ public void displaysErrorWhenPasswordContravenesPolicy() {
             //the only policy we can contravene by default is the length
     
             String newPassword = new RandomValueStringGenerator(260).generate();
    -
    +        webDriver.get(baseUrl + "/change_password");
             signIn(userEmail, PASSWORD);
     
             changePassword(PASSWORD, newPassword, newPassword);
    @@ -134,11 +135,8 @@ private void signOut() {
         }
     
         private void signIn(String userName, String password) {
    -        webDriver.get(baseUrl + "/logout.do");
    -        webDriver.get(baseUrl + "/login");
             webDriver.findElement(By.name("username")).sendKeys(userName);
             webDriver.findElement(By.name("password")).sendKeys(password);
             webDriver.findElement(By.xpath("//input[@value='Sign in']")).click();
    -        assertThat(webDriver.findElement(By.cssSelector("h1")).getText(), containsString("Where to?"));
         }
     }
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/zones/DisableUserManagementSecurityFilterMockMvcTest.java+7 1 modified
    @@ -351,8 +351,14 @@ public void changeEmailControllerVerifyEmailNotAllowed() throws Exception {
     
         @Test
         public void changePasswordControllerChangePasswordPageNotAllowed() throws Exception {
    +        MockMvcUtils.setDisableInternalUserManagement(false, getWebApplicationContext());
    +        
    +        ResultActions result = createUser();
    +        ScimUser createdUser = JsonUtils.readValue(result.andReturn().getResponse().getContentAsString(), ScimUser.class);
             MockMvcUtils.setDisableInternalUserManagement(true, getWebApplicationContext());
    -        getMockMvc().perform(get("/change_password"))
    +
    +        getMockMvc().perform(get("/change_password")
    +            .session(getUserSession(createdUser.getUserName(), PASSWD)))
                 .andExpect(status().isForbidden())
                 .andExpect(content()
                                .string(JsonObjectMatcherUtils.matchesJsonObject(
    
66132926f1ba

Only Reset Password code data should deserialize to PasswordChange

https://github.com/cloudfoundry/uaaMadhura BhaveMay 3, 2016via ghsa
5 files changed · +44 22
  • server/src/main/java/org/cloudfoundry/identity/uaa/account/UaaResetPasswordService.java+13 11 modified
    @@ -79,20 +79,22 @@ private ResetPasswordResponse changePasswordCodeAuthenticated(String code, Strin
                 throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
             String userId;
    -        String userName = null;
    -        Date passwordLastModified = null;
    -        String clientId = null;
    -        String redirectUri = null;
    +        String userName;
    +        Date passwordLastModified;
    +        String clientId;
    +        String redirectUri;
    +        PasswordChange change;
             try {
    -            PasswordChange change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
    -            userId = change.getUserId();
    -            userName = change.getUsername();
    -            passwordLastModified = change.getPasswordModifiedTime();
    -            clientId = change.getClientId();
    -            redirectUri = change.getRedirectUri();
    +            change = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
             } catch (JsonUtils.JsonUtilException x) {
    -            userId = expiringCode.getData();
    +            throw new InvalidCodeException("invalid_code", "Sorry, your reset password link is no longer valid. Please request a new one", 422);
             }
    +        userId = change.getUserId();
    +        userName = change.getUsername();
    +        passwordLastModified = change.getPasswordModifiedTime();
    +        clientId = change.getClientId();
    +        redirectUri = change.getRedirectUri();
    +
             ScimUser user = scimUserProvisioning.retrieve(userId);
             try {
                 if (isUserModified(user, expiringCode.getExpiresAt(), userName, passwordLastModified)) {
    
  • server/src/main/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordChange.java+0 2 modified
    @@ -1,11 +1,9 @@
     package org.cloudfoundry.identity.uaa.scim.endpoints;
     
    -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
     import com.fasterxml.jackson.annotation.JsonProperty;
     
     import java.util.Date;
     
    -@JsonIgnoreProperties(ignoreUnknown = true)
     public class PasswordChange {
         public PasswordChange() {}
     
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/login/UaaResetPasswordServiceTests.java+18 2 modified
    @@ -121,7 +121,7 @@ public void forgotPassword_PublishesResetPasswordRequestEvent() throws Exception
             ArgumentCaptor<ResetPasswordRequestEvent> captor = ArgumentCaptor.forClass(ResetPasswordRequestEvent.class);
             verify(publisher).publishEvent(captor.capture());
             ResetPasswordRequestEvent event = captor.getValue();
    -        assertThat((String) event.getSource(), equalTo("user@example.com"));
    +        assertThat(event.getSource(), equalTo("user@example.com"));
             assertThat(event.getCode(), equalTo("code"));
             assertThat(event.getAuthentication(), sameInstance(authentication));
         }
    @@ -185,7 +185,7 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             user.setMeta(new ScimMeta(new Date(), new Date(), 0));
             user.setPrimaryEmail("foo@example.com");
             ExpiringCode expiringCode = new ExpiringCode("good_code",
    -            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +            new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "{\"user_id\":\"user-id\",\"username\":\"username\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null);
             when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
             when(scimUserProvisioning.retrieve("user-id")).thenReturn(user);
             when(scimUserProvisioning.checkPasswordMatches("user-id", "Passwo3dAsOld"))
    @@ -202,6 +202,22 @@ public void resetPassword_InvalidPasswordException_NewPasswordSameAsOld() {
             }
         }
     
    +    @Test
    +    public void resetPassword_InvalidCodeData() {
    +        ExpiringCode expiringCode = new ExpiringCode("good_code",
    +                new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "user-id", null);
    +        when(codeStore.retrieveCode("good_code")).thenReturn(expiringCode);
    +        SecurityContext securityContext = mock(SecurityContext.class);
    +        when(securityContext.getAuthentication()).thenReturn(new MockAuthentication());
    +        SecurityContextHolder.setContext(securityContext);
    +        try {
    +            emailResetPasswordService.resetPassword("good_code", "password");
    +            fail();
    +        } catch (InvalidCodeException e) {
    +            assertEquals("Sorry, your reset password link is no longer valid. Please request a new one", e.getMessage());
    +        }
    +    }
    +
         @Test
         public void resetPassword_WithInvalidClientId() {
             setupResetPassword("invalid_client", "redirect.example.com");
    
  • server/src/test/java/org/cloudfoundry/identity/uaa/scim/endpoints/PasswordResetEndpointTest.java+8 3 modified
    @@ -237,7 +237,8 @@ public void testCreatingAPasswordResetWithAUsernameContainingSpecialCharacters()
         @Test
         public void testChangingAPasswordWithAValidCode() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +                .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                        "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}", null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -281,7 +282,9 @@ public void changing_password_with_invalid_code() throws Exception {
         @Test
         public void testChangingAPasswordForUnverifiedUser() throws Exception {
             when(expiringCodeStore.retrieveCode("secret_code"))
    -            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("secret_code", new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)), 0));
    @@ -338,7 +341,9 @@ public void changePassword_Returns422UnprocessableEntity_NewPasswordSameAsOld()
             Mockito.reset(passwordValidator);
     
             when(expiringCodeStore.retrieveCode("emailed_code"))
    -            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), "eyedee", null));
    +            .thenReturn(new ExpiringCode("emailed_code", new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME),
    +                    "{\"user_id\":\"eyedee\",\"username\":\"user@example.com\",\"passwordModifiedTime\":null,\"client_id\":\"\",\"redirect_uri\":\"\"}",
    +                    null));
     
             ScimUser scimUser = new ScimUser("eyedee", "user@example.com", "User", "Man");
             scimUser.setMeta(new ScimMeta(new Date(System.currentTimeMillis()-(1000*60*60*24)), new Date(System.currentTimeMillis()-(1000*60*60*24)), 0));
    
  • uaa/src/test/java/org/cloudfoundry/identity/uaa/login/ResetPasswordControllerMockMvcTests.java+5 4 modified
    @@ -239,7 +239,8 @@ public void testResettingAPasswordUsingTimestampForUserModification() throws Exc
             List<ScimUser> users = getWebApplicationContext().getBean(ScimUserProvisioning.class).query("username eq \"marissa\"");
             assertNotNull(users);
             assertEquals(1, users.size());
    -        ExpiringCode code = codeStore.generateCode(users.get(0).getId(), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(users.get(0).getId(), users.get(0).getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis()+ UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
     
             MockHttpServletRequestBuilder post = createChangePasswordRequest(users.get(0), code,
                 true, "newpassw0rD", "newpassw0rD");
    @@ -266,11 +267,11 @@ public void resetPassword_ReturnsUnprocessableEntity_NewPasswordSameAsOld() thro
             assertNotNull(users);
             assertEquals(1, users.size());
             ScimUser user = users.get(0);
    -
    -        ExpiringCode code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        PasswordChange passwordChange = new PasswordChange(user.getId(), user.getUserName(), null, null, null);
    +        ExpiringCode code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"));
     
    -        code = codeStore.generateCode(user.getId(), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
    +        code = codeStore.generateCode(JsonUtils.writeValueAsString(passwordChange), new Timestamp(System.currentTimeMillis() + UaaResetPasswordService.PASSWORD_RESET_LIFETIME), null);
             getMockMvc().perform(createChangePasswordRequest(user, code, true, "d3faultPasswd", "d3faultPasswd"))
                 .andExpect(status().isUnprocessableEntity())
                 .andExpect(view().name("forgot_password"))
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.