Aligning partial evaluation with the outcome from regular evaluations

Closes #38626

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2025-04-02 22:29:26 -03:00
parent 94673a6eb0
commit dbb0179a93
8 changed files with 728 additions and 415 deletions

View File

@ -23,9 +23,11 @@ import java.util.Map;
import java.util.Set;
public class ResourceType {
private final String type;
private final Set<String> scopes;
private final Map<String, Set<String>> scopeAliases;
private final String groupType;
@JsonCreator
public ResourceType(@JsonProperty("type") String type, @JsonProperty("scopes") Set<String> scopes) {
@ -33,9 +35,14 @@ public class ResourceType {
}
public ResourceType(String type, Set<String> scopes, Map<String, Set<String>> scopeAliases) {
this(type, scopes, scopeAliases, null);
}
public ResourceType(String type, Set<String> scopes, Map<String, Set<String>> scopeAliases, String groupType) {
this.type = type;
this.scopes = Collections.unmodifiableSet(scopes);
this.scopeAliases = scopeAliases;
this.groupType = groupType;
}
public String getType() {
@ -49,4 +56,8 @@ public class ResourceType {
public Map<String, Set<String>> getScopeAliases() {
return Collections.unmodifiableMap(scopeAliases);
}
public String getGroupType() {
return groupType;
}
}

View File

@ -0,0 +1,212 @@
/*
* Copyright 2025 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.models.jpa;
import static org.keycloak.authorization.AdminPermissionsSchema.GROUPS_RESOURCE_TYPE;
import static org.keycloak.authorization.AdminPermissionsSchema.USERS_RESOURCE_TYPE;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.keycloak.authorization.AdminPermissionsSchema;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.policy.provider.PartialEvaluationContext;
import org.keycloak.authorization.policy.provider.PartialEvaluationStorageProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
/**
* A {@link PartialEvaluationStorageProvider} that provides support for partial evaluation when querying {@link UserModel}.
*/
public interface JpaUserPartialEvaluationProvider extends PartialEvaluationStorageProvider {
KeycloakSession getSession();
EntityManager getEntityManager();
@Override
default List<Predicate> getFilters(PartialEvaluationContext context) {
KeycloakSession session = getSession();
if (!AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(session.getContext().getRealm())) {
// support for FGAP v1, remove once v1 is removed
Set<String> userGroups = (Set<String>) session.getAttribute(UserModel.GROUPS);
if (userGroups != null) {
return List.of(getFilterByGroupMembership(session, context, userGroups));
}
return List.of();
}
return Optional.ofNullable(getAllowedGroupFilters(context)).map(List::of).orElse(List.of());
}
@Override
default List<Predicate> getNegateFilters(PartialEvaluationContext context) {
return Optional.ofNullable(getDeniedGroupsFilters(context)).map(List::of).orElse(List.of());
}
private Predicate getAllowedGroupFilters(PartialEvaluationContext context) {
Set<String> allowedGroups = context.getAllowedGroups();
if (allowedGroups.isEmpty()) {
return null;
}
if (context.deniedResources().contains(USERS_RESOURCE_TYPE)) {
return null;
}
if (context.isResourceTypeAllowed()) {
return null;
}
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
if (allowedGroups.contains(GROUPS_RESOURCE_TYPE)) {
return cb.exists(createUserMembershipSubquery(context));
}
return cb.exists(createUserMembershipSubquery(context, root -> root.get("groupId").in(allowedGroups)));
}
private Predicate getDeniedGroupsFilters(PartialEvaluationContext context) {
CriteriaBuilder cb = context.getCriteriaBuilder();
Set<String> allowedGroups = context.getAllowedGroups();
Set<String> deniedGroups = context.getDeniedGroups();
if (deniedGroups.contains(GROUPS_RESOURCE_TYPE)) {
// no access granted to group resources
Predicate notMembers = cb.not(cb.exists(createUserMembershipSubquery(context)));
// access denied for the group resource type
if (context.isResourceTypeAllowed()) {
// access granted to all resources
if (allowedGroups.isEmpty()) {
if (context.getDeniedGroupIds().isEmpty()) {
// filter group members but allow
return cb.and(cb.or(notMembers, context.getPath().get("id").in(context.getAllowedResourceIds())));
}
return notMembers;
}
Predicate onlySpecificGroups = cb.exists(createUserMembershipSubquery(context, root -> root.get("groupId").in(allowedGroups)));
return cb.and(cb.or(notMembers, onlySpecificGroups));
}
return cb.not(cb.exists(createUserMembershipSubquery(context, root -> root.get("groupId").in(context.getDeniedGroupIds()))));
}
if (context.getAllowedResources().isEmpty() && (allowedGroups.isEmpty() || context.deniedResources().contains(USERS_RESOURCE_TYPE))) {
return null;
}
return cb.not(cb.exists(createUserMembershipSubquery(context, root -> root.get("groupId").in(deniedGroups))));
}
private Subquery<?> createUserMembershipSubquery(PartialEvaluationContext context) {
return createUserMembershipSubquery(context, null);
}
private Subquery<?> createUserMembershipSubquery(PartialEvaluationContext context, Function<Root<?>, Predicate> predicate) {
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<?> query = context.criteriaQuery();
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<?> from = subquery.from(UserGroupMembershipEntity.class);
subquery.select(cb.literal(1));
Path<?> root = context.getPath();
List<Predicate> finalPredicates = new ArrayList<>();
if (predicate != null) {
finalPredicates.add(predicate.apply(from));
}
finalPredicates.add(cb.equal(from.get("user").get("id"), root.get("id")));
subquery.where(finalPredicates.toArray(Predicate[]::new));
return subquery;
}
/**
* @deprecated remove once FGAP v1 is removed
*/
@Deprecated
private Predicate getFilterByGroupMembership(KeycloakSession session, PartialEvaluationContext context, Set<String> groupIds) {
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<?> query = context.criteriaQuery();
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<?> from = subquery.from(UserGroupMembershipEntity.class);
subquery.select(cb.literal(1));
List<Predicate> subPredicates = new ArrayList<>();
subPredicates.add(from.get("groupId").in(groupIds));
Path<?> root = context.getPath();
subPredicates.add(cb.equal(from.get("user").get("id"), root.get("id")));
Subquery<Integer> subquery1 = query.subquery(Integer.class);
subquery1.select(cb.literal(1));
Root<ResourceEntity> from1 = subquery1.from(ResourceEntity.class);
List<Predicate> subs = new ArrayList<>();
Expression<String> groupId = from.get("groupId");
RealmModel realm = session.getContext().getRealm();
if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) {
subs.add(cb.like(from1.get("name"), groupId));
} else {
subs.add(cb.like(from1.get("name"), cb.concat("group.resource.", groupId)));
}
subquery1.where(subs.toArray(Predicate[]::new));
subPredicates.add(cb.exists(subquery1));
subquery.where(subPredicates.toArray(Predicate[]::new));
return cb.exists(subquery);
}
}

View File

@ -19,8 +19,6 @@ package org.keycloak.models.jpa;
import jakarta.persistence.criteria.Path;
import org.keycloak.authorization.AdminPermissionsSchema;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.policy.provider.PartialEvaluationStorageProvider;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
@ -63,7 +61,6 @@ import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.keycloak.storage.jpa.JpaHashUtils;
import org.keycloak.utils.StringUtil;
@ -88,7 +85,7 @@ import static org.keycloak.utils.StreamsUtil.closing;
* @version $Revision: 1 $
*/
@SuppressWarnings("JpaQueryApiInspection")
public class JpaUserProvider implements UserProvider, UserCredentialStore, PartialEvaluationStorageProvider {
public class JpaUserProvider implements UserProvider, UserCredentialStore, JpaUserPartialEvaluationProvider {
private static final String EMAIL = "email";
private static final String EMAIL_VERIFIED = "emailVerified";
@ -1098,112 +1095,12 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore, Parti
}
@Override
public List<Predicate> getFilters(EvaluationContext evaluationContext) {
if (!AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(session.getContext().getRealm())) {
// support for FGAP v1
Set<String> userGroups = (Set<String>) session.getAttribute(UserModel.GROUPS);
if (userGroups != null) {
return List.of(getFilterByGroupMembership(session, evaluationContext, userGroups));
}
return List.of();
}
Predicate predicate = getFilterByGroupMembership(evaluationContext, false);
if (predicate != null) {
return List.of(predicate);
}
return List.of();
public KeycloakSession getSession() {
return session;
}
@Override
public List<Predicate> getNegateFilters(EvaluationContext evaluationContext) {
Predicate predicate = getFilterByGroupMembership(evaluationContext, true);
if (predicate != null) {
return List.of(predicate);
}
return List.of();
}
@Deprecated
private Predicate getFilterByGroupMembership(KeycloakSession session, EvaluationContext evaluationContext, Set<String> groupIds) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<?> query = evaluationContext.criteriaQuery();
Subquery subquery = query.subquery(String.class);
Root<?> from = subquery.from(UserGroupMembershipEntity.class);
subquery.select(cb.literal(1));
List<Predicate> subPredicates = new ArrayList<>();
subPredicates.add(from.get("groupId").in(groupIds));
Path<?> root = evaluationContext.path();
subPredicates.add(cb.equal(from.get("user").get("id"), root.get("id")));
Subquery subquery1 = query.subquery(String.class);
subquery1.select(cb.literal(1));
Root from1 = subquery1.from(ResourceEntity.class);
List<Predicate> subs = new ArrayList<>();
Expression<String> groupId = from.get("groupId");
RealmModel realm = session.getContext().getRealm();
if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) {
subs.add(cb.like(from1.get("name"), groupId));
} else {
subs.add(cb.like(from1.get("name"), cb.concat("group.resource.", groupId)));
}
subquery1.where(subs.toArray(Predicate[]::new));
subPredicates.add(cb.exists(subquery1));
subquery.where(subPredicates.toArray(Predicate[]::new));
return cb.exists(subquery);
}
private Predicate getFilterByGroupMembership(EvaluationContext evaluationContext, boolean negate) {
if (negate && evaluationContext.deniedGroupIds().isEmpty()) {
return null;
}
if (!negate && evaluationContext.allowedGroupIds().isEmpty()) {
return null;
}
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<?> query = evaluationContext.criteriaQuery();
Subquery subquery = query.subquery(String.class);
Root<?> from = subquery.from(UserGroupMembershipEntity.class);
subquery.select(cb.literal(1));
List<Predicate> subPredicates = new ArrayList<>();
subPredicates.add(from.get("groupId").in(negate ? evaluationContext.deniedGroupIds() : evaluationContext.allowedGroupIds()));
Path<?> root = evaluationContext.path();
subPredicates.add(cb.equal(from.get("user").get("id"), root.get("id")));
subquery.where(subPredicates.toArray(Predicate[]::new));
if (negate) {
return cb.not(cb.exists(subquery));
}
return cb.exists(subquery);
public EntityManager getEntityManager() {
return em;
}
}

View File

@ -98,7 +98,7 @@ public class AdminPermissionsSchema extends AuthorizationSchema {
public static final ResourceType CLIENTS = new ResourceType(CLIENTS_RESOURCE_TYPE, Set.of(CONFIGURE, MANAGE, MAP_ROLES, MAP_ROLES_CLIENT_SCOPE, MAP_ROLES_COMPOSITE, VIEW));
public static final ResourceType GROUPS = new ResourceType(GROUPS_RESOURCE_TYPE, Set.of(MANAGE, VIEW, MANAGE_MEMBERSHIP, MANAGE_MEMBERS, VIEW_MEMBERS));
public static final ResourceType ROLES = new ResourceType(ROLES_RESOURCE_TYPE, Set.of(MAP_ROLE, MAP_ROLE_CLIENT_SCOPE, MAP_ROLE_COMPOSITE));
public static final ResourceType USERS = new ResourceType(USERS_RESOURCE_TYPE, Set.of(MANAGE, VIEW, IMPERSONATE, MAP_ROLES, MANAGE_GROUP_MEMBERSHIP), Map.of(VIEW, Set.of(VIEW_MEMBERS), MANAGE, Set.of(MANAGE_MEMBERS)));
public static final ResourceType USERS = new ResourceType(USERS_RESOURCE_TYPE, Set.of(MANAGE, VIEW, IMPERSONATE, MAP_ROLES, MANAGE_GROUP_MEMBERSHIP), Map.of(VIEW, Set.of(VIEW_MEMBERS), MANAGE, Set.of(MANAGE_MEMBERS)), GROUPS.getType());
public static final AdminPermissionsSchema SCHEMA = new AdminPermissionsSchema();
private final PartialEvaluator partialEvaluator = new PartialEvaluator();
@ -444,7 +444,7 @@ public class AdminPermissionsSchema extends AuthorizationSchema {
}
public List<Predicate> applyAuthorizationFilters(KeycloakSession session, ResourceType resourceType, PartialEvaluationStorageProvider evaluator, RealmModel realm, CriteriaBuilder builder, CriteriaQuery<?> queryBuilder, Path<?> path) {
return partialEvaluator.applyAuthorizationFilters(session, resourceType, evaluator, realm, builder, queryBuilder, path);
return partialEvaluator.getPredicates(session, resourceType, evaluator, realm, builder, queryBuilder, path);
}
public PolicyEvaluator getPolicyEvaluator(KeycloakSession session, ResourceServer resourceServer) {

View File

@ -17,8 +17,6 @@
package org.keycloak.authorization;
import static java.util.function.Predicate.not;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@ -33,14 +31,13 @@ import org.keycloak.Config;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.provider.PartialEvaluationContext;
import org.keycloak.authorization.policy.provider.PartialEvaluationPolicyProvider;
import org.keycloak.authorization.policy.provider.PartialEvaluationStorageProvider;
import org.keycloak.authorization.policy.provider.PartialEvaluationStorageProvider.EvaluationContext;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@ -50,99 +47,45 @@ import org.keycloak.representations.idm.authorization.ResourceType;
public class PartialEvaluator {
public List<Predicate> applyAuthorizationFilters(KeycloakSession session, ResourceType resourceType, PartialEvaluationStorageProvider storage, RealmModel realm, CriteriaBuilder builder, CriteriaQuery<?> queryBuilder, Path<?> path) {
private static final String NO_ID = "none";
private static final String ID_FIELD = "id";
public List<Predicate> getPredicates(KeycloakSession session, ResourceType resourceType, PartialEvaluationStorageProvider storage, RealmModel realm, CriteriaBuilder builder, CriteriaQuery<?> queryBuilder, Path<?> path) {
if (!AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) {
// feature not enabled, if a storage evaluator is provided try to resolve any filter from there
return storage == null ? List.of() : storage.getFilters(new EvaluationContext(resourceType, queryBuilder, path, Set.of(), Set.of()));
return storage == null ? List.of() : storage.getFilters(new PartialEvaluationContext(storage, builder, queryBuilder, path));
}
KeycloakContext context = session.getContext();
UserModel adminUser = context.getUser();
UserModel adminUser = session.getContext().getUser();
if (skipPartialEvaluation(session, adminUser, realm, resourceType)) {
if (shouldSkipPartialEvaluation(session, adminUser, realm, resourceType)) {
// only run partial evaluation if the admin user does not have view-* or manage-* role for specified resourceType or has any query-* role
return List.of();
}
// collect the result from the partial evaluation so that the filters can be applied
PartialResourceEvaluationResult result = evaluate(session, adminUser, resourceType);
EvaluationContext evaluationContext = new EvaluationContext(resourceType, queryBuilder, path, new HashSet<>(), new HashSet<>());
PartialEvaluationContext context = runEvaluation(session, adminUser, resourceType, storage, builder, queryBuilder, path);
if (AdminPermissionsSchema.USERS.equals(resourceType)) {
PartialResourceEvaluationResult evaluateGroups = evaluate(session, adminUser, AdminPermissionsSchema.GROUPS);
evaluationContext.allowedGroupIds().addAll(evaluateGroups.allowedIds());
evaluationContext.deniedGroupIds().addAll(evaluateGroups.deniedIds());
}
List<Predicate> predicates = new ArrayList<>();
Set<String> deniedIds = result.deniedIds();
if (!deniedIds.isEmpty()) {
// add filters to remove denied resources from the result set
predicates.add(builder.not(path.get("id").in(deniedIds)));
}
List<Predicate> storageFilters = storage == null ? List.of() : storage.getFilters(evaluationContext);
List<Predicate> storageNegateFilters = storage == null ? List.of() : storage.getNegateFilters(evaluationContext);
predicates.addAll(storageNegateFilters);
if (storageFilters.isEmpty() && (result.isResourceTypedDenied() || (!deniedIds.isEmpty() && result.rawAllowedIds().isEmpty()))) {
// do not return any result because there is no filter from the evaluator, and access is denied for the resource type
return List.of(builder.equal(path.get("id"), "none"));
}
Set<String> allowedIds = result.allowedIds();
if (allowedIds.isEmpty()) {
// no resources granted, filter them based on any filter previously set
predicates.addAll(storageFilters);
return predicates;
}
if (storageFilters.isEmpty()) {
// no filter from the evaluator, filter based on the resources that were granted
predicates.add(builder.and(path.get("id").in(allowedIds)));
} else {
// there are filters from the evaluator, the resources granted will be a returned using a or condition
List<Predicate> orPredicates = new ArrayList<>(storageFilters);
orPredicates.add(path.get("id").in(allowedIds));
predicates.add(builder.or(orPredicates.toArray(new Predicate[0])));
}
return predicates;
return buildPredicates(context);
}
private record PartialResourceEvaluationResult(ResourceType resourceType, Set<String> rawAllowedIds, Set<String> rawDeniedIds) {
Set<String> allowedIds() {
return rawAllowedIds.stream().filter(not(resourceType.getType()::equals)).collect(Collectors.toSet());
}
Set<String> deniedIds() {
return rawDeniedIds.stream().filter(not(resourceType.getType()::equals)).collect(Collectors.toSet());
}
boolean isResourceTypedDenied() {
return rawAllowedIds.isEmpty() && (rawDeniedIds.isEmpty() || (rawDeniedIds.size() == 1 && rawDeniedIds.contains(resourceType.getType())));
}
}
private PartialResourceEvaluationResult evaluate(KeycloakSession session, UserModel adminUser, ResourceType resourceType) {
Set<String> allowedIds = new HashSet<>();
Set<String> deniedIds = new HashSet<>();
private PartialEvaluationContext runEvaluation(KeycloakSession session, UserModel adminUser, ResourceType resourceType, PartialEvaluationStorageProvider storage, CriteriaBuilder builder, CriteriaQuery<?> queryBuilder, Path<?> path) {
Set<String> allowedResources = new HashSet<>();
Set<String> deniedResources = new HashSet<>();
List<PartialEvaluationPolicyProvider> policyProviders = getPartialEvaluationPolicyProviders(session);
for (PartialEvaluationPolicyProvider policyProvider : policyProviders) {
policyProvider.getPermissions(session, resourceType, adminUser).forEach(permission -> {
if (permission.getScopes().stream().map(Scope::getName).noneMatch(name -> name.startsWith(AdminPermissionsSchema.VIEW))) {
if (!hasViewScope(permission)) {
// only run partial evaluation for permissions with any view scope
return;
}
Set<String> ids = permission.getResources().stream().map(Resource::getName).collect(Collectors.toSet());
Set<Policy> policies = permission.getAssociatedPolicies();
for (Policy policy : policies) {
PartialEvaluationPolicyProvider provider = policyProviders.stream().filter((p) -> p.supports(policy)).findAny().orElse(null);
PartialEvaluationPolicyProvider provider = getPartialEvaluationPolicyProvider(policy, policyProviders);
if (provider == null) {
continue;
@ -155,25 +98,107 @@ public class PartialEvaluator {
}
if (granted) {
allowedIds.addAll(ids);
allowedResources.addAll(ids);
} else {
deniedIds.addAll(ids);
deniedResources.addAll(ids);
}
}
});
}
allowedIds.removeAll(deniedIds);
allowedResources.removeAll(deniedResources);
if (allowedIds.contains(resourceType.getType())) {
allowedIds.removeIf(not(resourceType.getType()::equals));
return createEvaluationContext(session, resourceType, allowedResources, deniedResources, storage, builder, queryBuilder, path, adminUser);
}
private List<Predicate> buildPredicates(PartialEvaluationContext context) {
List<Predicate> storageFilters = getStorageFilters(context);
CriteriaBuilder builder = context.getCriteriaBuilder();
Path<?> path = context.getPath();
if (isDenied(storageFilters, context)) {
// do not return any result because there is no filter from the evaluator, and access is denied for the resource type
return List.of(builder.equal(path.get(ID_FIELD), NO_ID));
}
Set<String> deniedIds = context.getDeniedResources();
ResourceType resourceType = context.getResourceType();
if (deniedIds.contains(resourceType.getType())) {
deniedIds.removeIf(not(resourceType.getType()::equals));
// do not filter by id if access is granted to the resource type
deniedIds = Set.of();
}
return new PartialResourceEvaluationResult(resourceType, allowedIds, deniedIds);
List<Predicate> predicates = new ArrayList<>();
if (!deniedIds.isEmpty()) {
// add filters to remove denied resources from the result set
predicates.add(builder.not(path.get(ID_FIELD).in(deniedIds)));
}
List<Predicate> storageNegateFilters = getStorageNegateFilters(context);
// add filters from the storage that deny access to resources
predicates.addAll(storageNegateFilters);
Set<String> allowedResourceIds = context.getAllowedResources();
if (allowedResourceIds.contains(resourceType.getType())) {
// do not filter by id if access is granted to the resource type
allowedResourceIds = Set.of();
}
if (allowedResourceIds.isEmpty()) {
// no resources granted, return any predicates created until now
predicates.addAll(storageFilters);
return predicates;
}
if (storageFilters.isEmpty()) {
// no filter from the evaluator, filter based on the resources that were granted
predicates.add(builder.and(path.get(ID_FIELD).in(allowedResourceIds)));
} else {
// there are filters from the evaluator, the resources granted will be a returned using a or condition
List<Predicate> orPredicates = new ArrayList<>(storageFilters);
orPredicates.add(path.get(ID_FIELD).in(allowedResourceIds));
predicates.add(builder.or(orPredicates.toArray(new Predicate[0])));
}
return predicates;
}
private PartialEvaluationContext createEvaluationContext(KeycloakSession session, ResourceType resourceType, Set<String> allowedResources, Set<String> deniedResources, PartialEvaluationStorageProvider storage, CriteriaBuilder builder, CriteriaQuery<?> queryBuilder, Path<?> path, UserModel adminUser) {
PartialEvaluationContext context = new PartialEvaluationContext(resourceType, allowedResources, deniedResources, storage, builder, queryBuilder, path);
String groupType = resourceType.getGroupType();
if (groupType != null) {
// if the resource type has support for groups, evaluate permissions for the group
ResourceType groupResourceType = AdminPermissionsSchema.SCHEMA.getResourceTypes().get(groupType);
if (groupResourceType == null) {
return context;
}
PartialEvaluationContext evaluateGroups = runEvaluation(session, adminUser, groupResourceType, storage, builder, queryBuilder, path);
context.setAllowedGroups(evaluateGroups.getAllowedResources());
context.setDeniedGroups(evaluateGroups.getDeniedResources());
}
return context;
}
private List<Predicate> getStorageFilters(PartialEvaluationContext context) {
PartialEvaluationStorageProvider storage = context.getStorage();
return storage == null ? List.of() : storage.getFilters(context);
}
private List<Predicate> getStorageNegateFilters(PartialEvaluationContext context) {
PartialEvaluationStorageProvider storage = context.getStorage();
return storage == null ? List.of() : storage.getNegateFilters(context);
}
private boolean isDenied(List<Predicate> storageFilters, PartialEvaluationContext context) {
return context.getAllowedResources().isEmpty() && storageFilters.isEmpty();
}
private List<PartialEvaluationPolicyProvider> getPartialEvaluationPolicyProviders(KeycloakSession session) {
@ -183,22 +208,23 @@ public class PartialEvaluator {
.toList();
}
private boolean skipPartialEvaluation(KeycloakSession session, UserModel user, RealmModel realm, ResourceType resourceType) {
private PartialEvaluationPolicyProvider getPartialEvaluationPolicyProvider(Policy policy, List<PartialEvaluationPolicyProvider> policyProviders) {
return policyProviders.stream()
.filter((p) -> p.supports(policy))
.findAny()
.orElse(null);
}
private boolean hasViewScope(Policy permission) {
return permission.getScopes().stream().map(Scope::getName).anyMatch(name -> name.startsWith(AdminPermissionsSchema.VIEW));
}
private boolean shouldSkipPartialEvaluation(KeycloakSession session, UserModel user, RealmModel realm, ResourceType resourceType) {
if (user == null) {
return false;
}
String clientId;
if (realm.getName().equals(Config.getAdminRealm())) {
clientId = realm.getMasterAdminClient().getClientId();
} else {
ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
clientId = client == null ? null : client.getClientId();
}
// client probably removed when removing the realm
ClientModel client = clientId == null ? null : session.clients().getClientByClientId(realm, clientId);
ClientModel client = getRealmManagementClient(session);
if (client == null) {
return true;
@ -208,20 +234,32 @@ public class PartialEvaluator {
return user.hasRole(client.getRole(AdminRoles.VIEW_USERS)) || user.hasRole(client.getRole(AdminRoles.MANAGE_USERS)) || !hasAnyQueryAdminRole(client, user);
} else if (resourceType.equals(AdminPermissionsSchema.CLIENTS)) {
return user.hasRole(client.getRole(AdminRoles.VIEW_CLIENTS)) || user.hasRole(client.getRole(AdminRoles.MANAGE_CLIENTS)) || !hasAnyQueryAdminRole(client, user);
} else {
return false;
}
}
private boolean hasAnyQueryAdminRole(ClientModel client, UserModel user) {
for (String adminRole : List.of(AdminRoles.QUERY_CLIENTS, AdminRoles.QUERY_GROUPS, AdminRoles.QUERY_USERS)) {
RoleModel role = client.getRole(adminRole);
if (user.hasRole(role)) {
return true;
}
}
return false;
}
private ClientModel getRealmManagementClient(KeycloakSession session) {
RealmModel realm = session.getContext().getRealm();
if (realm.getName().equals(Config.getAdminRealm())) {
return session.clients().getClientByClientId(realm, realm.getMasterAdminClient().getClientId());
}
return realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
}
private boolean hasAnyQueryAdminRole(ClientModel client, UserModel user) {
boolean result = false;
for (String adminRole : List.of(AdminRoles.QUERY_CLIENTS, AdminRoles.QUERY_GROUPS, AdminRoles.QUERY_USERS)) {
RoleModel role = client.getRole(adminRole);
if (user.hasRole(role)) {
result = true;
break;
}
}
return result;
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright 2025 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.provider;
import static java.util.function.Predicate.not;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import org.keycloak.representations.idm.authorization.ResourceType;
/**
* An {@link PartialEvaluationContext} instance provides access to contextual information when building a query for realm
* resources of a given {@link ResourceType}.
*/
public class PartialEvaluationContext {
private final ResourceType resourceType;
private final CriteriaQuery<?> criteriaQuery;
private final Path<?> path;
private final PartialEvaluationStorageProvider storage;
private final CriteriaBuilder criteriaBuilder;
private final Set<String> allowedResources;
private final Set<String> deniedResources;
private Set<String> allowedGroups = Set.of();
private Set<String> deniedGroups = Set.of();
public PartialEvaluationContext(PartialEvaluationStorageProvider storage, CriteriaBuilder criteriaBuilder, CriteriaQuery<?> criteriaQuery, Path<?> path) {
this(null, Set.of(), Set.of(), storage, criteriaBuilder, criteriaQuery, path);
}
public PartialEvaluationContext(ResourceType resourceType, Set<String> allowedResources, Set<String> deniedResources, PartialEvaluationStorageProvider storage, CriteriaBuilder criteriaBuilder, CriteriaQuery<?> criteriaQuery, Path<?> path) {
this.allowedResources = allowedResources;
this.deniedResources = deniedResources;
this.storage = storage;
this.criteriaBuilder = criteriaBuilder;
this.criteriaQuery = criteriaQuery;
this.path = path;
this.resourceType = resourceType;
}
public boolean isResourceTypeAllowed() {
return allowedResources.contains(resourceType.getType());
}
public Set<String> getAllowedResourceIds() {
return allowedResources.stream().filter(not(resourceType.getType()::equals)).collect(Collectors.toSet());
}
public Set<String> getDeniedGroupIds() {
return deniedGroups.stream().filter(not(resourceType.getGroupType()::equals)).collect(Collectors.toSet());
}
public void setAllowedGroups(Set<String> allowedGroups) {
this.allowedGroups = allowedGroups;
}
public void setDeniedGroups(Set<String> deniedGroups) {
this.deniedGroups = deniedGroups;
}
public Set<String> getDeniedGroups() {
return deniedGroups;
}
public Set<String> getAllowedGroups() {
return allowedGroups;
}
public Set<String> getAllowedResources() {
return allowedResources;
}
public Path<?> getPath() {
return path;
}
public Set<String> deniedResources() {
return deniedResources;
}
public CriteriaQuery<?> criteriaQuery() {
return criteriaQuery;
}
public CriteriaBuilder getCriteriaBuilder() {
return criteriaBuilder;
}
public ResourceType getResourceType() {
return resourceType;
}
public PartialEvaluationStorageProvider getStorage() {
return storage;
}
public Set<String> getDeniedResources() {
return deniedResources;
}
}

View File

@ -19,12 +19,8 @@
package org.keycloak.authorization.policy.provider;
import java.util.List;
import java.util.Set;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import org.keycloak.representations.idm.authorization.ResourceType;
/**
* If a realm has the {@link org.keycloak.common.Profile.Feature#ADMIN_FINE_GRAINED_AUTHZ} feature enabled,
@ -41,26 +37,15 @@ public interface PartialEvaluationStorageProvider {
* @param evaluationContext the evaluation context.
* @return the list of predicates
*/
List<Predicate> getFilters(EvaluationContext evaluationContext);
List<Predicate> getFilters(PartialEvaluationContext evaluationContext);
/**
* A callback method that will be called when building queries for realm resources to deny access to resources. It returns a list of
* {@link Predicate} instances representing the filters that should be applied to queries
* when querying realm resources.
*
* @param evaluationContext the evaluation context.
* @param context the evaluation context.
* @return the list of predicates
*/
List<Predicate> getNegateFilters(EvaluationContext evaluationContext);
/**
* An {@link EvaluationContext} instance provides access to contextual information when building a query for realm
* resources of a given {@link ResourceType}.
*
* @param resourceType the type of the resource to query
* @param criteriaQuery the query to rely on when building predicates
* @param path the path for the root entity
*/
record EvaluationContext(ResourceType resourceType, CriteriaQuery<?> criteriaQuery, Path<?> path, Set<String> allowedGroupIds, Set<String> deniedGroupIds) {
}
List<Predicate> getNegateFilters(PartialEvaluationContext context);
}

View File

@ -28,9 +28,15 @@ import static org.keycloak.authorization.AdminPermissionsSchema.USERS_RESOURCE_T
import static org.keycloak.authorization.AdminPermissionsSchema.VIEW;
import static org.keycloak.authorization.AdminPermissionsSchema.VIEW_MEMBERS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import jakarta.ws.rs.ForbiddenException;
import org.junit.jupiter.api.AfterEach;
@ -67,6 +73,17 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@InjectUser(ref = "jdoe")
ManagedUser userJdoe;
@InjectUser(ref = "tom")
ManagedUser userTom;
@InjectUser(ref = "mary")
ManagedUser userMary;
ManagedUser myadmin;
private List<ManagedUser> ALL_GROUP_MEMBERS;
private List<ManagedUser> ALL_USERS;
@InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, client = "myclient", user = "myadmin")
Keycloak realmAdminClient;
@ -75,17 +92,36 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@BeforeEach
public void onBefore() {
for (int i = 0; i < 2; i++) {
GroupRepresentation group = new GroupRepresentation();
group.setName("group-" + i);
realm.admin().groups().add(group).close();
Map<String, List<ManagedUser>> groupMembers = new HashMap<>();
groupMembers.put("group-0", List.of(userAlice));
groupMembers.put("group-1", List.of(userBob));
groupMembers.put("group-2", List.of(userMary, userTom));
for (Entry<String, List<ManagedUser>> group : groupMembers.entrySet()) {
String name = group.getKey();
GroupRepresentation rep = new GroupRepresentation();
rep.setName(name);
realm.admin().groups().add(rep).close();
List<ManagedUser> members = group.getValue();
for (ManagedUser member : members) {
joinGroup(member, name);
}
}
joinGroup(userAlice, "group-0");
joinGroup(userBob, "group-1");
UserRepresentation myadmin = realm.admin().users().search("myadmin").get(0);
allowPolicy = createUserPolicy(realm, client, "Only My Admin User Policy", myadmin.getId());
denyPolicy = createUserPolicy(Logic.NEGATIVE, realm, client,"Not My Admin User Policy", myadmin.getId());
this.myadmin = new ManagedUser(myadmin, realm.admin().users().get(myadmin.getId()));
allowPolicy = createUserPolicy(realm, client, "Only My Admin User Policy", realm.admin().users().search("myadmin").get(0).getId());
denyPolicy = createUserPolicy(Logic.NEGATIVE, realm, client,"Not My Admin User Policy", realm.admin().users().search("myadmin").get(0).getId());
ALL_USERS = new ArrayList<>();
groupMembers.values().forEach(ALL_USERS::addAll);
ALL_USERS.add(this.myadmin);
ALL_USERS.add(userJdoe);
ALL_GROUP_MEMBERS = groupMembers.values().stream().flatMap(Collection::stream).toList();
}
@AfterEach
@ -101,8 +137,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_01() {
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -113,8 +148,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_02() {
allowAllGroups();
// TODO: should see only user members of groups
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("alice", "bob"));
assertFilter(ALL_GROUP_MEMBERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -125,8 +159,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_03() {
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -137,8 +170,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_04() {
allowGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("alice"));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -150,9 +182,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
allowAllGroups();
// TODO: should only see users that are members of groups
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("alice", "bob"));
assertFilter(ALL_GROUP_MEMBERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -164,9 +194,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
denyAllGroups();
// TODO: should see only members of single group
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("alice", "bob"));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -177,8 +205,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_07() {
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -190,9 +217,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyGroup("group-0");
allowAllGroups();
// TODO: should see only members of groups except those from single group
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("bob"));
assertFilter(userBob, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -216,8 +241,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_10() {
allowAllUsers();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -229,8 +253,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowAllUsers();
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -242,9 +265,8 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowAllUsers();
denyAllGroups();
// TODO: should not see users members of a group
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder("jdoe", "myadmin"));
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userJdoe.getUsername(), "myadmin"));
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -256,9 +278,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowAllUsers();
allowGroup("group-0");
// TODO: should return all users
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -271,8 +291,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -285,9 +304,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
denyAllGroups();
// TODO: should see user that are not a member of a group or members of the single group
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice, userJdoe, myadmin);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -299,8 +316,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowAllUsers();
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userBob, userJdoe, myadmin, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -313,8 +329,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userBob, userJdoe, myadmin, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -327,9 +342,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyGroup("group-0");
denyAllGroups();
// TODO: should see only users not members of groups
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userJdoe.getUsername(), "myadmin"));
assertFilter(userJdoe, myadmin);
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -340,8 +353,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
public void test_19() {
denyAllUsers();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -353,8 +365,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyAllUsers();
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -366,8 +377,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyAllUsers();
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -379,9 +389,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyAllUsers();
allowGroup("group-0");
// NOK denying all users permissions should have precedence over group permissions and not return users
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -394,9 +402,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
allowAllGroups();
// NOK denying all users permissions should have precedence over group permissions and not return users
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -409,9 +415,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
allowGroup("group-0");
denyAllGroups();
// NOK denying all users permissions should have precedence over group permissions and not return users
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -423,8 +427,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyAllUsers();
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -437,8 +440,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -451,8 +453,7 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
denyGroup("group-0");
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -461,10 +462,9 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_28() {
allowUser(userAlice.getId());
allowUser(userAlice);
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -473,11 +473,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_29() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice, userBob, userTom, userMary);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -486,11 +485,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_30() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -499,11 +497,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_31() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowGroup("group-1");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername()));
assertFilter(userAlice, userBob);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -512,13 +509,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_32() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowGroup("group-0");
allowAllGroups();
//NOK all group members should be granted
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername()));
assertFilter(userAlice, userBob, userTom, userMary);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -527,12 +522,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_33() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowGroup("group-0");
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -541,12 +535,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_33_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowGroup("group-1");
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername()));
assertFilter(userAlice, userBob);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -555,11 +548,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_34() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -568,13 +560,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_35() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyGroup("group-0");
allowAllGroups();
//NOK should grant access to users from other groups
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername()));
assertFilter(userBob, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -583,12 +573,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_35_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyGroup("group-1");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice, userTom, userMary);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -597,13 +586,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_36() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyGroup("group-0");
denyAllGroups();
//NOK should grant access to users from other groups
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -612,11 +599,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_37() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -625,12 +611,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_38() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -639,13 +624,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_39() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
denyAllGroups();
//NOK should not return members from groups other than the user that was granted
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userAlice, userJdoe, myadmin);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -654,13 +637,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_40() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowGroup("group-0");
//NOK not sure if it should restrict or grant access
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -669,13 +650,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_40_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowGroup("group-1");
//NOK not sure if it should restrict or grant access
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -684,13 +663,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_41() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowGroup("group-1");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -699,14 +677,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_42() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowGroup("group-0");
denyAllGroups();
//TODO should return single resource and users not members of groups
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userAlice, userJdoe, myadmin);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -715,14 +691,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_42_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
allowGroup("group-1");
denyAllGroups();
//NOK not sure if it should restrict or grant access
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(ALL_USERS);
assertUpdate(userAlice, true);
assertUpdate(userBob, true);
@ -731,12 +704,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_43() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userBob, userJdoe, myadmin, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -745,13 +717,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_44() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
denyGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userBob, userJdoe, myadmin, userTom, userMary);
assertUpdate(userAlice, false);
assertUpdate(userBob, true);
@ -760,14 +731,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_45() {
allowUser(userAlice.getId());
allowUser(userAlice);
allowAllUsers();
denyGroup("group-0");
denyAllGroups();
//NOK filtering is not taking into account when group membership is denied to all groups
//List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
//assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userBob.getUsername(), userJdoe.getUsername(), "myadmin"));
assertFilter(userJdoe, myadmin);
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -776,11 +745,10 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_46() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -789,12 +757,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_47() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -803,12 +770,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_48() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -817,12 +783,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_49() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
allowGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -831,12 +796,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_49_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
allowGroup("group-1");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername(), userBob.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -845,13 +809,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_50() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
allowGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -860,13 +823,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_51() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
allowGroup("group-0");
denyAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -875,12 +837,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_52() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
denyGroup("group-0");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -889,12 +850,11 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_52_a() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
denyGroup("group-1");
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(userAlice.getUsername()));
assertFilter(userAlice);
assertUpdate(userAlice, true);
assertUpdate(userBob, false);
@ -903,13 +863,12 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_53() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
denyGroup("group-0");
allowAllGroups();
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
@ -918,26 +877,104 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
@Test
public void test_54() {
allowUser(userAlice.getId());
allowUser(userAlice);
denyAllUsers();
denyGroup("group-0");
denyAllGroups();
// NOK should return alice because the resource is granted
// List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
// assertThat(search.isEmpty(), is(true));
assertFilter();
assertUpdate(userAlice, false);
assertUpdate(userBob, false);
assertUpdate(userJdoe, false);
}
@Test
public void test_55() {
allowAllUsers();
denyUser(userAlice);
denyUser(userJdoe);
assertFilter(userBob, myadmin, userMary, userTom);
assertUpdate(userBob, true);
assertUpdate(userAlice, false);
assertUpdate(userJdoe, false);
}
@Test
public void test_56() {
allowUser(userJdoe);
denyUser(userBob);
assertFilter(userJdoe);
assertUpdate(userJdoe, true);
assertUpdate(userBob, false);
assertUpdate(userAlice, false);
}
@Test
public void test_57() {
denyUser(userAlice);
denyUser(userJdoe);
assertFilter();
assertUpdate(userBob, false);
assertUpdate(userAlice, false);
assertUpdate(userJdoe, false);
}
@Test
public void test_58() {
denyUser(userTom);
allowGroup("group-2");
assertFilter(userMary);
assertUpdate(userMary, true);
assertUpdate(userTom, false);
assertUpdate(userJdoe, false);
}
@Test
public void test_59() {
allowAllUsers();
denyUser(userTom);
allowGroup("group-1");
assertFilter(userAlice, userBob, userMary, userJdoe, myadmin);
assertUpdate(userMary, true);
assertUpdate(userJdoe, true);
assertUpdate(userTom, false);
}
@Test
public void test_60() {
allowAllUsers();
denyUser(userTom);
allowGroup("group-1");
denyGroup("group-2");
assertFilter(userAlice, userBob, userJdoe, myadmin);
assertUpdate(userJdoe, true);
assertUpdate(userMary, false);
assertUpdate(userTom, false);
}
private void denyUser(ManagedUser user) {
createPermission(client, user.getId(), USERS_RESOURCE_TYPE, Set.of(VIEW, MANAGE), denyPolicy);
}
private void allowAllUsers() {
createAllPermission(client, USERS_RESOURCE_TYPE, allowPolicy, Set.of(VIEW, MANAGE));
}
private void allowUser(String id) {
createPermission(client, id , USERS_RESOURCE_TYPE, Set.of(VIEW, MANAGE), allowPolicy);
private void allowUser(ManagedUser user) {
createPermission(client, user.getId() , USERS_RESOURCE_TYPE, Set.of(VIEW, MANAGE), allowPolicy);
}
private void denyAllUsers() {
@ -1011,4 +1048,18 @@ public class UserResourceTypeEvaluationSpecTest extends AbstractPermissionTest {
}
}
}
private void assertFilter(List<ManagedUser> expected) {
assertFilter(expected.toArray(new ManagedUser[0]));
}
private void assertFilter(ManagedUser... expected) {
List<UserRepresentation> search = realmAdminClient.realm(realm.getName()).users().search(null, -1, -1);
if (expected.length == 0) {
assertThat(search.isEmpty(), is(true));
} else {
assertThat(search.stream().map(UserRepresentation::getUsername).toList(), containsInAnyOrder(Stream.of(expected).map(ManagedUser::getUsername).toArray()));
}
}
}