diff --git a/core/src/main/java/org/keycloak/representations/JsonWebToken.java b/core/src/main/java/org/keycloak/representations/JsonWebToken.java index e499271998..04071b6ab9 100755 --- a/core/src/main/java/org/keycloak/representations/JsonWebToken.java +++ b/core/src/main/java/org/keycloak/representations/JsonWebToken.java @@ -141,6 +141,7 @@ public class JsonWebToken implements Serializable { } public boolean hasAudience(String audience) { + if (this.audience == null) return false; for (String a : this.audience) { if (a.equals(audience)) { return true; diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java new file mode 100644 index 0000000000..a45ea7f79a --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/permissions/Helper.java @@ -0,0 +1,95 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.authorization.admin.permissions; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.models.ClientModel; +import org.keycloak.models.RoleModel; +import org.keycloak.representations.idm.authorization.DecisionStrategy; +import org.keycloak.representations.idm.authorization.Logic; +import org.keycloak.representations.idm.authorization.PolicyRepresentation; +import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class Helper { + public static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) { + ScopePermissionRepresentation representation = new ScopePermissionRepresentation(); + + representation.setName(name); + representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS); + representation.setLogic(Logic.POSITIVE); + representation.addResource(resource.getName()); + representation.addScope(scope.getName()); + representation.addPolicy(policy.getName()); + + return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer); + } + + public static Policy addEmptyScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope) { + ScopePermissionRepresentation representation = new ScopePermissionRepresentation(); + + representation.setName(name); + representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS); + representation.setLogic(Logic.POSITIVE); + representation.addResource(resource.getName()); + representation.addScope(scope.getName()); + + return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer); + } + + public static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) { + String roleName = getRolePolicyName(role); + return createRolePolicy(authz, resourceServer, role, roleName); + } + + public static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role, String policyName) { + PolicyRepresentation representation = new PolicyRepresentation(); + + representation.setName(policyName); + representation.setType("role"); + representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS); + representation.setLogic(Logic.POSITIVE); + String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]"; + Map config = new HashMap<>(); + config.put("roles", roleValues); + representation.setConfig(config); + + return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer); + } + + public static String getRolePolicyName(RoleModel role) { + String roleName = ""; + if (role.getContainer() instanceof ClientModel) { + ClientModel client = (ClientModel) role.getContainer(); + roleName = client.getClientId() + "." + role.getName(); + } else { + roleName = role.getName(); + } + roleName = "role.policy." + roleName; + return roleName; + } +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java new file mode 100644 index 0000000000..098c263df6 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/permissions/MgmtPermissions.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.authorization.admin.permissions; + +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; +import org.keycloak.authorization.common.KeycloakIdentity; +import org.keycloak.authorization.common.UserModelIdentity; +import org.keycloak.authorization.identity.Identity; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.AccessToken; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.admin.AdminAuth; +import org.keycloak.services.resources.admin.RealmAuth; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class MgmtPermissions { + public static final String MANAGE_SCOPE = "manage"; + + protected RealmModel realm; + protected KeycloakSession session; + protected AuthorizationProvider authz; + protected AdminAuth auth; + protected Identity identity; + + public MgmtPermissions(KeycloakSession session, RealmModel realm) { + this.session = session; + this.realm = realm; + AuthorizationProviderFactory factory = (AuthorizationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); + this.authz = factory.create(session, realm); + } + public MgmtPermissions(KeycloakSession session, RealmModel realm, AdminAuth auth) { + this(session, realm); + this.auth = auth; + + } + + public boolean isAdminSameRealm() { + return realm.getId().equals(auth.getRealm().getId()); + } + + public RealmAuth getRealmAuth() { + RealmManager realmManager = new RealmManager(session); + if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) { + return new RealmAuth(auth, realm.getMasterAdminClient()); + } else { + return new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()))); + } + } + + public Identity identity() { + if (identity != null) return identity; + if (auth.getClient().getClientId().equals(Constants.REALM_MANAGEMENT_CLIENT_ID)) { + this.identity = new UserModelIdentity(realm, auth.getUser()); + + } else { + this.identity = new KeycloakIdentity(auth.getToken(), session); + } + return this.identity; + } + + + public RoleMgmtPermissions roles() { + return new RoleMgmtPermissions(session, realm, authz, this); + } + + public ResourceServer findOrCreateResourceServer(ClientModel client) { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) { + server = authz.getStoreFactory().getResourceServerStore().create(client.getId()); + } + return server; + } + +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java new file mode 100644 index 0000000000..a745d56708 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/permissions/RoleMgmtPermissions.java @@ -0,0 +1,177 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.authorization.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.Decision; +import org.keycloak.authorization.common.DefaultEvaluationContext; +import org.keycloak.authorization.identity.Identity; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.permission.ResourcePermission; +import org.keycloak.authorization.permission.evaluator.PermissionEvaluator; +import org.keycloak.authorization.policy.evaluation.DecisionResult; +import org.keycloak.authorization.policy.evaluation.EvaluationContext; +import org.keycloak.authorization.util.Permissions; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; + +import java.util.List; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleMgmtPermissions { + private static final Logger logger = Logger.getLogger(RoleMgmtPermissions.class); + public static final String MAP_ROLE_SCOPE = "map-role"; + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public RoleMgmtPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + public boolean isPermissionsEnabled(RoleModel role) { + ClientModel client = getRoleClient(role); + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return false; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource == null) return false; + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), server.getId()); + + return policy != null; + } + + public void setPermissionsEnabled(RoleModel role, boolean enable) { + if (enable) { + ResourceServer server = getResourceServer(role); + if (authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()) != null) { + return; + } + createResource(role); + } else { + ClientModel client = getRoleClient(role); + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return; + Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()); + if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId()); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), server.getId()); + if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId()); + } + } + + public boolean canMapRole(RoleModel role) { + if (!root.isAdminSameRealm()) { + return true; + } + if (!isPermissionsEnabled(role)) return true; // no authz permissions set up so just allow it. + + ResourceServer resourceServer = getResourceServer(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRoleScopePermissionName(role), resourceServer.getId()); + if (policy.getAssociatedPolicies().isEmpty()) { + return true; // if no policies applied, just ignore + } + + + Identity identity = root.identity(); + + EvaluationContext context = new DefaultEvaluationContext(identity, session); + DecisionResult decisionCollector = new DecisionResult(); + Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), resourceServer.getId()); + Scope mapRoleScope = getMapRoleScope(resourceServer); + + List permissions = Permissions.permission(resourceServer, roleResource, mapRoleScope); + PermissionEvaluator from = authz.evaluators().from(permissions, context); + from.evaluate(decisionCollector); + if (!decisionCollector.completed()) { + logger.error("Failed to run map role policy check", decisionCollector.getError()); + return false; + } + return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT; + } + + + private ClientModel getRoleClient(RoleModel role) { + ClientModel client = null; + if (role.getContainer() instanceof ClientModel) { + client = (ClientModel)role.getContainer(); + } else { + client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + } + return client; + } + + public Policy getManageUsersPolicy(ResourceServer server) { + RoleModel role = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).getRole(AdminRoles.MANAGE_USERS); + return getRolePolicy(server, role); + } + + public Policy getRolePolicy(ResourceServer server, RoleModel role) { + String policyName = Helper.getRolePolicyName(role); + Policy policy = authz.getStoreFactory().getPolicyStore().findByName(policyName, server.getId()); + if (policy != null) return policy; + return Helper.createRolePolicy(authz, server, role, policyName); + } + + private Scope getMapRoleScope(ResourceServer server) { + Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_SCOPE, server.getId()); + if (scope == null) { + scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_SCOPE, server); + } + return scope; + } + + + private Resource createResource(RoleModel role) { + ResourceServer server = getResourceServer(role); + Resource resource = authz.getStoreFactory().getResourceStore().create(getRoleResourceName(role), server, server.getClientId()); + resource.setType("Role"); + Scope mapRoleScope = getMapRoleScope(server); + Helper.addEmptyScopePermission(authz, server, getMapRoleScopePermissionName(role), resource, mapRoleScope); + return resource; + } + + private String getMapRoleScopePermissionName(RoleModel role) { + return MAP_ROLE_SCOPE + ".permission." + role.getName(); + } + + private ResourceServer getResourceServer(RoleModel role) { + ClientModel client = getRoleClient(role); + return root.findOrCreateResourceServer(client); + } + + private static String getRoleResourceName(RoleModel role) { + return "role.resource." + role.getName(); + } + + +} diff --git a/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java b/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java new file mode 100644 index 0000000000..c198885d74 --- /dev/null +++ b/services/src/main/java/org/keycloak/authorization/admin/permissions/UsersPermissions.java @@ -0,0 +1,188 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.authorization.admin.permissions; + +import org.jboss.logging.Logger; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.Decision; +import org.keycloak.authorization.common.DefaultEvaluationContext; +import org.keycloak.authorization.identity.Identity; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.Resource; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.model.Scope; +import org.keycloak.authorization.permission.ResourcePermission; +import org.keycloak.authorization.permission.evaluator.PermissionEvaluator; +import org.keycloak.authorization.policy.evaluation.DecisionResult; +import org.keycloak.authorization.policy.evaluation.EvaluationContext; +import org.keycloak.authorization.util.Permissions; +import org.keycloak.models.ClientModel; +import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.admin.AdminAuth; +import org.keycloak.services.resources.admin.RealmAuth; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class UsersPermissions { + private static final Logger logger = Logger.getLogger(UsersPermissions.class); + protected final KeycloakSession session; + protected final RealmModel realm; + protected final AuthorizationProvider authz; + protected final MgmtPermissions root; + + public UsersPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) { + this.session = session; + this.realm = realm; + this.authz = authz; + this.root = root; + } + + private void initialize() { + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + ResourceServer server = root.findOrCreateResourceServer(client); + Scope manageScope = authz.getStoreFactory().getScopeStore().findByName(MgmtPermissions.MANAGE_SCOPE, server.getId()); + if (manageScope == null) { + manageScope = authz.getStoreFactory().getScopeStore().create(MgmtPermissions.MANAGE_SCOPE, server); + + } + Resource usersResource = authz.getStoreFactory().getResourceStore().findByName("Users", server.getId()); + if (usersResource == null) { + usersResource = authz.getStoreFactory().getResourceStore().create("Users", server, server.getClientId()); + } + Policy policy = authz.getStoreFactory().getPolicyStore().findByName("manage.permissions.users", server.getId()); + if (policy == null) { + Set scopeset = new HashSet<>(); + scopeset.add(manageScope); + usersResource.updateScopes(scopeset); + Policy manageUsersPolicy = root.roles().getManageUsersPolicy(server); + Helper.addScopePermission(authz, server, "manage.permission.users", usersResource, manageScope, manageUsersPolicy); + } + } + + public boolean isPermissionsEnabled() { + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return false; + + Resource resource = authz.getStoreFactory().getResourceStore().findByName("Users", server.getId()); + if (resource == null) return false; + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName("manage.permissions.users", server.getId()); + + return policy != null; + } + + public void setPermissionsEnabled(RoleModel role, boolean enable) { + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + if (enable) { + initialize(); + } else { + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return; + Resource usersResource = authz.getStoreFactory().getResourceStore().findByName("Users", server.getId()); + if (usersResource == null) { + authz.getStoreFactory().getResourceStore().delete(usersResource.getId()); + } + Policy policy = authz.getStoreFactory().getPolicyStore().findByName("manage.permissions.users", server.getId()); + if (policy == null) { + authz.getStoreFactory().getPolicyStore().delete(policy.getId()); + + } + } + } + + private Resource getUsersResource(ResourceServer server) { + Resource usersResource = authz.getStoreFactory().getResourceStore().findByName("Users", server.getId()); + if (usersResource == null) { + usersResource = authz.getStoreFactory().getResourceStore().create("Users", server, server.getClientId()); + } + return usersResource; + } + + private Scope getManageScope(ResourceServer server) { + Scope manageScope = authz.getStoreFactory().getScopeStore().findByName(MgmtPermissions.MANAGE_SCOPE, server.getId()); + if (manageScope == null) { + manageScope = authz.getStoreFactory().getScopeStore().create(MgmtPermissions.MANAGE_SCOPE, server); + + } + return manageScope; + } + + private ResourceServer getRealmManagementResourceServer() { + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + return root.findOrCreateResourceServer(client); + } + + private boolean canManageDefault() { + RealmAuth auth = root.getRealmAuth(); + auth.init(RealmAuth.Resource.USER); + return auth.hasManage(); + + } + + public boolean canManage(UserModel user) { + if (!root.isAdminSameRealm()) { + return canManageDefault(); + } + + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); + ResourceServer server = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId()); + if (server == null) return canManageDefault(); + + Resource resource = authz.getStoreFactory().getResourceStore().findByName("Users", server.getId()); + if (resource == null) return canManageDefault(); + + Policy policy = authz.getStoreFactory().getPolicyStore().findByName("manage.permissions.users", server.getId()); + if (policy == null) { + return canManageDefault(); + } + + Set associatedPolicies = policy.getAssociatedPolicies(); + // if no policies attached to permission then just do default behavior + if (associatedPolicies == null || associatedPolicies.isEmpty()) { + return canManageDefault(); + } + + Identity identity = root.identity(); + EvaluationContext context = new DefaultEvaluationContext(identity, session); + DecisionResult decisionCollector = new DecisionResult(); + ResourceServer resourceServer = getRealmManagementResourceServer(); + Resource roleResource = authz.getStoreFactory().getResourceStore().findByName("Users", resourceServer.getId()); + Scope manageScope = getManageScope(resourceServer); + + List permissions = Permissions.permission(resourceServer, roleResource, manageScope); + PermissionEvaluator from = authz.evaluators().from(permissions, context); + from.evaluate(decisionCollector); + if (!decisionCollector.completed()) { + logger.error("Failed to run Users manage check", decisionCollector.getError()); + return false; + } + return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT; + + } +} diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java index 6b5b0275ab..7898f7b715 100644 --- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java +++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java @@ -54,6 +54,74 @@ public class KeycloakIdentity implements Identity { this(Tokens.getAccessToken(keycloakSession), keycloakSession); } + public KeycloakIdentity(KeycloakSession keycloakSession, AccessToken accessToken) { + this(accessToken, keycloakSession, keycloakSession.getContext().getRealm()); + } + + public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, RealmModel realm) { + if (accessToken == null) { + throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN); + } + if (keycloakSession == null) { + throw new ErrorResponseException("no_keycloak_session", "No keycloak session", Status.FORBIDDEN); + } + if (realm == null) { + throw new ErrorResponseException("no_keycloak_session", "No realm set", Status.FORBIDDEN); + } + this.accessToken = accessToken; + this.keycloakSession = keycloakSession; + this.realm = realm; + + Map> attributes = new HashMap<>(); + + try { + ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken); + Iterator iterator = objectNode.fieldNames(); + + while (iterator.hasNext()) { + String fieldName = iterator.next(); + JsonNode fieldValue = objectNode.get(fieldName); + List values = new ArrayList<>(); + + if (fieldValue.isArray()) { + Iterator valueIterator = fieldValue.iterator(); + + while (valueIterator.hasNext()) { + values.add(valueIterator.next().asText()); + } + } else { + String value = fieldValue.asText(); + + if (StringUtil.isNullOrEmpty(value)) { + continue; + } + + values.add(value); + } + + if (!values.isEmpty()) { + attributes.put(fieldName, values); + } + } + + AccessToken.Access realmAccess = accessToken.getRealmAccess(); + + if (realmAccess != null) { + attributes.put("kc.realm.roles", realmAccess.getRoles()); + } + + Map resourceAccess = accessToken.getResourceAccess(); + + if (resourceAccess != null) { + resourceAccess.forEach((clientId, access) -> attributes.put("kc.client." + clientId + ".roles", access.getRoles())); + } + } catch (Exception e) { + throw new RuntimeException("Error while reading attributes from security token.", e); + } + + this.attributes = Attributes.from(attributes); + } + public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) { if (accessToken == null) { throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java index bcf35fb6dd..f9afdee37c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java @@ -21,6 +21,7 @@ import org.junit.Ignore; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; import org.keycloak.authorization.Decision; import org.keycloak.authorization.common.DefaultEvaluationContext; import org.keycloak.authorization.common.UserModelIdentity; @@ -79,9 +80,10 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest { public static void setupDefaults(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(TEST); - ClientModel client = realm.getClientByClientId("realm-management"); + ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID); - AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class); + AuthorizationProviderFactory factory = (AuthorizationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); + AuthorizationProvider authz = factory.create(session, realm); ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId()); Scope mapRoleScope = authz.getStoreFactory().getScopeStore().create("map-role", resourceServer); Scope manageScope = authz.getStoreFactory().getScopeStore().create("manage", resourceServer); @@ -103,6 +105,7 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest { String name = "map.role.permission." + client.getClientId() + "." + role.getName(); Policy permission = addScopePermission(authz, resourceServer, name, resource, mapRoleScope, policy); + addEmptyScopePermission(authz, resourceServer, "empty. " + name, resource, mapRoleScope); } Resource usersResource = authz.getStoreFactory().getResourceStore().create("Users", resourceServer, resourceServer.getClientId()); @@ -125,6 +128,18 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest { return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer); } + private static Policy addEmptyScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope) { + ScopePermissionRepresentation representation = new ScopePermissionRepresentation(); + + representation.setName(name); + representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS); + representation.setLogic(Logic.POSITIVE); + representation.addResource(resource.getName()); + representation.addScope(scope.getName()); + + return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer); + } + private static Resource createRoleResource(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) { String roleName = getRoleResourceName(role); Resource resource = authz.getStoreFactory().getResourceStore().create(roleName, resourceServer, resourceServer.getClientId()); @@ -179,7 +194,7 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest { } - //@Test + @Test public void testUI() throws Exception { testingClient.server().run(FineGrainAdminLocalTest::setupDefaults); testingClient.server().run(FineGrainAdminLocalTest::setupUsers); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java index 74fcb7bd0b..25382e8bc9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java @@ -20,6 +20,7 @@ import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.AuthorizationProviderFactory; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; @@ -66,7 +67,8 @@ public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest { RoleModel role1 = client.addRole("client-role1"); - AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class); + AuthorizationProviderFactory factory = (AuthorizationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class); + AuthorizationProvider authz = factory.create(session, realm); ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId()); Policy policy = createRolePolicy(authz, resourceServer, role1);