From 1543963c9ff39cd036da7012b71ce648cdcb5886 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Thu, 30 Jan 2014 20:31:44 -0500 Subject: [PATCH] composite tests --- .../keycloak/models/RoleContainerModel.java | 3 +- .../java/org/keycloak/models/RoleModel.java | 2 +- .../models/jpa/ApplicationAdapter.java | 5 +- .../org/keycloak/models/jpa/RealmAdapter.java | 5 +- .../org/keycloak/models/jpa/RoleAdapter.java | 11 ++- .../models/jpa/entities/RoleEntity.java | 8 +- .../services/managers/TokenManager.java | 63 ++++++++++--- .../admin/RoleContainerResource.java | 2 +- .../java/org/keycloak/test/AdapterTest.java | 2 +- .../composites/CompositeRoleTest.java | 89 +++++++++++++++++++ 10 files changed, 163 insertions(+), 27 deletions(-) diff --git a/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java b/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java index a34d4c89b6..bd8bfab1e7 100755 --- a/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java +++ b/model/api/src/main/java/org/keycloak/models/RoleContainerModel.java @@ -1,6 +1,7 @@ package org.keycloak.models; import java.util.List; +import java.util.Set; /** * @author Bill Burke @@ -13,7 +14,7 @@ public interface RoleContainerModel { boolean removeRoleById(String id); - List getRoles(); + Set getRoles(); RoleModel getRoleById(String id); } diff --git a/model/api/src/main/java/org/keycloak/models/RoleModel.java b/model/api/src/main/java/org/keycloak/models/RoleModel.java index e4cd7f504d..1cacaff3d6 100755 --- a/model/api/src/main/java/org/keycloak/models/RoleModel.java +++ b/model/api/src/main/java/org/keycloak/models/RoleModel.java @@ -29,6 +29,6 @@ public interface RoleModel { RoleContainerModel getContainer(); - boolean hasRole(RoleModel role); + } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java index 29dfdbdda4..8f650c6be4 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java @@ -143,8 +143,8 @@ public class ApplicationAdapter implements ApplicationModel { } @Override - public List getRoles() { - ArrayList list = new ArrayList(); + public Set getRoles() { + Set list = new HashSet(); Collection roles = application.getRoles(); if (roles == null) return list; for (RoleEntity entity : roles) { @@ -264,6 +264,7 @@ public class ApplicationAdapter implements ApplicationModel { public boolean equals(Object o) { if (o == null) return false; + if (o == this) return true; if (!(o instanceof ApplicationAdapter)) return false; ApplicationAdapter app = (ApplicationAdapter)o; return app.getId().equals(getId()); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 82a95b579f..b14fa97b33 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -880,8 +880,8 @@ public class RealmAdapter implements RealmModel { } @Override - public List getRoles() { - ArrayList list = new ArrayList(); + public Set getRoles() { + Set list = new HashSet(); Collection roles = realm.getRoles(); if (roles == null) return list; for (RoleEntity entity : roles) { @@ -1000,7 +1000,6 @@ public class RealmAdapter implements RealmModel { entity.setUser(((UserAdapter) agent).getUser()); entity.setRole(((RoleAdapter)role).getRole()); em.persist(entity); - em.flush(); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java index 529fc7daa6..69aa3ae245 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java @@ -3,7 +3,6 @@ package org.keycloak.models.jpa; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.UserModel; import org.keycloak.models.jpa.entities.ApplicationRoleEntity; import org.keycloak.models.jpa.entities.RealmRoleEntity; import org.keycloak.models.jpa.entities.RoleEntity; @@ -23,6 +22,7 @@ public class RoleAdapter implements RoleModel { protected RealmModel realm; public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) { + this.em = em; this.realm = realm; this.role = role; } @@ -77,6 +77,7 @@ public class RoleAdapter implements RoleModel { if (composite.equals(entity)) return; } getRole().getCompositeRoles().add(entity); + em.flush(); } @Override @@ -98,25 +99,27 @@ public class RoleAdapter implements RoleModel { return set; } - public static boolean searchCompositeFor(RoleModel role, RoleModel composite, Set visited) { + public static boolean searchFor(RoleModel role, RoleModel composite, Set visited) { if (visited.contains(composite)) return false; visited.add(composite); Set composites = composite.getComposites(); if (composites.contains(role)) return true; for (RoleModel contained : composites) { if (!contained.isComposite()) continue; - if (searchCompositeFor(role, contained, visited)) return true; + if (searchFor(role, contained, visited)) return true; } return false; } + + @Override public boolean hasRole(RoleModel role) { if (this.equals(role)) return true; if (!isComposite()) return false; Set visited = new HashSet(); - return searchCompositeFor(role, this, visited); + return searchFor(role, this, visited); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java index f65a59f5f8..6d1460133f 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java @@ -8,6 +8,7 @@ import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import java.util.ArrayList; import java.util.Collection; @@ -17,16 +18,17 @@ import java.util.Collection; * @version $Revision: 1 $ */ @Entity +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class RoleEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.AUTO) private String id; private String name; private String description; private boolean composite; - @OneToMany(fetch = FetchType.LAZY, cascade = {}, orphanRemoval = false) - @JoinTable(name = "COMPOSITE_ROLE") + @ManyToMany(fetch = FetchType.LAZY, cascade = {}) + //@JoinTable(name = "COMPOSITE_ROLE") private Collection compositeRoles = new ArrayList(); diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java index 8cdb6905de..e447ab1dad 100755 --- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java +++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java @@ -62,6 +62,21 @@ public class TokenManager { return scope == null || scope.isEmpty(); } + public static void addScopes(RoleModel role, RoleModel scope, Set visited, Set requested) { + if (visited.contains(scope)) return; + visited.add(scope); + if (role.hasRole(scope)) { + requested.add(scope); + return; + } + if (!scope.isComposite()) return; + + for (RoleModel contained : scope.getComposites()) { + addScopes(role, contained, visited, requested); + } + } + + public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) { AccessCodeEntry code = new AccessCodeEntry(); @@ -73,21 +88,17 @@ public class TokenManager { Set roleMappings = realm.getRoleMappings(user); Set scopeMappings = realm.getScopeMappings(client); + ApplicationModel clientApp = realm.getApplicationByName(client.getLoginName()); + Set clientAppRoles = clientApp == null ? null : clientApp.getRoles(); + if (clientAppRoles != null) scopeMappings.addAll(clientAppRoles); + Set requestedRoles = new HashSet(); for (RoleModel role : roleMappings) { + if (clientApp != null && role.getContainer().equals(clientApp)) requestedRoles.add(role); for (RoleModel desiredRole : scopeMappings) { - if (desiredRole.equals(role)) { - requestedRoles.add(role); - } else if (desiredRole.hasRole(role)) { - requestedRoles.add(role); - } else if (role.hasRole(desiredRole)) { - requestedRoles.add(desiredRole); - } else if (role.getContainer() instanceof ApplicationModel) { - if (((ApplicationModel)role.getContainer()).getApplicationUser().getLoginName().equals(client.getLoginName())) { - requestedRoles.add(role); - } - } + Set visited = new HashSet(); + addScopes(role, desiredRole, visited, requestedRoles); } } @@ -98,6 +109,36 @@ public class TokenManager { ApplicationModel app = (ApplicationModel)role.getContainer(); if (desiresScope(scopeMap, app.getName(), role.getName())) { resourceRolesRequested.add(app.getName(), role); + + } + } + } + + + + + Set realmRoleMappings = realm.getRealmRoleMappings(user); + + for (RoleModel role : realmRoleMappings) { + if (!desiresScope(scopeMap, "realm", role.getName())) continue; + for (RoleModel desiredRole : scopeMappings) { + if (desiredRole.hasRole(role)) { + realmRolesRequested.add(role); + } else if (role.hasRole(desiredRole)) { + realmRolesRequested.add(desiredRole); + } + } + } + + for (ApplicationModel application : realm.getApplications()) { + if (!desiresScopeGroup(scopeMap, application.getName())) continue; + Set appRoleMappings = application.getApplicationRoleMappings(user); + for (RoleModel role : appRoleMappings) { + if (!desiresScope(scopeMap, application.getName(), role.getName())) continue; + for (RoleModel desiredRole : scopeMappings) { + if (!application.getApplicationUser().getLoginName().equals(client.getLoginName()) + && !desiredRole.hasRole(role)) continue; + resourceRolesRequested.add(application.getName(), role); } } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 3d4317889c..1971b31c4a 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -34,7 +34,7 @@ public class RoleContainerResource { @NoCache @Produces("application/json") public List getRoles() { - List roleModels = roleContainer.getRoles(); + Set roleModels = roleContainer.getRoles(); List roles = new ArrayList(); for (RoleModel roleModel : roleModels) { if (!roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) { diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java index 3c49cd4706..9f49a98dc7 100755 --- a/services/src/test/java/org/keycloak/test/AdapterTest.java +++ b/services/src/test/java/org/keycloak/test/AdapterTest.java @@ -448,7 +448,7 @@ public class AdapterTest extends AbstractKeycloakTest { test1CreateRealm(); realmModel.addRole("admin"); realmModel.addRole("user"); - List roles = realmModel.getRoles(); + Set roles = realmModel.getRoles(); Assert.assertEquals(5, roles.size()); UserModel user = realmModel.addUser("bburke"); RoleModel role = realmModel.getRole("user"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java index d21d85f3d1..3cc1e4093c 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java @@ -97,6 +97,46 @@ public class CompositeRoleTest { realm.updateCredential(realmRole1Application.getApplicationUser(), UserCredentialModel.password("password")); + final ApplicationModel appRoleApplication = new ApplicationManager(manager).createApplication(realm, "APP_ROLE_APPLICATION"); + appRoleApplication.setEnabled(true); + appRoleApplication.setBaseUrl("http://localhost:8081/app"); + appRoleApplication.setManagementUrl("http://localhost:8081/app/logout"); + realm.updateCredential(appRoleApplication.getApplicationUser(), UserCredentialModel.password("password")); + final RoleModel appRole1 = appRoleApplication.addRole("APP_ROLE_1"); + final RoleModel appRole2 = appRoleApplication.addRole("APP_ROLE_2"); + + final RoleModel realmAppCompositeRole = realm.addRole("REALM_APP_COMPOSITE_ROLE"); + realmAppCompositeRole.setComposite(true); + realmAppCompositeRole.addCompositeRole(appRole1); + + final UserModel realmAppCompositeUser = realm.addUser("REALM_APP_COMPOSITE_USER"); + realmAppCompositeUser.setEnabled(true); + realm.updateCredential(realmAppCompositeUser, UserCredentialModel.password("password")); + realm.grantRole(realmAppCompositeUser, realmAppCompositeRole); + + final UserModel realmAppRoleUser = realm.addUser("REALM_APP_ROLE_USER"); + realmAppRoleUser.setEnabled(true); + realm.updateCredential(realmAppRoleUser, UserCredentialModel.password("password")); + realm.grantRole(realmAppRoleUser, appRole2); + + final ApplicationModel appCompositeApplication = new ApplicationManager(manager).createApplication(realm, "APP_COMPOSITE_APPLICATION"); + appCompositeApplication.setEnabled(true); + appCompositeApplication.setBaseUrl("http://localhost:8081/app"); + appCompositeApplication.setManagementUrl("http://localhost:8081/app/logout"); + realm.updateCredential(appCompositeApplication.getApplicationUser(), UserCredentialModel.password("password")); + final RoleModel appCompositeRole = appCompositeApplication.addRole("APP_COMPOSITE_ROLE"); + appCompositeRole.setComposite(true); + appCompositeApplication.addScope(appRole2); + appCompositeRole.addCompositeRole(realmRole1); + appCompositeRole.addCompositeRole(realmRole2); + appCompositeRole.addCompositeRole(realmRole3); + appCompositeRole.addCompositeRole(appRole1); + + final UserModel appCompositeUser = realm.addUser("APP_COMPOSITE_USER"); + appCompositeUser.setEnabled(true); + realm.updateCredential(appCompositeUser, UserCredentialModel.password("password")); + realm.grantRole(appCompositeUser, realmAppCompositeRole); + realm.grantRole(appCompositeUser, realmComposite1); deployServlet("app", "/app", ApplicationServlet.class); @@ -115,6 +155,55 @@ public class CompositeRoleTest { @WebResource protected LoginPage loginPage; + @Test + public void testAppCompositeUser() throws Exception { + oauth.realm("Test"); + oauth.realmPublicKey(realmPublicKey); + oauth.clientId("APP_COMPOSITE_APPLICATION"); + oauth.doLogin("APP_COMPOSITE_USER", "password"); + + String code = oauth.getCurrentQuery().get("code"); + AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password"); + + Assert.assertEquals(200, response.getStatusCode()); + + Assert.assertEquals("bearer", response.getTokenType()); + + SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken()); + + Assert.assertEquals("APP_COMPOSITE_USER", token.getSubject()); + + Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size()); + Assert.assertEquals(1, token.getRealmAccess().getRoles().size()); + Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1")); + Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1")); + } + + + @Test + public void testRealmAppCompositeUser() throws Exception { + oauth.realm("Test"); + oauth.realmPublicKey(realmPublicKey); + oauth.clientId("APP_ROLE_APPLICATION"); + oauth.doLogin("REALM_APP_COMPOSITE_USER", "password"); + + String code = oauth.getCurrentQuery().get("code"); + AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password"); + + Assert.assertEquals(200, response.getStatusCode()); + + Assert.assertEquals("bearer", response.getTokenType()); + + SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken()); + + Assert.assertEquals("REALM_APP_COMPOSITE_USER", token.getSubject()); + + Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size()); + Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1")); + } + + + @Test public void testRealmOnlyWithUserCompositeAppComposite() throws Exception { oauth.realm("Test");