From 711bf244ed73aed6afa5d545ea19ff7a0115069a Mon Sep 17 00:00:00 2001 From: pedroigor Date: Thu, 15 Mar 2018 14:02:15 -0300 Subject: [PATCH] [KEYCLOAK-6628] - Expose methods to query roles, groups, and attributes of users in Evaluation API --- .../policy/evaluation/DefaultEvaluation.java | 146 +++++ .../policy/evaluation/Evaluation.java | 7 + .../policy/evaluation/Realm.java | 114 ++++ .../testsuite/authz/PolicyEvaluationTest.java | 578 ++++++++++++++++++ 4 files changed, 845 insertions(+) create mode 100644 server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Realm.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java index 74a2e076ce..cbf23cb11b 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java @@ -18,11 +18,26 @@ package org.keycloak.authorization.policy.evaluation; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.Decision; import org.keycloak.authorization.Decision.Effect; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.permission.ResourcePermission; +import org.keycloak.models.ClientModel; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.models.utils.RoleUtils; import org.keycloak.representations.idm.authorization.Logic; /** @@ -36,6 +51,7 @@ public class DefaultEvaluation implements Evaluation { private final Policy policy; private final Policy parentPolicy; private final AuthorizationProvider authorizationProvider; + private final Realm realm; private Effect effect; public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision, AuthorizationProvider authorizationProvider) { @@ -45,6 +61,7 @@ public class DefaultEvaluation implements Evaluation { this.policy = policy; this.decision = decision; this.authorizationProvider = authorizationProvider; + this.realm = createRealm(); } @Override @@ -84,6 +101,11 @@ public class DefaultEvaluation implements Evaluation { return this.policy; } + @Override + public Realm getRealm() { + return realm; + } + @Override public AuthorizationProvider getAuthorizationProvider() { return authorizationProvider; @@ -102,4 +124,128 @@ public class DefaultEvaluation implements Evaluation { deny(); } } + + private Realm createRealm() { + return new Realm() { + + @Override + public boolean isUserInGroup(String id, String groupId, boolean checkParent) { + KeycloakSession session = authorizationProvider.getKeycloakSession(); + UserModel user = getUser(id, session); + + if (Objects.isNull(user)) { + return false; + } + + RealmModel realm = session.getContext().getRealm(); + GroupModel group = KeycloakModelUtils.findGroupByPath(realm, groupId); + + if (Objects.isNull(group)) { + return false; + } + + if (checkParent) { + return RoleUtils.isMember(user.getGroups(), group); + } + + return user.isMemberOf(group); + } + + private UserModel getUser(String id, KeycloakSession session) { + RealmModel realm = session.getContext().getRealm(); + UserModel user = session.users().getUserById(id, realm); + + if (Objects.isNull(user)) { + user = session.users().getUserByUsername(id, realm); + + if (Objects.isNull(user)) { + user = session.users().getUserByEmail(id, realm); + } + } + + return user; + } + + @Override + public boolean isUserInRealmRole(String id, String roleName) { + KeycloakSession session = authorizationProvider.getKeycloakSession(); + UserModel user = getUser(id, session); + + if (Objects.isNull(user)) { + return false; + } + + Set roleMappings = user.getRoleMappings().stream() + .filter(role -> !role.isClientRole()) + .collect(Collectors.toSet()); + + return RoleUtils.hasRole(roleMappings, session.getContext().getRealm().getRole(roleName)); + } + + @Override + public boolean isUserInClientRole(String id, String clientId, String roleName) { + KeycloakSession session = authorizationProvider.getKeycloakSession(); + RealmModel realm = session.getContext().getRealm(); + UserModel user = getUser(id, session); + + if (Objects.isNull(user)) { + return false; + } + + Set roleMappings = user.getRoleMappings().stream() + .filter(role -> role.isClientRole() && ClientModel.class.cast(role.getContainer()).getClientId().equals(clientId)) + .collect(Collectors.toSet()); + + if (roleMappings.isEmpty()) { + return false; + } + + RoleModel role = realm.getClientById(ClientModel.class.cast(roleMappings.iterator().next().getContainer()).getId()).getRole(roleName); + + if (Objects.isNull(role)) { + return false; + } + + return RoleUtils.hasRole(roleMappings, role); + } + + @Override + public boolean isGroupInRole(String id, String role) { + KeycloakSession session = authorizationProvider.getKeycloakSession(); + RealmModel realm = session.getContext().getRealm(); + GroupModel group = KeycloakModelUtils.findGroupByPath(realm, id); + + return RoleUtils.hasRoleFromGroup(group, realm.getRole(role), false); + } + + @Override + public List getUserRealmRoles(String id) { + return getUser(id, authorizationProvider.getKeycloakSession()).getRoleMappings().stream() + .filter(role -> !role.isClientRole()) + .map(RoleModel::getName) + .collect(Collectors.toList()); + } + + @Override + public List getUserClientRoles(String id, String clientId) { + return getUser(id, authorizationProvider.getKeycloakSession()).getRoleMappings().stream() + .filter(role -> role.isClientRole()) + .map(RoleModel::getName) + .collect(Collectors.toList()); + } + + @Override + public List getUserGroups(String id) { + return getUser(id, authorizationProvider.getKeycloakSession()).getGroups().stream() + .map(ModelToRepresentation::buildGroupPath) + .collect(Collectors.toList()); + } + + @Override + public Map> getUserAttributes(String id) { + Map> attributes = getUser(id, authorizationProvider.getKeycloakSession()).getAttributes(); + return attributes; + } + }; + } } diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java index 4ac0264b34..7fd656655b 100644 --- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java +++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java @@ -51,6 +51,13 @@ public interface Evaluation { */ Policy getPolicy(); + /** + * Returns a {@link Realm} that can be used by policies to query information. + * + * @return a {@link Realm} instance + */ + Realm getRealm(); + AuthorizationProvider getAuthorizationProvider(); /** diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Realm.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Realm.java new file mode 100644 index 0000000000..b5d5dbb04a --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Realm.java @@ -0,0 +1,114 @@ +/* + * Copyright 2018 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.policy.evaluation; + +import java.util.List; +import java.util.Map; + +/** + * This interface provides methods to query information from a realm. + * + * @author Pedro Igor + */ +public interface Realm { + + /** + *

Checks whether or not a user with the given id is a member of the given group. + * + *

This method will also consider memberships where the user is a member of any child group of the given group. + * For instance, if user is member of /Group A/Group B and this method is checking if user is a member of /Group A + * the result will be true given that the user is a member of a child group of /Group A. + * + * @param id the user id. It can be the id, username or email + * @param group the group path. For instance, /Group A/Group B. + * @return true if user is a member of the given group. Otherwise returns false. + */ + default boolean isUserInGroup(String id, String group) { + return isUserInGroup(id, group, true); + } + + /** + * Checks whether or not a user with the given id is a member of the given group. + * + * @param id the user id. It can be the id, username or email + * @param group the group path. For instance, /Group A/Group B. + * @param checkParent if true, this method returns true even though the user is not directly associated with the given group but a member of any child of the group. + * @return true if user is a member of the given group. Otherwise returns false. + */ + boolean isUserInGroup(String id, String group, boolean checkParent); + + /** + * Checks whether or not a user with the given id is granted with the given realm role. + * + * @param id the user id. It can be the id, username or email + * @param role the role name + * @return true if the user is granted with the role. Otherwise, false. + */ + boolean isUserInRealmRole(String id, String role); + + /** + * Checks whether or not a user with the given id is granted with the given client role. + * + * @param id the user id. It can be the id, username or email + * @param clientId the client id + * @param role the role name + * @return true if the user is granted with the role. Otherwise, false. + */ + boolean isUserInClientRole(String id, String clientId, String role); + + /** + * Checks whether or not a group is granted with the given realm role. + * + * @param group the group path. For instance, /Group A/Group B. + * @param role the role name + * @return true if the group is granted with the role. Otherwise, false. + */ + boolean isGroupInRole(String group, String role); + + /** + * Returns all realm roles granted for a user with the given id. + * + * @param id the user id. It can be the id, username or email + * @return the roles granted to the user + */ + List getUserRealmRoles(String id); + + /** + * Returns all client roles granted for a user with the given id. + * + * @param id the user id. It can be the id, username or email + * @param clientId the client id + * @return the roles granted to the user + */ + List getUserClientRoles(String id, String clientId); + + /** + * Returns all groups which the user with the given id is a member. + * + * @param id the user id. It can be the id, username or email + * @return the groups which the user is a member + */ + List getUserGroups(String id); + + /** + * Returns all attributes associated with the a user with the given id. + * + * @param id the user id. It can be the id, username or email + * @return a map with the attributes associated with the user + */ + Map> getUserAttributes(String id); +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java new file mode 100644 index 0000000000..9ffad7b417 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java @@ -0,0 +1,578 @@ +/* + * Copyright 2018 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.testsuite.authz; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; +import org.junit.Test; +import org.keycloak.authorization.AuthorizationProvider; +import org.keycloak.authorization.Decision; +import org.keycloak.authorization.Decision.Effect; +import org.keycloak.authorization.attribute.Attributes; +import org.keycloak.authorization.common.DefaultEvaluationContext; +import org.keycloak.authorization.identity.Identity; +import org.keycloak.authorization.model.Policy; +import org.keycloak.authorization.model.ResourceServer; +import org.keycloak.authorization.permission.ResourcePermission; +import org.keycloak.authorization.policy.evaluation.DefaultEvaluation; +import org.keycloak.authorization.policy.evaluation.Evaluation; +import org.keycloak.authorization.policy.provider.PolicyProvider; +import org.keycloak.authorization.store.StoreFactory; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.RepresentationToModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper; +import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; +import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.keycloak.testsuite.util.ClientBuilder; +import org.keycloak.testsuite.util.GroupBuilder; +import org.keycloak.testsuite.util.RealmBuilder; +import org.keycloak.testsuite.util.RoleBuilder; +import org.keycloak.testsuite.util.RolesBuilder; +import org.keycloak.testsuite.util.UserBuilder; + +/** + * @author Pedro Igor + */ +public class PolicyEvaluationTest extends AbstractAuthzTest { + + @Override + public void addTestRealms(List testRealms) { + ProtocolMapperRepresentation groupProtocolMapper = new ProtocolMapperRepresentation(); + + groupProtocolMapper.setName("groups"); + groupProtocolMapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID); + groupProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + groupProtocolMapper.setConsentRequired(false); + Map config = new HashMap<>(); + config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups"); + config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + config.put("full.path", "true"); + groupProtocolMapper.setConfig(config); + + testRealms.add(RealmBuilder.create().name("authz-test") + .roles(RolesBuilder.create() + .realmRole(RoleBuilder.create().name("uma_authorization").build()) + .realmRole(RoleBuilder.create().name("role-a").build()) + .realmRole(RoleBuilder.create().name("role-b").build()) + ) + .group(GroupBuilder.create().name("Group A") + .subGroups(Arrays.asList("Group B", "Group D").stream().map(name -> { + if ("Group B".equals(name)) { + return GroupBuilder.create().name(name).subGroups(Arrays.asList("Group C", "Group E").stream().map(new Function() { + @Override + public GroupRepresentation apply(String name) { + return GroupBuilder.create().name(name).build(); + } + }).collect(Collectors.toList())).build(); + } + return GroupBuilder.create().name(name).realmRoles(Arrays.asList("role-a")).build(); + }).collect(Collectors.toList())).build()) + .group(GroupBuilder.create().name("Group E").build()) + .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization", "role-a").addGroups("Group A")) + .user(UserBuilder.create().username("alice").password("password").addRoles("uma_authorization").addGroups("/Group A/Group B/Group E")) + .user(UserBuilder.create().username("kolo").password("password").addRoles("uma_authorization").addGroups("/Group A/Group D")) + .user(UserBuilder.create().username("trinity").password("password").addRoles("uma_authorization").role("role-mapping-client", "client-role-a")) + .user(UserBuilder.create().username("jdoe").password("password").addGroups("/Group A/Group B", "/Group A/Group D")) + .client(ClientBuilder.create().clientId("resource-server-test") + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/resource-server-test") + .defaultRoles("uma_protection") + .directAccessGrants() + .protocolMapper(groupProtocolMapper)) + .client(ClientBuilder.create().clientId("role-mapping-client") + .defaultRoles("client-role-a", "client-role-b")) + .build()); + } + + @Deployment + public static WebArchive deploy() { + return RunOnServerDeployment.create(AbstractAuthzTest.class); + } + + @Test + public void testCheckUserInGroup() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserInGroup); + } + + public static void testCheckUserInGroup(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserInGroup"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('marta', 'Group C')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('marta', 'Group A')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('marta', '/Group A')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('marta', '/Group A/Group B')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('alice', '/Group A/Group B/Group E')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('alice', '/Group A')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (!realm.isUserInGroup('alice', '/Group A', false)) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('alice', '/Group E')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInGroup('alice', 'Group E')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + } + + @Test + public void testCheckUserInRole() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserInRole); + } + + public static void testCheckUserInRole(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserInRole"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInRealmRole('marta', 'role-a')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInRealmRole('marta', 'role-b')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + } + + @Test + public void testCheckUserInClientRole() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserInClientRole); + } + + public static void testCheckUserInClientRole(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserInClientRole"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInClientRole('trinity', 'role-mapping-client', 'client-role-a')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isUserInRealmRole('trinity', 'client-role-b')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + } + + @Test + public void testCheckGroupInRole() { + testingClient.server().run(PolicyEvaluationTest::testCheckGroupInRole); + } + + public static void testCheckGroupInRole(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckGroupInRole"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isGroupInRole('/Group A/Group D', 'role-a')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + + builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("if (realm.isGroupInRole('/Group A/Group D', 'role-b')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + policyRepresentation.setId(policy.getId()); + policy = RepresentationToModel.toModel(policyRepresentation, authorization, policy); + + evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertNull(evaluation.getEffect()); + } + + @Test + public void testCheckUserRealmRoles() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserRealmRoles); + } + + public static void testCheckUserRealmRoles(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserRealmRoles"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("var roles = realm.getUserRealmRoles('marta');"); + builder.append("if (roles.size() == 2 && roles.contains('uma_authorization') && roles.contains('role-a')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + } + + @Test + public void testCheckUserClientRoles() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserClientRoles); + } + + public static void testCheckUserClientRoles(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserClientRoles"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("var roles = realm.getUserClientRoles('trinity', 'role-mapping-client');"); + builder.append("if (roles.size() == 1 && roles.contains('client-role-a')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + } + + @Test + public void testCheckUserGroups() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserGroups); + } + + public static void testCheckUserGroups(KeycloakSession session) { + session.getContext().setRealm(session.realms().getRealmByName("authz-test")); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserGroups"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("var groups = realm.getUserGroups('jdoe');"); + builder.append("if (groups.size() == 2 && groups.contains('/Group A/Group B') && groups.contains('/Group A/Group D')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + } + + @Test + public void testCheckUserAttributes() { + testingClient.server().run(PolicyEvaluationTest::testCheckUserAttributes); + } + + public static void testCheckUserAttributes(KeycloakSession session) { + RealmModel realm = session.realms().getRealmByName("authz-test"); + UserModel jdoe = session.users().getUserByUsername("jdoe", realm); + + jdoe.setAttribute("a1", Arrays.asList("1", "2")); + jdoe.setSingleAttribute("a2", "3"); + + session.getContext().setRealm(realm); + AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class); + ClientModel clientModel = session.realms().getClientByClientId("resource-server-test", session.getContext().getRealm()); + StoreFactory storeFactory = authorization.getStoreFactory(); + ResourceServer resourceServer = storeFactory.getResourceServerStore().findById(clientModel.getId()); + JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation(); + + policyRepresentation.setName("testCheckUserAttributes"); + StringBuilder builder = new StringBuilder(); + + builder.append("var realm = $evaluation.getRealm();"); + builder.append("var attributes = realm.getUserAttributes('jdoe');"); + builder.append("if (attributes.size() == 2 && attributes.containsKey('a1') && attributes.containsKey('a2') && attributes.get('a1').size() == 2 && attributes.get('a2').get(0).equals('3')) { $evaluation.grant(); }"); + + policyRepresentation.setCode(builder.toString()); + + Policy policy = storeFactory.getPolicyStore().create(policyRepresentation, resourceServer); + PolicyProvider provider = authorization.getProvider(policy.getType()); + + DefaultEvaluation evaluation = createEvaluation(session, authorization, resourceServer, policy); + + provider.evaluate(evaluation); + + Assert.assertEquals(Effect.PERMIT, evaluation.getEffect()); + } + + @NotNull + private static DefaultEvaluation createEvaluation(KeycloakSession session, AuthorizationProvider authorization, ResourceServer resourceServer, Policy policy) { + return new DefaultEvaluation(new ResourcePermission(null, null, resourceServer), new DefaultEvaluationContext(new Identity() { + @Override + public String getId() { + return null; + } + + @Override + public Attributes getAttributes() { + return null; + } + }, session), policy, policy, new Decision() { + @Override + public void onDecision(Evaluation evaluation) { + + } + }, authorization); + } +}