High severity7.5NVD Advisory· Published Mar 10, 2017· Updated May 13, 2026
CVE-2017-4960
CVE-2017-4960
Description
An issue was discovered in Cloud Foundry release v247 through v252, UAA stand-alone release v3.9.0 through v3.11.0, and UAA Bosh Release v21 through v26. There is a potential to subject the UAA OAuth clients to a denial of service attack.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | >= 3.10.0, < 3.12.0 | 3.12.0 |
org.cloudfoundry.identity:cloudfoundry-identity-serverMaven | < 3.9.8 | 3.9.8 |
Affected products
29cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:21:*:*:*:*:*:*:*+ 11 more
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:21:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:22:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:23:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.1:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.2:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.3:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.4:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.5:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:24.6:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:25:*:*:*:*:*:*:*
- cpe:2.3:a:cloudfoundry:cloud_foundry_uaa_bosh:26:*:*:*:*:*:*:*
cpe:2.3:a:pivotal_software:cloud_foundry:247.0:*:*:*:*:*:*:*+ 5 more
- cpe:2.3:a:pivotal_software:cloud_foundry:247.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry:248.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry:249.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry:250.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry:251.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry:252.0:*:*:*:*:*:*:*
cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.10.0:*:*:*:*:*:*:*+ 10 more
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.10.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.11.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.0:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.1:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.2:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.3:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.4:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.5:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.6:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.7:*:*:*:*:*:*:*
- cpe:2.3:a:pivotal_software:cloud_foundry_uaa:3.9.8:*:*:*:*:*:*:*
Patches
378731f8aa37aset a global flag to disable clientlockoutpolicy
5 files changed · +54 −109
model/src/main/java/org/cloudfoundry/identity/uaa/provider/LockoutPolicy.java+0 −7 modified@@ -1,7 +1,5 @@ package org.cloudfoundry.identity.uaa.provider; -import com.fasterxml.jackson.annotation.JsonIgnore; - public class LockoutPolicy { private int lockoutPeriodSeconds; private int lockoutAfterFailures; @@ -17,11 +15,6 @@ public LockoutPolicy(int countFailuresWithin, int lockoutAfterFailures, int lock this.lockoutPeriodSeconds = lockoutPeriodSeconds; } - @JsonIgnore - public boolean isLockoutEnabled() { - return countFailuresWithin > 0; - } - public LockoutPolicy setLockoutPeriodSeconds(int lockoutPeriod) { this.lockoutPeriodSeconds = lockoutPeriod; return this;
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ClientLockoutPolicyRetriever.java+15 −3 modified@@ -6,11 +6,18 @@ public class ClientLockoutPolicyRetriever implements LockoutPolicyRetriever { private LockoutPolicy defaultLockoutPolicy; - + private LockoutPolicy disabledLockoutPolicy = new LockoutPolicy(); + + private boolean isEnabled; + @Override public LockoutPolicy getLockoutPolicy() { - LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); - return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + if(isEnabled) { + LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); + return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + } else { + return disabledLockoutPolicy; + } } @Override @@ -22,4 +29,9 @@ public LockoutPolicy getDefaultLockoutPolicy() { public void setDefaultLockoutPolicy(LockoutPolicy defaultLockoutPolicy) { this.defaultLockoutPolicy = defaultLockoutPolicy; } + + public ClientLockoutPolicyRetriever setEnabled(boolean enabled) { + isEnabled = enabled; + return this; + } }
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/CommonLoginPolicy.java+0 −4 modified@@ -44,10 +44,6 @@ public CommonLoginPolicy(UaaAuditService auditService, public Result isAllowed(String principalId) { LockoutPolicy lockoutPolicy = lockoutPolicyRetriever.getLockoutPolicy(); - if (!lockoutPolicy.isLockoutEnabled()) { - return new Result(true, 0); - } - long eventsAfter = System.currentTimeMillis() - lockoutPolicy.getCountFailuresWithin() * 1000; List<AuditEvent> events = auditService.find(principalId, eventsAfter);
uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml+3 −2 modified@@ -255,6 +255,7 @@ </bean> <bean id="clientLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.ClientLockoutPolicyRetriever"> + <property name="enabled" value="${authentication.policy.client.lockoutEnabled:false}" /> <property name="defaultLockoutPolicy" ref="defaultClientLockoutPolicy" /> </bean> @@ -573,11 +574,11 @@ <bean id="defaultClientLockoutPolicy" class="org.cloudfoundry.identity.uaa.provider.LockoutPolicy"> <property name="lockoutAfterFailures" - value="${authentication.policy.global.lockoutAfterFailures:5}"/> + value="${authentication.policy.global.lockoutAfterFailures:-1}"/> <property name="countFailuresWithin" value="${authentication.policy.global.countFailuresWithinSeconds:-1}"/> <property name="lockoutPeriodSeconds" - value="${authentication.policy.global.lockoutPeriodSeconds:300}"/> + value="${authentication.policy.global.lockoutPeriodSeconds:-1}"/> </bean> <bean id="globalUserLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.UserLockoutPolicyRetriever">
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java+36 −93 modified@@ -25,12 +25,7 @@ import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt; import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper; -import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants; -import org.cloudfoundry.identity.uaa.oauth.token.Claims; -import org.cloudfoundry.identity.uaa.oauth.token.CompositeAccessToken; -import org.cloudfoundry.identity.uaa.oauth.token.JdbcRevocableTokenProvisioning; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableToken; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableTokenProvisioning; +import org.cloudfoundry.identity.uaa.oauth.token.*; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.PasswordPolicy; import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition; @@ -81,69 +76,30 @@ import java.net.URL; import java.net.URLDecoder; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createClient; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createUser; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getClientCredentialsOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getUserOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.setDisableInternalAuth; +import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.*; import static org.cloudfoundry.identity.uaa.oauth.UaaTokenServicesTests.PASSWORD; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT_NONE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.OPAQUE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REFRESH_TOKEN_SUFFIX; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REQUEST_TOKEN_FORMAT; +import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.*; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.StringStartsWith.startsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.security.oauth2.common.OAuth2AccessToken.ACCESS_TOKEN; import static org.springframework.security.oauth2.common.OAuth2AccessToken.REFRESH_TOKEN; import static org.springframework.security.oauth2.common.util.OAuth2Utils.GRANT_TYPE; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class TokenMvcMockTests extends AbstractTokenMockMvcTests { @@ -2352,17 +2308,8 @@ public void clientCredentials_byDefault_willNotLockoutClientsUsingFormData() thr String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); - for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET)) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInBody(clientId); } getMockMvc() @@ -2383,57 +2330,53 @@ public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuth() th setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInHeader(clientId); } - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); + login(clientId); } @Test public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuthAndFormData() throws Exception { String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); + for (int i = 0; i < 3; i++) { + tryLoginWithWrongSecretInHeader(clientId); + tryLoginWithWrongSecretInBody(clientId); + } + + login(clientId); + } - String body = null; - for(int i = 0; i < 3; i++){ - body = getMockMvc().perform(post("/oauth/token") + private void tryLoginWithWrongSecretInHeader(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isUnauthorized()) .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET) - ) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); - - } + private void tryLoginWithWrongSecretInBody(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("grant_type", "client_credentials") + .param("client_id", clientId) + .param("client_secret", BADSECRET) + ) + .andExpect(status().isUnauthorized()) + .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") + private void login(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); }
17a0b86afe1fWIP: set a global flag to disable clientlockoutpolicy
5 files changed · +63 −126
model/src/main/java/org/cloudfoundry/identity/uaa/provider/LockoutPolicy.java+0 −7 modified@@ -1,7 +1,5 @@ package org.cloudfoundry.identity.uaa.provider; -import com.fasterxml.jackson.annotation.JsonIgnore; - public class LockoutPolicy { private int lockoutPeriodSeconds; private int lockoutAfterFailures; @@ -17,11 +15,6 @@ public LockoutPolicy(int countFailuresWithin, int lockoutAfterFailures, int lock this.lockoutPeriodSeconds = lockoutPeriodSeconds; } - @JsonIgnore - public boolean isLockoutEnabled() { - return countFailuresWithin > 0; - } - public LockoutPolicy setLockoutPeriodSeconds(int lockoutPeriod) { this.lockoutPeriodSeconds = lockoutPeriod; return this;
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ClientLockoutPolicyRetriever.java+15 −3 modified@@ -6,11 +6,18 @@ public class ClientLockoutPolicyRetriever implements LockoutPolicyRetriever { private LockoutPolicy defaultLockoutPolicy; - + private LockoutPolicy disabledLockoutPolicy = new LockoutPolicy(); + + private boolean isEnabled; + @Override public LockoutPolicy getLockoutPolicy() { - LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); - return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + if(isEnabled) { + LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); + return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + } else { + return disabledLockoutPolicy; + } } @Override @@ -22,4 +29,9 @@ public LockoutPolicy getDefaultLockoutPolicy() { public void setDefaultLockoutPolicy(LockoutPolicy defaultLockoutPolicy) { this.defaultLockoutPolicy = defaultLockoutPolicy; } + + public ClientLockoutPolicyRetriever setEnabled(boolean enabled) { + isEnabled = enabled; + return this; + } }
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/CommonLoginPolicy.java+0 −4 modified@@ -48,10 +48,6 @@ public CommonLoginPolicy(UaaAuditService auditService, public Result isAllowed(String principalId) { LockoutPolicy lockoutPolicy = lockoutPolicyRetriever.getLockoutPolicy(); - if (!lockoutPolicy.isLockoutEnabled()) { - return new Result(true, 0); - } - long eventsAfter = timeService.getCurrentTimeMillis() - lockoutPolicy.getCountFailuresWithin() * 1000; List<AuditEvent> events = auditService.find(principalId, eventsAfter);
uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml+3 −2 modified@@ -263,6 +263,7 @@ </bean> <bean id="clientLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.ClientLockoutPolicyRetriever"> + <property name="enabled" value="${authentication.policy.client.lockoutEnabled:false}" /> <property name="defaultLockoutPolicy" ref="defaultClientLockoutPolicy" /> </bean> @@ -583,11 +584,11 @@ <bean id="defaultClientLockoutPolicy" class="org.cloudfoundry.identity.uaa.provider.LockoutPolicy"> <property name="lockoutAfterFailures" - value="${authentication.policy.global.lockoutAfterFailures:5}"/> + value="${authentication.policy.global.lockoutAfterFailures:-1}"/> <property name="countFailuresWithin" value="${authentication.policy.global.countFailuresWithinSeconds:-1}"/> <property name="lockoutPeriodSeconds" - value="${authentication.policy.global.lockoutPeriodSeconds:300}"/> + value="${authentication.policy.global.lockoutPeriodSeconds:-1}"/> </bean> <bean id="globalUserLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.UserLockoutPolicyRetriever">
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java+45 −110 modified@@ -28,17 +28,8 @@ import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt; import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper; -import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants; -import org.cloudfoundry.identity.uaa.oauth.token.Claims; -import org.cloudfoundry.identity.uaa.oauth.token.CompositeAccessToken; -import org.cloudfoundry.identity.uaa.oauth.token.JdbcRevocableTokenProvisioning; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableToken; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableTokenProvisioning; -import org.cloudfoundry.identity.uaa.provider.IdentityProvider; -import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; -import org.cloudfoundry.identity.uaa.provider.PasswordPolicy; -import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; -import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition; +import org.cloudfoundry.identity.uaa.oauth.token.*; +import org.cloudfoundry.identity.uaa.provider.*; import org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils; import org.cloudfoundry.identity.uaa.provider.saml.idp.ZoneAwareIdpMetadataManager; import org.cloudfoundry.identity.uaa.scim.ScimGroup; @@ -56,14 +47,12 @@ import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.IdentityZoneProvisioning; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.*; import org.opensaml.xml.ConfigurationException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.MediaType; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; @@ -97,83 +86,39 @@ import java.net.URL; import java.net.URLDecoder; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import static java.util.Collections.emptySet; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createClient; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createUser; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getClientCredentialsOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getUserOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.setDisableInternalAuth; +import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.*; import static org.cloudfoundry.identity.uaa.oauth.UaaTokenServicesTests.PASSWORD; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_SAML2_BEARER; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT_NONE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.OPAQUE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REFRESH_TOKEN_SUFFIX; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REQUEST_TOKEN_FORMAT; +import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.*; import static org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils.createLocalSamlIdpDefinition; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.StringStartsWith.startsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.springframework.http.HttpHeaders.AUTHORIZATION; -import static org.springframework.http.HttpHeaders.CONTENT_TYPE; -import static org.springframework.http.HttpHeaders.HOST; +import static org.junit.Assert.*; +import static org.springframework.http.HttpHeaders.*; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.security.oauth2.common.OAuth2AccessToken.ACCESS_TOKEN; import static org.springframework.security.oauth2.common.OAuth2AccessToken.REFRESH_TOKEN; import static org.springframework.security.oauth2.common.util.OAuth2Utils.GRANT_TYPE; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class TokenMvcMockTests extends AbstractTokenMockMvcTests { private String BADSECRET = "badsecret"; private TestClient testClient; private RandomValueStringGenerator generator = new RandomValueStringGenerator(); - + private MockEnvironment mockEnvironment; private static SamlTestUtils samlTestUtils = new SamlTestUtils(); @BeforeClass @@ -185,7 +130,10 @@ public static void initializeSamlUtils() { } } - + @Before + public void setup () throws Exception { + mockEnvironment = ((MockEnvironment) getWebApplicationContext().getEnvironment()); + } @Override public void setUpContext() throws Exception { testClient = new TestClient(); @@ -2872,17 +2820,8 @@ public void clientCredentials_byDefault_willNotLockoutClientsUsingFormData() thr String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); - for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET)) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInBody(clientId); } getMockMvc() @@ -2903,57 +2842,53 @@ public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuth() th setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInHeader(clientId); } - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); + login(clientId); } @Test public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuthAndFormData() throws Exception { String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); + for (int i = 0; i < 3; i++) { + tryLoginWithWrongSecretInHeader(clientId); + tryLoginWithWrongSecretInBody(clientId); + } + + login(clientId); + } - String body = null; - for(int i = 0; i < 3; i++){ - body = getMockMvc().perform(post("/oauth/token") + private void tryLoginWithWrongSecretInHeader(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isUnauthorized()) .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET) - ) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); - - } + private void tryLoginWithWrongSecretInBody(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("grant_type", "client_credentials") + .param("client_id", clientId) + .param("client_secret", BADSECRET) + ) + .andExpect(status().isUnauthorized()) + .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") + private void login(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); }
5eab756eaf4bset a global flag to disable clientlockoutpolicy
5 files changed · +57 −116
model/src/main/java/org/cloudfoundry/identity/uaa/provider/LockoutPolicy.java+0 −7 modified@@ -1,7 +1,5 @@ package org.cloudfoundry.identity.uaa.provider; -import com.fasterxml.jackson.annotation.JsonIgnore; - public class LockoutPolicy { private int lockoutPeriodSeconds; private int lockoutAfterFailures; @@ -17,11 +15,6 @@ public LockoutPolicy(int countFailuresWithin, int lockoutAfterFailures, int lock this.lockoutPeriodSeconds = lockoutPeriodSeconds; } - @JsonIgnore - public boolean isLockoutEnabled() { - return countFailuresWithin > 0; - } - public LockoutPolicy setLockoutPeriodSeconds(int lockoutPeriod) { this.lockoutPeriodSeconds = lockoutPeriod; return this;
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/ClientLockoutPolicyRetriever.java+15 −3 modified@@ -6,11 +6,18 @@ public class ClientLockoutPolicyRetriever implements LockoutPolicyRetriever { private LockoutPolicy defaultLockoutPolicy; - + private LockoutPolicy disabledLockoutPolicy = new LockoutPolicy(); + + private boolean isEnabled; + @Override public LockoutPolicy getLockoutPolicy() { - LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); - return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + if(isEnabled) { + LockoutPolicy res = IdentityZoneHolder.get().getConfig().getClientLockoutPolicy(); + return res.getLockoutAfterFailures() != -1 ? res : defaultLockoutPolicy; + } else { + return disabledLockoutPolicy; + } } @Override @@ -22,4 +29,9 @@ public LockoutPolicy getDefaultLockoutPolicy() { public void setDefaultLockoutPolicy(LockoutPolicy defaultLockoutPolicy) { this.defaultLockoutPolicy = defaultLockoutPolicy; } + + public ClientLockoutPolicyRetriever setEnabled(boolean enabled) { + isEnabled = enabled; + return this; + } }
server/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/CommonLoginPolicy.java+0 −4 modified@@ -48,10 +48,6 @@ public CommonLoginPolicy(UaaAuditService auditService, public Result isAllowed(String principalId) { LockoutPolicy lockoutPolicy = lockoutPolicyRetriever.getLockoutPolicy(); - if (!lockoutPolicy.isLockoutEnabled()) { - return new Result(true, 0); - } - long eventsAfter = timeService.getCurrentTimeMillis() - lockoutPolicy.getCountFailuresWithin() * 1000; List<AuditEvent> events = auditService.find(principalId, eventsAfter);
uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml+3 −2 modified@@ -263,6 +263,7 @@ </bean> <bean id="clientLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.ClientLockoutPolicyRetriever"> + <property name="enabled" value="${authentication.policy.client.lockoutEnabled:false}" /> <property name="defaultLockoutPolicy" ref="defaultClientLockoutPolicy" /> </bean> @@ -583,11 +584,11 @@ <bean id="defaultClientLockoutPolicy" class="org.cloudfoundry.identity.uaa.provider.LockoutPolicy"> <property name="lockoutAfterFailures" - value="${authentication.policy.global.lockoutAfterFailures:5}"/> + value="${authentication.policy.global.lockoutAfterFailures:-1}"/> <property name="countFailuresWithin" value="${authentication.policy.global.countFailuresWithinSeconds:-1}"/> <property name="lockoutPeriodSeconds" - value="${authentication.policy.global.lockoutPeriodSeconds:300}"/> + value="${authentication.policy.global.lockoutPeriodSeconds:-1}"/> </bean> <bean id="globalUserLockoutPolicyRetriever" class="org.cloudfoundry.identity.uaa.authentication.manager.UserLockoutPolicyRetriever">
uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/token/TokenMvcMockTests.java+39 −100 modified@@ -28,17 +28,8 @@ import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.oauth.jwt.Jwt; import org.cloudfoundry.identity.uaa.oauth.jwt.JwtHelper; -import org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants; -import org.cloudfoundry.identity.uaa.oauth.token.Claims; -import org.cloudfoundry.identity.uaa.oauth.token.CompositeAccessToken; -import org.cloudfoundry.identity.uaa.oauth.token.JdbcRevocableTokenProvisioning; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableToken; -import org.cloudfoundry.identity.uaa.oauth.token.RevocableTokenProvisioning; -import org.cloudfoundry.identity.uaa.provider.IdentityProvider; -import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; -import org.cloudfoundry.identity.uaa.provider.PasswordPolicy; -import org.cloudfoundry.identity.uaa.provider.SamlIdentityProviderDefinition; -import org.cloudfoundry.identity.uaa.provider.UaaIdentityProviderDefinition; +import org.cloudfoundry.identity.uaa.oauth.token.*; +import org.cloudfoundry.identity.uaa.provider.*; import org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils; import org.cloudfoundry.identity.uaa.provider.saml.idp.ZoneAwareIdpMetadataManager; import org.cloudfoundry.identity.uaa.scim.ScimGroup; @@ -63,6 +54,7 @@ import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.MediaType; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; @@ -95,80 +87,40 @@ import java.net.URL; import java.net.URLDecoder; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createClient; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.createUser; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getClientCredentialsOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.getUserOAuthAccessToken; -import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.setDisableInternalAuth; +import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.*; import static org.cloudfoundry.identity.uaa.oauth.UaaTokenServicesTests.PASSWORD; import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.JTI; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_SAML2_BEARER; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.ID_TOKEN_HINT_PROMPT_NONE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.OPAQUE; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REFRESH_TOKEN_SUFFIX; -import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.REQUEST_TOKEN_FORMAT; +import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.*; import static org.cloudfoundry.identity.uaa.provider.saml.idp.SamlTestUtils.createLocalSamlIdpDefinition; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.FORM_REDIRECT_PARAMETER; import static org.cloudfoundry.identity.uaa.web.UaaSavedRequestAwareAuthenticationSuccessHandler.SAVED_REQUEST_SESSION_ATTRIBUTE; import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.StringStartsWith.startsWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.springframework.http.HttpHeaders.AUTHORIZATION; import static org.springframework.http.HttpHeaders.HOST; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.security.oauth2.common.OAuth2AccessToken.ACCESS_TOKEN; import static org.springframework.security.oauth2.common.OAuth2AccessToken.REFRESH_TOKEN; import static org.springframework.security.oauth2.common.util.OAuth2Utils.GRANT_TYPE; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.cookie; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class TokenMvcMockTests extends AbstractTokenMockMvcTests { private String BADSECRET = "badsecret"; private TestClient testClient; private RandomValueStringGenerator generator = new RandomValueStringGenerator(); + private MockEnvironment mockEnvironment; public static final String IDP_ENTITY_ID = "cloudfoundry-saml-login"; private static SamlTestUtils samlTestUtils = new SamlTestUtils(); @@ -2732,17 +2684,8 @@ public void clientCredentials_byDefault_willNotLockoutClientsUsingFormData() thr String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); - for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET)) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInBody(clientId); } getMockMvc() @@ -2763,57 +2706,53 @@ public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuth() th setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); for(int i = 0; i < 6; i++){ - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); + tryLoginWithWrongSecretInHeader(clientId); } - getMockMvc() - .perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) - .param("grant_type", "client_credentials")) - .andExpect(status().isOk()) - .andReturn().getResponse().getContentAsString(); + login(clientId); } @Test public void clientCredentials_byDefault_WillNotLockoutDuringFailedBasicAuthAndFormData() throws Exception { String clientId = "testclient" + generator.generate(); String scopes = "space.*.developer,space.*.admin,org.*.reader,org.123*.admin,*.*,*"; setUpClients(clientId, scopes, scopes, GRANT_TYPES, true); + for (int i = 0; i < 3; i++) { + tryLoginWithWrongSecretInHeader(clientId); + tryLoginWithWrongSecretInBody(clientId); + } + + login(clientId); + } - String body = null; - for(int i = 0; i < 3; i++){ - body = getMockMvc().perform(post("/oauth/token") + private void tryLoginWithWrongSecretInHeader(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + BADSECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isUnauthorized()) .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") - .accept(MediaType.APPLICATION_JSON_VALUE) - .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .param("grant_type", "client_credentials") - .param("client_id", clientId) - .param("client_secret", BADSECRET) - ) - .andExpect(status().isUnauthorized()) - .andReturn().getResponse().getContentAsString(); - - } + private void tryLoginWithWrongSecretInBody(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE) + .param("grant_type", "client_credentials") + .param("client_id", clientId) + .param("client_secret", BADSECRET) + ) + .andExpect(status().isUnauthorized()) + .andReturn().getResponse().getContentAsString(); + } - body = getMockMvc().perform(post("/oauth/token") + private void login(String clientId) throws Exception { + getMockMvc().perform(post("/oauth/token") .accept(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Basic " + new String(Base64.encode((clientId + ":" + SECRET).getBytes()))) .param("grant_type", "client_credentials") - ) + ) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); }
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
9- www.securityfocus.com/bid/96780nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-hxgw-7539-pv7rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-4960ghsaADVISORY
- www.cloudfoundry.org/cve-2017-4960/nvdVendor Advisory
- github.com/cloudfoundry/uaa/commit/17a0b86afe1fbd4ed8819267906afa3f76a8dfdcghsaWEB
- github.com/cloudfoundry/uaa/commit/5eab756eaf4bb397302f00fbd0273f2470009d38ghsaWEB
- github.com/cloudfoundry/uaa/commit/78731f8aa37a53385d0194821a5356ab66e2138ghsaWEB
- web.archive.org/web/20200227185243/http://www.securityfocus.com/bid/96780ghsaWEB
- www.cloudfoundry.org/cve-2017-4960ghsaWEB
News mentions
0No linked articles in our index yet.