composite tests

This commit is contained in:
Bill Burke 2014-01-30 20:31:44 -05:00
parent 6a5994c3e2
commit 1543963c9f
10 changed files with 163 additions and 27 deletions

View file

@ -1,6 +1,7 @@
package org.keycloak.models; package org.keycloak.models;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -13,7 +14,7 @@ public interface RoleContainerModel {
boolean removeRoleById(String id); boolean removeRoleById(String id);
List<RoleModel> getRoles(); Set<RoleModel> getRoles();
RoleModel getRoleById(String id); RoleModel getRoleById(String id);
} }

View file

@ -29,6 +29,6 @@ public interface RoleModel {
RoleContainerModel getContainer(); RoleContainerModel getContainer();
boolean hasRole(RoleModel role); boolean hasRole(RoleModel role);
} }

View file

@ -143,8 +143,8 @@ public class ApplicationAdapter implements ApplicationModel {
} }
@Override @Override
public List<RoleModel> getRoles() { public Set<RoleModel> getRoles() {
ArrayList<RoleModel> list = new ArrayList<RoleModel>(); Set<RoleModel> list = new HashSet<RoleModel>();
Collection<ApplicationRoleEntity> roles = application.getRoles(); Collection<ApplicationRoleEntity> roles = application.getRoles();
if (roles == null) return list; if (roles == null) return list;
for (RoleEntity entity : roles) { for (RoleEntity entity : roles) {
@ -264,6 +264,7 @@ public class ApplicationAdapter implements ApplicationModel {
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == null) return false; if (o == null) return false;
if (o == this) return true;
if (!(o instanceof ApplicationAdapter)) return false; if (!(o instanceof ApplicationAdapter)) return false;
ApplicationAdapter app = (ApplicationAdapter)o; ApplicationAdapter app = (ApplicationAdapter)o;
return app.getId().equals(getId()); return app.getId().equals(getId());

View file

@ -880,8 +880,8 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public List<RoleModel> getRoles() { public Set<RoleModel> getRoles() {
ArrayList<RoleModel> list = new ArrayList<RoleModel>(); Set<RoleModel> list = new HashSet<RoleModel>();
Collection<RealmRoleEntity> roles = realm.getRoles(); Collection<RealmRoleEntity> roles = realm.getRoles();
if (roles == null) return list; if (roles == null) return list;
for (RoleEntity entity : roles) { for (RoleEntity entity : roles) {
@ -1000,7 +1000,6 @@ public class RealmAdapter implements RealmModel {
entity.setUser(((UserAdapter) agent).getUser()); entity.setUser(((UserAdapter) agent).getUser());
entity.setRole(((RoleAdapter)role).getRole()); entity.setRole(((RoleAdapter)role).getRole());
em.persist(entity); em.persist(entity);
em.flush();
} }
@Override @Override

View file

@ -3,7 +3,6 @@ package org.keycloak.models.jpa;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.ApplicationRoleEntity; import org.keycloak.models.jpa.entities.ApplicationRoleEntity;
import org.keycloak.models.jpa.entities.RealmRoleEntity; import org.keycloak.models.jpa.entities.RealmRoleEntity;
import org.keycloak.models.jpa.entities.RoleEntity; import org.keycloak.models.jpa.entities.RoleEntity;
@ -23,6 +22,7 @@ public class RoleAdapter implements RoleModel {
protected RealmModel realm; protected RealmModel realm;
public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) { public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) {
this.em = em;
this.realm = realm; this.realm = realm;
this.role = role; this.role = role;
} }
@ -77,6 +77,7 @@ public class RoleAdapter implements RoleModel {
if (composite.equals(entity)) return; if (composite.equals(entity)) return;
} }
getRole().getCompositeRoles().add(entity); getRole().getCompositeRoles().add(entity);
em.flush();
} }
@Override @Override
@ -98,25 +99,27 @@ public class RoleAdapter implements RoleModel {
return set; return set;
} }
public static boolean searchCompositeFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) { public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
if (visited.contains(composite)) return false; if (visited.contains(composite)) return false;
visited.add(composite); visited.add(composite);
Set<RoleModel> composites = composite.getComposites(); Set<RoleModel> composites = composite.getComposites();
if (composites.contains(role)) return true; if (composites.contains(role)) return true;
for (RoleModel contained : composites) { for (RoleModel contained : composites) {
if (!contained.isComposite()) continue; if (!contained.isComposite()) continue;
if (searchCompositeFor(role, contained, visited)) return true; if (searchFor(role, contained, visited)) return true;
} }
return false; return false;
} }
@Override @Override
public boolean hasRole(RoleModel role) { public boolean hasRole(RoleModel role) {
if (this.equals(role)) return true; if (this.equals(role)) return true;
if (!isComposite()) return false; if (!isComposite()) return false;
Set<RoleModel> visited = new HashSet<RoleModel>(); Set<RoleModel> visited = new HashSet<RoleModel>();
return searchCompositeFor(role, this, visited); return searchFor(role, this, visited);
} }
@Override @Override

