Skip to content

Commit

Permalink
Added GET endpoint. Denying access to update the UAA zone.
Browse files Browse the repository at this point in the history
[#88576464]
https://www.pivotaltracker.com/story/show/88576464

Signed-off-by: Madhura Bhave <mbhave@pivotal.io>
  • Loading branch information
Will Tran committed Feb 20, 2015
1 parent a2472ce commit 5ba3b73
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 28 deletions.
@@ -0,0 +1,19 @@
package org.cloudfoundry.identity.uaa.zone;

import org.springframework.security.access.AccessDeniedException;

public class DenyAccessToUaaAdvice {

public void checkIdentityZone(IdentityZone identityZone) {
if (IdentityZone.getUaa().equals(identityZone)) {
throw new AccessDeniedException("Access to UAA is not allowed.");
}
}

public void checkIdentityZoneId(String identityZoneId) {
if (IdentityZone.getUaa().getId().equals(identityZoneId)) {
throw new AccessDeniedException("Access to UAA is not allowed.");
}
}

}
Expand Up @@ -17,14 +17,13 @@
import java.util.UUID;

import org.cloudfoundry.identity.uaa.authentication.Origin;
import org.cloudfoundry.identity.uaa.oauth.ClientAdminEndpoints;
import org.cloudfoundry.identity.uaa.oauth.ClientDetailsValidator;
import org.cloudfoundry.identity.uaa.oauth.InvalidClientDetailsException;
import org.cloudfoundry.identity.uaa.util.UaaStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
Expand Down Expand Up @@ -64,6 +63,10 @@ public IdentityZoneEndpoints(IdentityZoneProvisioning zoneDao, IdentityProviderP
this.clientDetailsValidator = clientDetailsValidator;
}

@RequestMapping(value="{id}", method = GET)
public IdentityZone getIdentityZone(@PathVariable String id) {
return zoneDao.retrieve(id);
}

@RequestMapping(method = POST)
public ResponseEntity<IdentityZone> createIdentityZone(@RequestBody @Valid IdentityZoneCreationRequest body)
Expand Down Expand Up @@ -118,6 +121,7 @@ public ResponseEntity<IdentityZone> updateIdentityZone(
}
}
}
// ignore the id in the body, the id in the path is the only one that matters
body.getIdentityZone().setId(id);
IdentityZone updated = zoneDao.update(body.getIdentityZone());
IdentityZoneHolder.set(updated);
Expand Down Expand Up @@ -153,6 +157,11 @@ public ResponseEntity<Void> handleValidationException(MethodArgumentNotValidExce
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<Void> handleAccessDeniedException(MethodArgumentNotValidException e) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Void> handleException(Exception e) {
log.error(e.getClass()+": "+e.getMessage(),e);
Expand Down
3 changes: 2 additions & 1 deletion uaa/src/main/webapp/WEB-INF/spring-servlet.xml
Expand Up @@ -212,9 +212,10 @@
</bean>

<!--<oauth:web-expression-handler id="oauthWebExpressionHandler" />-->
<bean id="uaaIdentityZone" class="org.cloudfoundry.identity.uaa.zone.IdentityZone" factory-method="getUaa"/>
<bean id="oauthWebExpressionHandler"
class="org.cloudfoundry.identity.uaa.oauth.expression.ContextSensitiveOAuth2WebSecurityExpressionHandler">
<property name="identityZone" value="#{T(org.cloudfoundry.identity.uaa.zone.IdentityZone).getUaa()}"/>
<property name="identityZone" ref="uaaIdentityZone"/>
</bean>
<!--<mvc:resources location="/" mapping="/**" />-->

Expand Down
15 changes: 12 additions & 3 deletions uaa/src/main/webapp/WEB-INF/spring/multitenant-endpoints.xml
Expand Up @@ -32,14 +32,23 @@

<http name="identityZoneSecurity" pattern="/identity-zones/**" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
use-expressions="true" authentication-manager-ref="emptyAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/**" access="#oauth2.hasScope('zones.create')" method="POST"/>
<intercept-url pattern="/**" access="#oauth2.hasScope('zones.create')" method="PUT"/>
<intercept-url pattern="/**" access="#oauth2.hasScopeInAuthZone('zones.{zone.id}.admin')"/>
<intercept-url pattern="/**" access="#oauth2.hasScopeInAuthZone('zones.create')" method="POST"/>
<intercept-url pattern="/**" access="#oauth2.hasScopeInAuthZone('zones.create') or #oauth2.hasScopeInAuthZone('zones.{zone.id}.admin')" method="PUT"/>
<custom-filter ref="resourceAgnosticAuthenticationFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</http>

<bean id="denyAccessToUaaAdvice" class="org.cloudfoundry.identity.uaa.zone.DenyAccessToUaaAdvice"/>

<aop:config proxy-target-class="true">
<aop:aspect ref="denyAccessToUaaAdvice">
<aop:before method="checkIdentityZone" pointcut="execution(* *..IdentityZoneProvisioning+.update(..)) and args(identityZone)" />
<aop:before method="checkIdentityZone" pointcut="execution(* *..IdentityZoneEndpoints+.updateIdentityZone(..)) and args(identityZone,*)" />
<aop:before method="checkIdentityZoneId" pointcut="execution(* *..IdentityZoneEndpoints+.updateIdentityZone(..)) and args(*,identityZoneId)" />
</aop:aspect>
</aop:config>

<bean id="identityZoneEventPublisher" class="org.cloudfoundry.identity.uaa.zone.event.IdentityZoneEventPublisher"/>

<aop:config proxy-target-class="true">
Expand Down
Expand Up @@ -57,7 +57,7 @@
public class IdentityZoneEndpointsMockMvcTests {
private static XmlWebApplicationContext webApplicationContext;
private static MockMvc mockMvc;
private static String identityAdminToken = null;
private static String identityClientToken = null;
private static String adminToken = null;
private static TestClient testClient = null;
private RandomValueStringGenerator generator = new RandomValueStringGenerator();
Expand All @@ -79,7 +79,7 @@ public static void setUp() throws Exception {
eventListener = TestApplicationEventListener.forEventClass(IdentityZoneModifiedEvent.class);
webApplicationContext.addApplicationListener(eventListener);

identityAdminToken = testClient.getClientCredentialsOAuthAccessToken(
identityClientToken = testClient.getClientCredentialsOAuthAccessToken(
"identity",
"identitysecret",
"zones.create");
Expand Down Expand Up @@ -137,14 +137,30 @@ private ScimUser getScimUser() {
user.addEmail(email);
return user;
}


@Test
public void testGetZoneAsIdentityClient() throws Exception {
String id = generator.generate();
IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken);
IdentityZone retrieved = getIdentityZone(id, HttpStatus.OK, identityClientToken);
assertEquals(created.getId(), retrieved.getId());
assertEquals(created.getName(), retrieved.getName());
assertEquals(created.getSubdomain(), retrieved.getSubdomain());
assertEquals(created.getDescription(), retrieved.getDescription());
}

@Test
public void testGetZoneThatDoesntExist() throws Exception {
String id = generator.generate();
getIdentityZone(id, HttpStatus.NOT_FOUND, identityClientToken);
}

@Test
public void testCreateZone() throws Exception {
String id = generator.generate();
IdentityZone zone = createZone(id, HttpStatus.CREATED, identityAdminToken);
IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken);
assertEquals(id, zone.getId());
assertEquals(id, zone.getSubdomain());

checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);
}

Expand All @@ -167,7 +183,7 @@ public void testCreateZoneNoToken() throws Exception {

@Test
public void testCreateZoneWithoutID() throws Exception {
IdentityZone zone = createZone("", HttpStatus.CREATED, identityAdminToken);
IdentityZone zone = createZone("", HttpStatus.CREATED, identityClientToken);
assertTrue(StringUtils.hasText(zone.getId()));

checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);
Expand All @@ -182,12 +198,18 @@ public void testUpdateNonExistentReturns403() throws Exception {

assertEquals(0, eventListener.getEventCount());
}

@Test
public void testUpdateUaaIsForbidden() throws Exception {
updateZone(IdentityZone.getUaa(), HttpStatus.FORBIDDEN, identityClientToken);
checkAuditEventListener(0, AuditEventType.IdentityZoneModifiedEvent);
}

@Test
public void testUpdateNonExistentReturns404() throws Exception {
String id = generator.generate();
IdentityZone identityZone = getIdentityZone(id);
updateZone(identityZone, HttpStatus.NOT_FOUND, identityAdminToken);
updateZone(identityZone, HttpStatus.NOT_FOUND, identityClientToken);

assertEquals(0, eventListener.getEventCount());
}
Expand All @@ -196,38 +218,38 @@ public void testUpdateNonExistentReturns404() throws Exception {
public void testUpdateWithSameDataReturns200() throws Exception {
String id = generator.generate();

IdentityZone created = createZone(id, HttpStatus.CREATED, identityAdminToken);
IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken);
checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);

updateZone(created, HttpStatus.OK, identityAdminToken);
updateZone(created, HttpStatus.OK, identityClientToken);
checkAuditEventListener(2, AuditEventType.IdentityZoneModifiedEvent);
}

@Test
public void testUpdateWithDifferentDataReturns200() throws Exception {
String id = generator.generate();

IdentityZone created = createZone(id, HttpStatus.CREATED, identityAdminToken);
IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken);

checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);
created.setDescription("updated description");
IdentityZone updated = updateZone(created, HttpStatus.OK, identityAdminToken);
IdentityZone updated = updateZone(created, HttpStatus.OK, identityClientToken);
assertEquals("updated description", updated.getDescription());
checkAuditEventListener(2, AuditEventType.IdentityZoneModifiedEvent);
}

