Skip to content

Commit

Permalink
Remove the scope zones.admin
Browse files Browse the repository at this point in the history
Since all administration of zones is done through the UAA zone, the uaa.admin serves as super admin
Furthermore, zones.create is the only action that should be allowed as client_credentials
A zone update, and any management in a zone, should require zones.{zone_id}.admin
https://www.pivotaltracker.com/story/show/86812618
[#86812618]
  • Loading branch information
fhanik committed Jan 26, 2015
1 parent 0fc9fe8 commit 1cadd15
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public IdentityZoneSwitchingFilter(IdentityZoneProvisioning dao) {

protected boolean isAuthorizedToSwitchToIdentityZone(String identityZoneId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
boolean hasScope = OAuth2ExpressionUtils.hasAnyScope(authentication,new String[] {"zones."+identityZoneId+".admin","zones.admin"});
boolean hasScope = OAuth2ExpressionUtils.hasAnyScope(authentication,new String[] {"zones."+identityZoneId+".admin"});
boolean isUaa = IdentityZoneHolder.isUaa();
boolean isTokenAuth = (authentication instanceof OAuth2Authentication);
return isTokenAuth && isUaa && hasScope;
Expand Down
3 changes: 2 additions & 1 deletion uaa/src/main/webapp/WEB-INF/spring/multitenant-endpoints.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

<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')" />
<intercept-url pattern="/**" access="#oauth2.hasScope('zones.create')" method="POST"/>
<intercept-url pattern="/**" access="#oauth2.hasScopeInAuthZone('zones.{zone.id}.admin')"/>
<custom-filter ref="resourceAgnosticAuthenticationFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,20 @@
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.apache.commons.codec.binary.Base64;
import org.cloudfoundry.identity.uaa.scim.ScimGroup;
import org.cloudfoundry.identity.uaa.scim.ScimGroupMember;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.test.TestClient;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneCreationRequest;
import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import scala.actors.threadpool.Arrays;

public class MockMvcUtils {

Expand Down Expand Up @@ -69,4 +75,33 @@ public ScimGroup createGroup(MockMvc mockMvc, String accessToken, ScimGroup grou
.andReturn().getResponse().getContentAsByteArray(),
ScimGroup.class);
}

public String getZoneAdminToken(MockMvc mockMvc, String adminToken, String zoneId) throws Exception {
ScimUser user = new ScimUser();
user.setUserName(new RandomValueStringGenerator().generate());
user.setPrimaryEmail(user.getUserName()+"@test.org");
user.setPassword("secret");
user = MockMvcUtils.utils().createUser(mockMvc, adminToken, user);
ScimGroup group = new ScimGroup("zones."+zoneId+".admin");
group.setMembers(Arrays.asList(new ScimGroupMember[]{new ScimGroupMember(user.getId())}));
MockMvcUtils.utils().createGroup(mockMvc, adminToken, group);
return getUserOAuthAccessToken(mockMvc, "identity", "identitysecret", user.getUserName(), "secret", group.getDisplayName());
}

public String getUserOAuthAccessToken(MockMvc mockMvc, String clientId, String clientSecret, String username, String password, String scope)
throws Exception {
String basicDigestHeaderValue = "Basic "
+ new String(Base64.encodeBase64((clientId + ":" + clientSecret).getBytes()));
MockHttpServletRequestBuilder oauthTokenPost = post("/oauth/token")
.header("Authorization", basicDigestHeaderValue)
.param("grant_type", "password")
.param("client_id", clientId)
.param("username", username)
.param("password", password)
.param("scope", scope);
MvcResult result = mockMvc.perform(oauthTokenPost).andExpect(status().isOk()).andReturn();
TestClient.OAuthToken oauthToken = new ObjectMapper().readValue(result.getResponse().getContentAsByteArray(), TestClient.OAuthToken.class);
return oauthToken.accessToken;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.googlecode.flyway.core.Flyway;
import org.apache.commons.codec.binary.Base64;
import org.cloudfoundry.identity.uaa.authentication.Origin;
import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
import org.cloudfoundry.identity.uaa.scim.ScimGroup;
import org.cloudfoundry.identity.uaa.scim.ScimGroupMember;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.test.TestClient;
import org.cloudfoundry.identity.uaa.test.YamlServletProfileInitializerContextInitializer;
Expand Down Expand Up @@ -34,6 +37,7 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
import scala.actors.threadpool.Arrays;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -47,6 +51,7 @@
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.result.MockMvcResultHandlers.print;
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.status;
Expand Down Expand Up @@ -78,7 +83,7 @@ public static void setUp() throws Exception {
adminToken = testClient.getClientCredentialsOAuthAccessToken(
"admin",
"adminsecret",
"uaa.admin");
"");

}

Expand Down Expand Up @@ -178,17 +183,26 @@ public void testCreateDuplicateZoneReturns409() throws Exception {
createZone(id, HttpStatus.CONFLICT, identityAdminToken);
}

@Test
public void testUpdateNonExistentReturns403() throws Exception {
String id = generator.generate();
//zone doesn't exist and we don't have the token scop
updateZone(id, HttpStatus.FORBIDDEN, identityAdminToken);
}

@Test
public void testUpdateNonExistentReturns404() throws Exception {
String id = generator.generate();
updateZone(id, HttpStatus.NOT_FOUND, identityAdminToken);
String zoneAdminToken = MockMvcUtils.utils().getZoneAdminToken(mockMvc, adminToken, id);
updateZone(id, HttpStatus.NOT_FOUND, zoneAdminToken);
}

@Test
public void testUpdateExistentReturns200() throws Exception {
String id = generator.generate();
createZone(id, HttpStatus.CREATED, identityAdminToken);
updateZone(id, HttpStatus.OK, identityAdminToken);
String zoneAdminToken = MockMvcUtils.utils().getZoneAdminToken(mockMvc, adminToken, id);
updateZone(id, HttpStatus.OK, zoneAdminToken);
}


Expand All @@ -211,6 +225,7 @@ public MvcResult updateZone(String id, HttpStatus expect, String token) throws E
creationRequest.setIdentityZone(identityZone);
return mockMvc.perform(put("/identity-zones/" + id)
.header("Authorization", "Bearer " + token)
.header("X-Identity-Zone-Id", id)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(creationRequest)))
.andExpect(status().is(expect.value()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import static org.springframework.http.MediaType.APPLICATION_JSON;
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.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.RandomStringUtils;
Expand Down Expand Up @@ -49,6 +48,7 @@ public class IdentityZoneSwitchingFilterMockMvcTest {
private static MockMvc mockMvc;
private static TestClient testClient;
private static String identityToken;
private static String adminToken;

@BeforeClass
public static void setUp() throws Exception {
Expand All @@ -66,7 +66,12 @@ public static void setUp() throws Exception {
identityToken = testClient.getClientCredentialsOAuthAccessToken(
"identity",
"identitysecret",
"zones.create,zones.admin,clients.write");
"zones.create,clients.write");

adminToken = testClient.getClientCredentialsOAuthAccessToken(
"admin",
"adminsecret",
"");
}

@AfterClass
Expand All @@ -78,15 +83,15 @@ public static void tearDown() throws Exception {
public void testSwitchingZones() throws Exception {

final String zoneId = createZone(identityToken);

String zoneAdminToken = MockMvcUtils.utils().getZoneAdminToken(mockMvc,adminToken, zoneId);
// Using Identity Client, authenticate in originating Zone
// - Create Client using X-Identity-Zone-Id header in new Zone
final String clientId = UUID.randomUUID().toString();
BaseClientDetails client = new BaseClientDetails(clientId, null, null, "client_credentials", null);
client.setClientSecret("secret");
mockMvc.perform(post("/oauth/clients")
.header(IdentityZoneSwitchingFilter.HEADER, zoneId)
.header("Authorization", "Bearer " + identityToken)
.header("Authorization", "Bearer " + zoneAdminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(client)))
Expand All @@ -102,7 +107,7 @@ public void testSwitchingZones() throws Exception {

@Test
public void testSwitchingToNonExistentZone() throws Exception {
createClientInOtherZone(identityToken, "i-do-not-exist", status().isNotFound());
createClientInOtherZone(identityToken, "i-do-not-exist", status().isForbidden());
}

@Test
Expand Down Expand Up @@ -144,12 +149,11 @@ private void createClientInOtherZone(String accessToken, String zoneId, ResultMa
BaseClientDetails client = new BaseClientDetails(clientId, null, null, "client_credentials", null);
client.setClientSecret("secret");
mockMvc.perform(post("/oauth/clients")
.header(IdentityZoneSwitchingFilter.HEADER, zoneId)
.header("Authorization", "Bearer " + accessToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(client)))
.andDo(print())
.header(IdentityZoneSwitchingFilter.HEADER, zoneId)
.header("Authorization", "Bearer " + accessToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(client)))
.andExpect(statusMatcher);
}
}

0 comments on commit 1cadd15

Please sign in to comment.