View file

@ -8,6 +8,7 @@ import javax.persistence.Id;
import javax.persistence.Inheritance; import javax.persistence.Inheritance;
import javax.persistence.InheritanceType; import javax.persistence.InheritanceType;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -17,16 +18,17 @@ import java.util.Collection;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
@Entity @Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class RoleEntity { public abstract class RoleEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.AUTO)
private String id; private String id;
private String name; private String name;
private String description; private String description;
private boolean composite; private boolean composite;
@OneToMany(fetch = FetchType.LAZY, cascade = {}, orphanRemoval = false) @ManyToMany(fetch = FetchType.LAZY, cascade = {})
@JoinTable(name = "COMPOSITE_ROLE") //@JoinTable(name = "COMPOSITE_ROLE")
private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>(); private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();

View file

@ -62,6 +62,21 @@ public class TokenManager {
return scope == null || scope.isEmpty(); return scope == null || scope.isEmpty();
} }
public static void addScopes(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> 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) { public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
AccessCodeEntry code = new AccessCodeEntry(); AccessCodeEntry code = new AccessCodeEntry();
@ -73,21 +88,17 @@ public class TokenManager {
Set<RoleModel> roleMappings = realm.getRoleMappings(user); Set<RoleModel> roleMappings = realm.getRoleMappings(user);
Set<RoleModel> scopeMappings = realm.getScopeMappings(client); Set<RoleModel> scopeMappings = realm.getScopeMappings(client);
ApplicationModel clientApp = realm.getApplicationByName(client.getLoginName());
Set<RoleModel> clientAppRoles = clientApp == null ? null : clientApp.getRoles();
if (clientAppRoles != null) scopeMappings.addAll(clientAppRoles);
Set<RoleModel> requestedRoles = new HashSet<RoleModel>(); Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
for (RoleModel role : roleMappings) { for (RoleModel role : roleMappings) {
if (clientApp != null && role.getContainer().equals(clientApp)) requestedRoles.add(role);
for (RoleModel desiredRole : scopeMappings) { for (RoleModel desiredRole : scopeMappings) {
if (desiredRole.equals(role)) { Set<RoleModel> visited = new HashSet<RoleModel>();
requestedRoles.add(role); addScopes(role, desiredRole, visited, requestedRoles);
} 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);
}
}
} }
} }
@ -98,6 +109,36 @@ public class TokenManager {
ApplicationModel app = (ApplicationModel)role.getContainer(); ApplicationModel app = (ApplicationModel)role.getContainer();
if (desiresScope(scopeMap, app.getName(), role.getName())) { if (desiresScope(scopeMap, app.getName(), role.getName())) {
resourceRolesRequested.add(app.getName(), role); resourceRolesRequested.add(app.getName(), role);
}
}
}
Set<RoleModel> 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<RoleModel> 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);
} }
} }
} }

View file

@ -34,7 +34,7 @@ public class RoleContainerResource {
@NoCache @NoCache
@Produces("application/json") @Produces("application/json")
public List<RoleRepresentation> getRoles() { public List<RoleRepresentation> getRoles() {
List<RoleModel> roleModels = roleContainer.getRoles(); Set<RoleModel> roleModels = roleContainer.getRoles();
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>(); List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
for (RoleModel roleModel : roleModels) { for (RoleModel roleModel : roleModels) {
if (!roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) { if (!roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {

View file

@ -448,7 +448,7 @@ public class AdapterTest extends AbstractKeycloakTest {
test1CreateRealm(); test1CreateRealm();
realmModel.addRole("admin"); realmModel.addRole("admin");
realmModel.addRole("user"); realmModel.addRole("user");
List<RoleModel> roles = realmModel.getRoles(); Set<RoleModel> roles = realmModel.getRoles();
Assert.assertEquals(5, roles.size()); Assert.assertEquals(5, roles.size());
UserModel user = realmModel.addUser("bburke"); UserModel user = realmModel.addUser("bburke");
RoleModel role = realmModel.getRole("user"); RoleModel role = realmModel.getRole("user");

View file

@ -97,6 +97,46 @@ public class CompositeRoleTest {
realm.updateCredential(realmRole1Application.getApplicationUser(), UserCredentialModel.password("password")); 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); deployServlet("app", "/app", ApplicationServlet.class);
@ -115,6 +155,55 @@ public class CompositeRoleTest {
@WebResource @WebResource
protected LoginPage loginPage; 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 @Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception { public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
oauth.realm("Test"); oauth.realm("Test");