@Test
public void testUpdateZoneWithExistingSubdomain() throws Exception {
String id1 = generator.generate();
IdentityZone created1 = createZone(id1, HttpStatus.CREATED, identityAdminToken);
IdentityZone created1 = createZone(id1, HttpStatus.CREATED, identityClientToken);
checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);

String id2 = generator.generate();
IdentityZone created2 = createZone(id2, HttpStatus.CREATED, identityAdminToken);
IdentityZone created2 = createZone(id2, HttpStatus.CREATED, identityClientToken);
checkAuditEventListener(2, AuditEventType.IdentityZoneCreatedEvent);

created1.setSubdomain(created2.getSubdomain());
updateZone(created1, HttpStatus.CONFLICT, identityAdminToken);
updateZone(created1, HttpStatus.CONFLICT, identityClientToken);

checkAuditEventListener(2, AuditEventType.IdentityZoneCreatedEvent);
}
Expand All @@ -253,11 +275,11 @@ public void testUpdateZoneInsufficientScope() throws Exception {
@Test
public void testCreateDuplicateZoneReturns409() throws Exception {
String id = generator.generate();
createZone(id, HttpStatus.CREATED, identityAdminToken);
createZone(id, HttpStatus.CREATED, identityClientToken);

checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent);

createZone(id, HttpStatus.CONFLICT, identityAdminToken);
createZone(id, HttpStatus.CONFLICT, identityClientToken);

assertEquals(1, eventListener.getEventCount());
}
Expand All @@ -270,7 +292,7 @@ public void testCreateZoneAndIdentityProvider() throws Exception {
creationRequest.setIdentityZone(identityZone);

mockMvc.perform(post("/identity-zones")
.header("Authorization", "Bearer "+identityAdminToken)
.header("Authorization", "Bearer "+identityClientToken)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
.andExpect(status().isCreated())
Expand Down Expand Up @@ -302,7 +324,7 @@ public void testCreateInvalidZone() throws Exception {
IdentityZoneCreationRequest creationRequest = new IdentityZoneCreationRequest();
creationRequest.setIdentityZone(identityZone);
mockMvc.perform(post("/identity-zones")
.header("Authorization", "Bearer "+identityAdminToken)
.header("Authorization", "Bearer "+identityClientToken)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
.andExpect(status().isBadRequest());
Expand All @@ -322,7 +344,7 @@ public void testCreatesZonesWithDuplicateSubdomains() throws Exception {
IdentityZoneCreationRequest creationRequest = new IdentityZoneCreationRequest();
creationRequest.setIdentityZone(identityZone1);
mockMvc.perform(post("/identity-zones")
.header("Authorization", "Bearer "+identityAdminToken)
.header("Authorization", "Bearer "+identityClientToken)
.contentType(APPLICATION_JSON)
.accept(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
Expand All @@ -332,7 +354,7 @@ public void testCreatesZonesWithDuplicateSubdomains() throws Exception {

creationRequest.setIdentityZone(identityZone2);
mockMvc.perform(post("/identity-zones")
.header("Authorization", "Bearer "+identityAdminToken)
.header("Authorization", "Bearer "+identityClientToken)
.contentType(APPLICATION_JSON)
.accept(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
Expand All @@ -357,7 +379,7 @@ public void testCreateZoneAndClients() throws Exception {
creationRequest.setClientDetails(clientDetails);

mockMvc.perform(post("/identity-zones")
.header("Authorization", "Bearer "+identityAdminToken)
.header("Authorization", "Bearer "+identityClientToken)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
.andExpect(status().isCreated());
Expand Down Expand Up @@ -490,6 +512,18 @@ public void testModifyandDeleteUserInOtherZoneIsUnauthorized() throws Exception
.andReturn();
}

private IdentityZone getIdentityZone(String id, HttpStatus expect, String token) throws Exception {
MvcResult result = mockMvc.perform(get("/identity-zones/"+id)
.header("Authorization", "Bearer " + token))
.andExpect(status().is(expect.value()))
.andReturn();

if (expect.is2xxSuccessful()) {
return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityZone.class);
}
return null;
}

private IdentityZone createZone(String id, HttpStatus expect, String token) throws Exception {
IdentityZone identityZone = getIdentityZone(id);
IdentityZoneCreationRequest creationRequest = new IdentityZoneCreationRequest();
Expand Down Expand Up @@ -559,6 +593,8 @@ private String getBasicAuthHeaderValue(String clientId, String clientSecret) {
private void checkAuditEventListener(int eventCount, AuditEventType eventType) {
IdentityZoneModifiedEvent event = eventListener.getLatestEvent();
assertEquals(eventCount, eventListener.getEventCount());
assertEquals(eventType, event.getAuditEvent().getType());
if (eventCount > 0) {
assertEquals(eventType, event.getAuditEvent().getType());
}
}
}

0 comments on commit 5ba3b73

Please sign in to comment.