composite tests
This commit is contained in:
parent
6a5994c3e2
commit
1543963c9f
10 changed files with 163 additions and 27 deletions
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -13,7 +14,7 @@ public interface RoleContainerModel {
|
|||
|
||||
boolean removeRoleById(String id);
|
||||
|
||||
List<RoleModel> getRoles();
|
||||
Set<RoleModel> getRoles();
|
||||
|
||||
RoleModel getRoleById(String id);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,6 @@ public interface RoleModel {
|
|||
|
||||
RoleContainerModel getContainer();
|
||||
|
||||
|
||||
boolean hasRole(RoleModel role);
|
||||
|
||||
}
|
||||
|
|
|
@ -143,8 +143,8 @@ public class ApplicationAdapter implements ApplicationModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getRoles() {
|
||||
ArrayList<RoleModel> list = new ArrayList<RoleModel>();
|
||||
public Set<RoleModel> getRoles() {
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
Collection<ApplicationRoleEntity> 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());
|
||||
|
|
|
@ -880,8 +880,8 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<RoleModel> getRoles() {
|
||||
ArrayList<RoleModel> list = new ArrayList<RoleModel>();
|
||||
public Set<RoleModel> getRoles() {
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
Collection<RealmRoleEntity> 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
|
||||
|
|
|
@ -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<RoleModel> visited) {
|
||||
public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
|
||||
if (visited.contains(composite)) return false;
|
||||
visited.add(composite);
|
||||
Set<RoleModel> 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<RoleModel> visited = new HashSet<RoleModel>();
|
||||
return searchCompositeFor(role, this, visited);
|
||||
return searchFor(role, this, visited);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
|
||||
|
||||
|
||||
|
|
|
@ -62,6 +62,21 @@ public class TokenManager {
|
|||
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) {
|
||||
AccessCodeEntry code = new AccessCodeEntry();
|
||||
|
@ -73,21 +88,17 @@ public class TokenManager {
|
|||
|
||||
Set<RoleModel> roleMappings = realm.getRoleMappings(user);
|
||||
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>();
|
||||
|
||||
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<RoleModel> visited = new HashSet<RoleModel>();
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public class RoleContainerResource {
|
|||
@NoCache
|
||||
@Produces("application/json")
|
||||
public List<RoleRepresentation> getRoles() {
|
||||
List<RoleModel> roleModels = roleContainer.getRoles();
|
||||
Set<RoleModel> roleModels = roleContainer.getRoles();
|
||||
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
|
||||
for (RoleModel roleModel : roleModels) {
|
||||
if (!roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {
|
||||
|
|
|
@ -448,7 +448,7 @@ public class AdapterTest extends AbstractKeycloakTest {
|
|||
test1CreateRealm();
|
||||
realmModel.addRole("admin");
|
||||
realmModel.addRole("user");
|
||||
List<RoleModel> roles = realmModel.getRoles();
|
||||
Set<RoleModel> roles = realmModel.getRoles();
|
||||
Assert.assertEquals(5, roles.size());
|
||||
UserModel user = realmModel.addUser("bburke");
|
||||
RoleModel role = realmModel.getRole("user");
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue