[KEYCLOAK-7950] - Fixes user pagination when using filtering users members of groups
This commit is contained in:
parent
17a1a33987
commit
b4b3527df7
45 changed files with 1183 additions and 839 deletions
|
@ -33,6 +33,14 @@ public class MultivaluedHashMap<K, V> extends HashMap<K, List<V>>
|
||||||
public MultivaluedHashMap() {
|
public MultivaluedHashMap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MultivaluedHashMap(Map<K, List<V>> map) {
|
||||||
|
if (map == null) {
|
||||||
|
throw new IllegalArgumentException("Map can not be null");
|
||||||
|
}
|
||||||
|
putAll(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public MultivaluedHashMap(MultivaluedHashMap<K, V> config) {
|
public MultivaluedHashMap(MultivaluedHashMap<K, V> config) {
|
||||||
addAll(config);
|
addAll(config);
|
||||||
}
|
}
|
||||||
|
|
47
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultLazyLoader.java
vendored
Normal file
47
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultLazyLoader.java
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.models.cache.infinispan;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link DefaultLazyLoader} that only fetches data once. This implementation is not thread-safe
|
||||||
|
* and cached data is assumed to not be shared across different threads to sync state.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class DefaultLazyLoader<S, D> implements LazyLoader<S, D> {
|
||||||
|
|
||||||
|
private final Function<S, D> loader;
|
||||||
|
private Supplier<D> fallback;
|
||||||
|
private D data;
|
||||||
|
|
||||||
|
public DefaultLazyLoader(Function<S, D> loader, Supplier<D> fallback) {
|
||||||
|
this.loader = loader;
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public D get(Supplier<S> sourceSupplier) {
|
||||||
|
if (data == null) {
|
||||||
|
S source = sourceSupplier.get();
|
||||||
|
data = source == null ? fallback.get() : this.loader.apply(source);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,29 +29,33 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class GroupAdapter implements GroupModel {
|
public class GroupAdapter implements GroupModel {
|
||||||
|
|
||||||
|
protected final CachedGroup cached;
|
||||||
|
protected final RealmCacheSession cacheSession;
|
||||||
|
protected final KeycloakSession keycloakSession;
|
||||||
|
protected final RealmModel realm;
|
||||||
|
private final Supplier<GroupModel> modelSupplier;
|
||||||
protected volatile GroupModel updated;
|
protected volatile GroupModel updated;
|
||||||
protected CachedGroup cached;
|
|
||||||
protected RealmCacheSession cacheSession;
|
|
||||||
protected KeycloakSession keycloakSession;
|
|
||||||
protected RealmModel realm;
|
|
||||||
|
|
||||||
public GroupAdapter(CachedGroup cached, RealmCacheSession cacheSession, KeycloakSession keycloakSession, RealmModel realm) {
|
public GroupAdapter(CachedGroup cached, RealmCacheSession cacheSession, KeycloakSession keycloakSession, RealmModel realm) {
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
this.cacheSession = cacheSession;
|
this.cacheSession = cacheSession;
|
||||||
this.keycloakSession = keycloakSession;
|
this.keycloakSession = keycloakSession;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
modelSupplier = this::getGroupModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void getDelegateForUpdate() {
|
protected void getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
cacheSession.registerGroupInvalidation(cached.getId());
|
cacheSession.registerGroupInvalidation(cached.getId());
|
||||||
updated = cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
updated = modelSupplier.get();
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,19 +132,19 @@ public class GroupAdapter implements GroupModel {
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
if (isUpdated()) return updated.getFirstAttribute(name);
|
if (isUpdated()) return updated.getFirstAttribute(name);
|
||||||
return cached.getAttributes().getFirst(name);
|
return cached.getAttributes(modelSupplier).getFirst(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
List<String> values = cached.getAttributes().get(name);
|
List<String> values = cached.getAttributes(modelSupplier).get(name);
|
||||||
if (values == null) return null;
|
if (values == null) return null;
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
return cached.getAttributes();
|
return cached.getAttributes(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -178,7 +182,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
if (isUpdated()) return updated.hasRole(role);
|
if (isUpdated()) return updated.hasRole(role);
|
||||||
if (cached.getRoleMappings().contains(role.getId())) return true;
|
if (cached.getRoleMappings(modelSupplier).contains(role.getId())) return true;
|
||||||
|
|
||||||
Set<RoleModel> mappings = getRoleMappings();
|
Set<RoleModel> mappings = getRoleMappings();
|
||||||
for (RoleModel mapping: mappings) {
|
for (RoleModel mapping: mappings) {
|
||||||
|
@ -197,7 +201,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
public Set<RoleModel> getRoleMappings() {
|
public Set<RoleModel> getRoleMappings() {
|
||||||
if (isUpdated()) return updated.getRoleMappings();
|
if (isUpdated()) return updated.getRoleMappings();
|
||||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
for (String id : cached.getRoleMappings()) {
|
for (String id : cached.getRoleMappings(modelSupplier)) {
|
||||||
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
||||||
if (roleById == null) {
|
if (roleById == null) {
|
||||||
// chance that role was removed, so just delegate to persistence and get user invalidated
|
// chance that role was removed, so just delegate to persistence and get user invalidated
|
||||||
|
@ -233,7 +237,7 @@ public class GroupAdapter implements GroupModel {
|
||||||
public Set<GroupModel> getSubGroups() {
|
public Set<GroupModel> getSubGroups() {
|
||||||
if (isUpdated()) return updated.getSubGroups();
|
if (isUpdated()) return updated.getSubGroups();
|
||||||
Set<GroupModel> subGroups = new HashSet<>();
|
Set<GroupModel> subGroups = new HashSet<>();
|
||||||
for (String id : cached.getSubGroups()) {
|
for (String id : cached.getSubGroups(modelSupplier)) {
|
||||||
GroupModel subGroup = keycloakSession.realms().getGroupById(id, realm);
|
GroupModel subGroup = keycloakSession.realms().getGroupById(id, realm);
|
||||||
if (subGroup == null) {
|
if (subGroup == null) {
|
||||||
// chance that role was removed, so just delegate to persistence and get user invalidated
|
// chance that role was removed, so just delegate to persistence and get user invalidated
|
||||||
|
@ -267,4 +271,8 @@ public class GroupAdapter implements GroupModel {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
updated.removeChild(subGroup);
|
updated.removeChild(subGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GroupModel getGroupModel() {
|
||||||
|
return cacheSession.getRealmDelegate().getGroupById(cached.getId(), realm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/LazyLoader.java
vendored
Normal file
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/LazyLoader.java
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.models.cache.infinispan;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A functional interface that can be used to return data {@code D} from a source {@code S} where implementations are free to define how and when
|
||||||
|
* data is fetched from source as well how it is internally cached.
|
||||||
|
*
|
||||||
|
* <p>The source does not need to worry about caching data but always fetch data as demanded. The way data will actually be cached is an implementation detail.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
* @see DefaultLazyLoader
|
||||||
|
*/
|
||||||
|
public interface LazyLoader<S, D> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns data from the given {@code source}. Data is only fetched from {@code source} once and only if necessary, it is
|
||||||
|
* up to implementations to decide the momentum to actually fetch data from source.
|
||||||
|
*
|
||||||
|
* @param source the source from where data will be fetched.
|
||||||
|
* @return the data from source
|
||||||
|
*/
|
||||||
|
D get(Supplier<S> source);
|
||||||
|
}
|
|
@ -35,30 +35,34 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class UserAdapter implements CachedUserModel {
|
public class UserAdapter implements CachedUserModel {
|
||||||
|
|
||||||
|
private final Supplier<UserModel> modelSupplier;
|
||||||
|
protected final CachedUser cached;
|
||||||
|
protected final UserCacheSession userProviderCache;
|
||||||
|
protected final KeycloakSession keycloakSession;
|
||||||
|
protected final RealmModel realm;
|
||||||
protected volatile UserModel updated;
|
protected volatile UserModel updated;
|
||||||
protected CachedUser cached;
|
|
||||||
protected UserCacheSession userProviderCache;
|
|
||||||
protected KeycloakSession keycloakSession;
|
|
||||||
protected RealmModel realm;
|
|
||||||
|
|
||||||
public UserAdapter(CachedUser cached, UserCacheSession userProvider, KeycloakSession keycloakSession, RealmModel realm) {
|
public UserAdapter(CachedUser cached, UserCacheSession userProvider, KeycloakSession keycloakSession, RealmModel realm) {
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
this.userProviderCache = userProvider;
|
this.userProviderCache = userProvider;
|
||||||
this.keycloakSession = keycloakSession;
|
this.keycloakSession = keycloakSession;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
this.modelSupplier = this::getUserModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getDelegateForUpdate() {
|
public UserModel getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
userProviderCache.registerUserInvalidation(realm, cached);
|
userProviderCache.registerUserInvalidation(realm, cached);
|
||||||
updated = userProviderCache.getDelegate().getUserById(getId(), realm);
|
updated = modelSupplier.get();
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -147,26 +151,26 @@ public class UserAdapter implements CachedUserModel {
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
if (updated != null) return updated.getFirstAttribute(name);
|
if (updated != null) return updated.getFirstAttribute(name);
|
||||||
return cached.getAttributes().getFirst(name);
|
return cached.getAttributes(modelSupplier).getFirst(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
if (updated != null) return updated.getAttribute(name);
|
if (updated != null) return updated.getAttribute(name);
|
||||||
List<String> result = cached.getAttributes().get(name);
|
List<String> result = cached.getAttributes(modelSupplier).get(name);
|
||||||
return (result == null) ? Collections.<String>emptyList() : result;
|
return (result == null) ? Collections.<String>emptyList() : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
if (updated != null) return updated.getAttributes();
|
if (updated != null) return updated.getAttributes();
|
||||||
return cached.getAttributes();
|
return cached.getAttributes(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getRequiredActions() {
|
public Set<String> getRequiredActions() {
|
||||||
if (updated != null) return updated.getRequiredActions();
|
if (updated != null) return updated.getRequiredActions();
|
||||||
return cached.getRequiredActions();
|
return cached.getRequiredActions(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -301,7 +305,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
if (updated != null) return updated.hasRole(role);
|
if (updated != null) return updated.hasRole(role);
|
||||||
if (cached.getRoleMappings().contains(role.getId())) return true;
|
if (cached.getRoleMappings(modelSupplier).contains(role.getId())) return true;
|
||||||
|
|
||||||
Set<RoleModel> mappings = getRoleMappings();
|
Set<RoleModel> mappings = getRoleMappings();
|
||||||
for (RoleModel mapping: mappings) {
|
for (RoleModel mapping: mappings) {
|
||||||
|
@ -320,7 +324,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
public Set<RoleModel> getRoleMappings() {
|
public Set<RoleModel> getRoleMappings() {
|
||||||
if (updated != null) return updated.getRoleMappings();
|
if (updated != null) return updated.getRoleMappings();
|
||||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
for (String id : cached.getRoleMappings()) {
|
for (String id : cached.getRoleMappings(modelSupplier)) {
|
||||||
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
||||||
if (roleById == null) {
|
if (roleById == null) {
|
||||||
// chance that role was removed, so just delete to persistence and get user invalidated
|
// chance that role was removed, so just delete to persistence and get user invalidated
|
||||||
|
@ -343,7 +347,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
public Set<GroupModel> getGroups() {
|
public Set<GroupModel> getGroups() {
|
||||||
if (updated != null) return updated.getGroups();
|
if (updated != null) return updated.getGroups();
|
||||||
Set<GroupModel> groups = new HashSet<GroupModel>();
|
Set<GroupModel> groups = new HashSet<GroupModel>();
|
||||||
for (String id : cached.getGroups()) {
|
for (String id : cached.getGroups(modelSupplier)) {
|
||||||
GroupModel groupModel = keycloakSession.realms().getGroupById(id, realm);
|
GroupModel groupModel = keycloakSession.realms().getGroupById(id, realm);
|
||||||
if (groupModel == null) {
|
if (groupModel == null) {
|
||||||
// chance that role was removed, so just delete to persistence and get user invalidated
|
// chance that role was removed, so just delete to persistence and get user invalidated
|
||||||
|
@ -372,7 +376,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
@Override
|
@Override
|
||||||
public boolean isMemberOf(GroupModel group) {
|
public boolean isMemberOf(GroupModel group) {
|
||||||
if (updated != null) return updated.isMemberOf(group);
|
if (updated != null) return updated.isMemberOf(group);
|
||||||
if (cached.getGroups().contains(group.getId())) return true;
|
if (cached.getGroups(modelSupplier).contains(group.getId())) return true;
|
||||||
Set<GroupModel> roles = getGroups();
|
Set<GroupModel> roles = getGroups();
|
||||||
return RoleUtils.isMember(roles, group);
|
return RoleUtils.isMember(roles, group);
|
||||||
}
|
}
|
||||||
|
@ -391,7 +395,7 @@ public class UserAdapter implements CachedUserModel {
|
||||||
return getId().hashCode();
|
return getId().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserModel getUserModel() {
|
||||||
|
return userProviderCache.getDelegate().getUserById(cached.getId(), realm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
|
import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
|
||||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||||
import org.keycloak.representations.idm.authorization.Logic;
|
import org.keycloak.representations.idm.authorization.Logic;
|
||||||
|
@ -30,27 +33,32 @@ import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
protected CachedPolicy cached;
|
|
||||||
protected StoreFactoryCacheSession cacheSession;
|
private final Supplier<Policy> modelSupplier;
|
||||||
|
protected final CachedPolicy cached;
|
||||||
|
protected final StoreFactoryCacheSession cacheSession;
|
||||||
protected Policy updated;
|
protected Policy updated;
|
||||||
|
|
||||||
public PolicyAdapter(CachedPolicy cached, StoreFactoryCacheSession cacheSession) {
|
public PolicyAdapter(CachedPolicy cached, StoreFactoryCacheSession cacheSession) {
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
this.cacheSession = cacheSession;
|
this.cacheSession = cacheSession;
|
||||||
|
this.modelSupplier = this::getPolicyModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy getDelegateForUpdate() {
|
public Policy getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
updated = cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
|
updated = modelSupplier.get();
|
||||||
String defaultResourceType = updated.getConfig().get("defaultResourceType");
|
String defaultResourceType = updated.getConfig().get("defaultResourceType");
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), defaultResourceType, cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), defaultResourceType, cached.getResourceServerId());
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -98,7 +106,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), name, cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.setName(name);
|
updated.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,15 +150,15 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getConfig() {
|
public Map<String, String> getConfig() {
|
||||||
if (isUpdated()) return updated.getConfig();
|
if (isUpdated()) return updated.getConfig();
|
||||||
return cached.getConfig();
|
return cached.getConfig(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfig(Map<String, String> config) {
|
public void setConfig(Map<String, String> config) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
if (config.containsKey("defaultResourceType") || cached.getConfig().containsKey("defaultResourceType")) {
|
if (config.containsKey("defaultResourceType") || cached.getConfig(modelSupplier).containsKey("defaultResourceType")) {
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), config.get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), config.get("defaultResourceType"), cached.getResourceServerId());
|
||||||
}
|
}
|
||||||
updated.setConfig(config);
|
updated.setConfig(config);
|
||||||
|
|
||||||
|
@ -160,7 +168,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
public void removeConfig(String name) {
|
public void removeConfig(String name) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
if (name.equals("defaultResourceType")) {
|
if (name.equals("defaultResourceType")) {
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
}
|
}
|
||||||
updated.removeConfig(name);
|
updated.removeConfig(name);
|
||||||
|
|
||||||
|
@ -170,8 +178,8 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
public void putConfig(String name, String value) {
|
public void putConfig(String name, String value) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
if (name.equals("defaultResourceType")) {
|
if (name.equals("defaultResourceType")) {
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), value, cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), value, cached.getResourceServerId());
|
||||||
}
|
}
|
||||||
updated.putConfig(name, value);
|
updated.putConfig(name, value);
|
||||||
}
|
}
|
||||||
|
@ -192,40 +200,49 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Policy> getAssociatedPolicies() {
|
public Set<Policy> getAssociatedPolicies() {
|
||||||
if (isUpdated()) return updated.getAssociatedPolicies();
|
if (isUpdated()) {
|
||||||
|
return updated.getAssociatedPolicies().stream().map(policy -> new PolicyAdapter(cacheSession.createCachedPolicy(policy, policy.getId()), cacheSession)).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
if (associatedPolicies != null) return associatedPolicies;
|
if (associatedPolicies != null) return associatedPolicies;
|
||||||
associatedPolicies = new HashSet<>();
|
associatedPolicies = new HashSet<>();
|
||||||
for (String scopeId : cached.getAssociatedPoliciesIds()) {
|
PolicyStore policyStore = cacheSession.getPolicyStore();
|
||||||
associatedPolicies.add(cacheSession.getPolicyStore().findById(scopeId, cached.getResourceServerId()));
|
String resourceServerId = cached.getResourceServerId();
|
||||||
|
for (String id : cached.getAssociatedPoliciesIds(modelSupplier)) {
|
||||||
|
Policy policy = policyStore.findById(id, resourceServerId);
|
||||||
|
cacheSession.cachePolicy(policy);
|
||||||
|
associatedPolicies.add(policy);
|
||||||
}
|
}
|
||||||
associatedPolicies = Collections.unmodifiableSet(associatedPolicies);
|
return associatedPolicies = Collections.unmodifiableSet(associatedPolicies);
|
||||||
return associatedPolicies;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<Resource> resources;
|
protected Set<Resource> resources;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Resource> getResources() {
|
public Set<Resource> getResources() {
|
||||||
if (isUpdated()) return updated.getResources();
|
if (isUpdated()) return updated.getResources();
|
||||||
if (resources != null) return resources;
|
if (resources != null) return resources;
|
||||||
resources = new HashSet<>();
|
resources = new HashSet<>();
|
||||||
for (String resourceId : cached.getResourcesIds()) {
|
ResourceStore resourceStore = cacheSession.getResourceStore();
|
||||||
resources.add(cacheSession.getResourceStore().findById(resourceId, cached.getResourceServerId()));
|
for (String resourceId : cached.getResourcesIds(modelSupplier)) {
|
||||||
|
String resourceServerId = cached.getResourceServerId();
|
||||||
|
Resource resource = resourceStore.findById(resourceId, resourceServerId);
|
||||||
|
cacheSession.cacheResource(resource);
|
||||||
|
resources.add(resource);
|
||||||
}
|
}
|
||||||
resources = Collections.unmodifiableSet(resources);
|
return resources = Collections.unmodifiableSet(resources);
|
||||||
return resources;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addScope(Scope scope) {
|
public void addScope(Scope scope) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.addScope(scope);
|
updated.addScope(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeScope(Scope scope) {
|
public void removeScope(Scope scope) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), new HashSet<>(Arrays.asList(scope.getId())), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.removeScope(scope);
|
updated.removeScope(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +265,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
HashSet<String> resources = new HashSet<>();
|
HashSet<String> resources = new HashSet<>();
|
||||||
resources.add(resource.getId());
|
resources.add(resource.getId());
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.addResource(resource);
|
updated.addResource(resource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -258,11 +275,16 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
HashSet<String> resources = new HashSet<>();
|
HashSet<String> resources = new HashSet<>();
|
||||||
resources.add(resource.getId());
|
resources.add(resource.getId());
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), resources, cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.removeResource(resource);
|
updated.removeResource(resource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFetched(String association) {
|
||||||
|
return modelSupplier.get().isFetched(association);
|
||||||
|
}
|
||||||
|
|
||||||
protected Set<Scope> scopes;
|
protected Set<Scope> scopes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -270,11 +292,14 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
if (isUpdated()) return updated.getScopes();
|
if (isUpdated()) return updated.getScopes();
|
||||||
if (scopes != null) return scopes;
|
if (scopes != null) return scopes;
|
||||||
scopes = new HashSet<>();
|
scopes = new HashSet<>();
|
||||||
for (String scopeId : cached.getScopesIds()) {
|
ScopeStore scopeStore = cacheSession.getScopeStore();
|
||||||
scopes.add(cacheSession.getScopeStore().findById(scopeId, cached.getResourceServerId()));
|
String resourceServerId = cached.getResourceServerId();
|
||||||
|
for (String scopeId : cached.getScopesIds(modelSupplier)) {
|
||||||
|
Scope scope = scopeStore.findById(scopeId, resourceServerId);
|
||||||
|
cacheSession.cacheScope(scope);
|
||||||
|
scopes.add(scope);
|
||||||
}
|
}
|
||||||
scopes = Collections.unmodifiableSet(scopes);
|
return scopes = Collections.unmodifiableSet(scopes);
|
||||||
return scopes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -286,7 +311,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
@Override
|
@Override
|
||||||
public void setOwner(String owner) {
|
public void setOwner(String owner) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), cached.getScopesIds(), cached.getConfig().get("defaultResourceType"), cached.getResourceServerId());
|
cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(modelSupplier), cached.getScopesIds(modelSupplier), cached.getConfig(modelSupplier).get("defaultResourceType"), cached.getResourceServerId());
|
||||||
updated.setOwner(owner);
|
updated.setOwner(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +329,7 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
return getId().hashCode();
|
return getId().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Policy getPolicyModel() {
|
||||||
|
return cacheSession.getPolicyStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.keycloak.models.cache.infinispan.authorization;
|
||||||
|
|
||||||
import org.keycloak.authorization.model.CachedModel;
|
import org.keycloak.authorization.model.CachedModel;
|
||||||
import org.keycloak.authorization.model.PermissionTicket;
|
import org.keycloak.authorization.model.PermissionTicket;
|
||||||
import org.keycloak.authorization.model.Policy;
|
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
@ -31,7 +30,7 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,20 +39,22 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
|
|
||||||
protected CachedResource cached;
|
private final Supplier<Resource> modelSupplier;
|
||||||
protected StoreFactoryCacheSession cacheSession;
|
protected final CachedResource cached;
|
||||||
|
protected final StoreFactoryCacheSession cacheSession;
|
||||||
protected Resource updated;
|
protected Resource updated;
|
||||||
|
|
||||||
public ResourceAdapter(CachedResource cached, StoreFactoryCacheSession cacheSession) {
|
public ResourceAdapter(CachedResource cached, StoreFactoryCacheSession cacheSession) {
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
this.cacheSession = cacheSession;
|
this.cacheSession = cacheSession;
|
||||||
|
this.modelSupplier = this::getResourceModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource getDelegateForUpdate() {
|
public Resource getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
updated = modelSupplier.get();
|
||||||
updated = cacheSession.getResourceStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
|
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(modelSupplier), cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
|
@ -101,7 +102,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
@Override
|
@Override
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUris(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUris(modelSupplier), cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.setName(name);
|
updated.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
@Override
|
@Override
|
||||||
public void setDisplayName(String name) {
|
public void setDisplayName(String name) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUris(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUris(modelSupplier), cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.setDisplayName(name);
|
updated.setDisplayName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,13 +140,13 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getUris() {
|
public Set<String> getUris() {
|
||||||
if (isUpdated()) return updated.getUris();
|
if (isUpdated()) return updated.getUris();
|
||||||
return cached.getUris();
|
return cached.getUris(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUris(Set<String> uris) {
|
public void updateUris(Set<String> uris) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), uris, cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), uris, cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.updateUris(uris);
|
updated.updateUris(uris);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
@Override
|
@Override
|
||||||
public void setType(String type) {
|
public void setType(String type) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), type, cached.getUris(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), type, cached.getUris(modelSupplier), cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.setType(type);
|
updated.setType(type);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -170,11 +171,10 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
if (isUpdated()) return updated.getScopes();
|
if (isUpdated()) return updated.getScopes();
|
||||||
if (scopes != null) return scopes;
|
if (scopes != null) return scopes;
|
||||||
scopes = new LinkedList<>();
|
scopes = new LinkedList<>();
|
||||||
for (String scopeId : cached.getScopesIds()) {
|
for (String scopeId : cached.getScopesIds(modelSupplier)) {
|
||||||
scopes.add(cacheSession.getScopeStore().findById(scopeId, cached.getResourceServerId()));
|
scopes.add(cacheSession.getScopeStore().findById(scopeId, cached.getResourceServerId()));
|
||||||
}
|
}
|
||||||
scopes = Collections.unmodifiableList(scopes);
|
return scopes = Collections.unmodifiableList(scopes);
|
||||||
return scopes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -192,7 +192,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
@Override
|
@Override
|
||||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(modelSupplier), cached.getScopesIds(modelSupplier), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.setOwnerManagedAccess(ownerManagedAccess);
|
updated.setOwnerManagedAccess(ownerManagedAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,21 +219,21 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(), scopes.stream().map(scope1 -> scope1.getId()).collect(Collectors.toSet()), cached.getResourceServerId(), cached.getOwner());
|
cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUris(modelSupplier), scopes.stream().map(scope1 -> scope1.getId()).collect(Collectors.toSet()), cached.getResourceServerId(), cached.getOwner());
|
||||||
updated.updateScopes(scopes);
|
updated.updateScopes(scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
if (updated != null) return updated.getAttributes();
|
if (updated != null) return updated.getAttributes();
|
||||||
return cached.getAttributes();
|
return cached.getAttributes(modelSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSingleAttribute(String name) {
|
public String getSingleAttribute(String name) {
|
||||||
if (updated != null) return updated.getSingleAttribute(name);
|
if (updated != null) return updated.getSingleAttribute(name);
|
||||||
|
|
||||||
List<String> values = cached.getAttributes().getOrDefault(name, Collections.emptyList());
|
List<String> values = cached.getAttributes(modelSupplier).getOrDefault(name, Collections.emptyList());
|
||||||
|
|
||||||
if (values.isEmpty()) {
|
if (values.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -246,7 +246,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
if (updated != null) return updated.getAttribute(name);
|
if (updated != null) return updated.getAttribute(name);
|
||||||
|
|
||||||
List<String> values = cached.getAttributes().getOrDefault(name, Collections.emptyList());
|
List<String> values = cached.getAttributes(modelSupplier).getOrDefault(name, Collections.emptyList());
|
||||||
|
|
||||||
if (values.isEmpty()) {
|
if (values.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -267,6 +267,11 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
updated.removeAttribute(name);
|
updated.removeAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFetched(String association) {
|
||||||
|
return modelSupplier.get().isFetched(association);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -281,4 +286,7 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
||||||
return getId().hashCode();
|
return getId().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Resource getResourceModel() {
|
||||||
|
return cacheSession.getResourceStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,7 +407,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean modelMightExist(String id) {
|
boolean modelMightExist(String id) {
|
||||||
return invalidations.contains(id) || cache.get(id, NonExistentItem.class) == null;
|
return invalidations.contains(id) || cache.get(id, NonExistentItem.class) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,11 +712,11 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId, consumer);
|
(revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <R, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId) {
|
private <R extends Resource, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId) {
|
||||||
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServerId, null);
|
return cacheQuery(cacheKey, queryType, resultSupplier, querySupplier, resourceServerId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <R, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId, Consumer<R> consumer) {
|
private <R extends Resource, Q extends ResourceQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId, Consumer<R> consumer) {
|
||||||
Q query = cache.get(cacheKey, queryType);
|
Q query = cache.get(cacheKey, queryType);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
logger.tracev("cache hit for key: {0}", cacheKey);
|
logger.tracev("cache hit for key: {0}", cacheKey);
|
||||||
|
@ -730,7 +730,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
cache.addRevisioned(query, startupRevision);
|
cache.addRevisioned(query, startupRevision);
|
||||||
if (consumer != null) {
|
if (consumer != null) {
|
||||||
for (R resource : model) {
|
for (R resource : model) {
|
||||||
consumer.accept(resource);
|
consumer.andThen(r -> cacheResource(resource)).accept(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
|
@ -799,9 +799,9 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
logger.tracev("by id cache hit: {0}", cached.getId());
|
logger.tracev("by id cache hit: {0}", cached.getId());
|
||||||
}
|
}
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
Long loaded = cache.getCurrentRevision(id);
|
|
||||||
if (! modelMightExist(id)) return null;
|
if (! modelMightExist(id)) return null;
|
||||||
Policy model = getPolicyStoreDelegate().findById(id, resourceServerId);
|
Policy model = getPolicyStoreDelegate().findById(id, resourceServerId);
|
||||||
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
setModelDoesNotExists(id, loaded);
|
setModelDoesNotExists(id, loaded);
|
||||||
return null;
|
return null;
|
||||||
|
@ -922,7 +922,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
return getPolicyStoreDelegate().findDependentPolicies(id, resourceServerId);
|
return getPolicyStoreDelegate().findDependentPolicies(id, resourceServerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <R, Q extends PolicyQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId, Consumer<R> consumer) {
|
private <R extends Policy, Q extends PolicyQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId, Consumer<R> consumer) {
|
||||||
Q query = cache.get(cacheKey, queryType);
|
Q query = cache.get(cacheKey, queryType);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
logger.tracev("cache hit for key: {0}", cacheKey);
|
logger.tracev("cache hit for key: {0}", cacheKey);
|
||||||
|
@ -936,7 +936,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
cache.addRevisioned(query, startupRevision);
|
cache.addRevisioned(query, startupRevision);
|
||||||
if (consumer != null) {
|
if (consumer != null) {
|
||||||
for (R policy: model) {
|
for (R policy: model) {
|
||||||
consumer.accept(policy);
|
consumer.andThen(r -> cachePolicy(policy)).accept(policy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
|
@ -1080,4 +1080,46 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cachePolicy(Policy model) {
|
||||||
|
String id = model.getId();
|
||||||
|
if (cache.getCache().containsKey(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!modelMightExist(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (invalidations.contains(id)) return;
|
||||||
|
cache.addRevisioned(createCachedPolicy(model, id), startupRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedPolicy createCachedPolicy(Policy model, String id) {
|
||||||
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
|
return new CachedPolicy(loaded, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cacheResource(Resource model) {
|
||||||
|
String id = model.getId();
|
||||||
|
if (cache.getCache().containsKey(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
|
if (!modelMightExist(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (invalidations.contains(id)) return;
|
||||||
|
cache.addRevisioned(new CachedResource(loaded, model), startupRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cacheScope(Scope model) {
|
||||||
|
String id = model.getId();
|
||||||
|
if (cache.getCache().containsKey(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
|
if (!modelMightExist(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (invalidations.contains(id)) return;
|
||||||
|
cache.addRevisioned(new CachedScope(loaded, model), startupRevision);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,17 @@ package org.keycloak.models.cache.infinispan.authorization.entities;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||||
|
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||||
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
||||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||||
import org.keycloak.representations.idm.authorization.Logic;
|
import org.keycloak.representations.idm.authorization.Logic;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,16 +39,16 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public class CachedPolicy extends AbstractRevisioned implements InResourceServer {
|
public class CachedPolicy extends AbstractRevisioned implements InResourceServer {
|
||||||
|
|
||||||
private String type;
|
private final String type;
|
||||||
private DecisionStrategy decisionStrategy;
|
private final DecisionStrategy decisionStrategy;
|
||||||
private Logic logic;
|
private final Logic logic;
|
||||||
private Map<String, String> config;
|
private final String name;
|
||||||
private String name;
|
private final String description;
|
||||||
private String description;
|
private final String resourceServerId;
|
||||||
private String resourceServerId;
|
private final LazyLoader<Policy, Set<String>> associatedPoliciesIds;
|
||||||
private Set<String> associatedPoliciesIds;
|
private final LazyLoader<Policy, Set<String>> resourcesIds;
|
||||||
private Set<String> resourcesIds;
|
private final LazyLoader<Policy, Set<String>> scopesIds;
|
||||||
private Set<String> scopesIds;
|
private final LazyLoader<Policy, Map<String, String>> config;
|
||||||
private final String owner;
|
private final String owner;
|
||||||
|
|
||||||
public CachedPolicy(Long revision, Policy policy) {
|
public CachedPolicy(Long revision, Policy policy) {
|
||||||
|
@ -52,13 +56,38 @@ public class CachedPolicy extends AbstractRevisioned implements InResourceServer
|
||||||
this.type = policy.getType();
|
this.type = policy.getType();
|
||||||
this.decisionStrategy = policy.getDecisionStrategy();
|
this.decisionStrategy = policy.getDecisionStrategy();
|
||||||
this.logic = policy.getLogic();
|
this.logic = policy.getLogic();
|
||||||
this.config = new HashMap(policy.getConfig());
|
|
||||||
this.name = policy.getName();
|
this.name = policy.getName();
|
||||||
this.description = policy.getDescription();
|
this.description = policy.getDescription();
|
||||||
this.resourceServerId = policy.getResourceServer().getId();
|
this.resourceServerId = policy.getResourceServer().getId();
|
||||||
this.associatedPoliciesIds = policy.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toSet());
|
|
||||||
this.resourcesIds = policy.getResources().stream().map(Resource::getId).collect(Collectors.toSet());
|
if (policy.isFetched("associatedPolicies")) {
|
||||||
this.scopesIds = policy.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
|
Set<String> data = policy.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toSet());
|
||||||
|
this.associatedPoliciesIds = source -> data;
|
||||||
|
} else {
|
||||||
|
this.associatedPoliciesIds = new DefaultLazyLoader<>(source -> source.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.isFetched("resources")) {
|
||||||
|
Set<String> data = policy.getResources().stream().map(Resource::getId).collect(Collectors.toSet());
|
||||||
|
this.resourcesIds = source -> data;
|
||||||
|
} else {
|
||||||
|
this.resourcesIds = new DefaultLazyLoader<>(source -> source.getResources().stream().map(Resource::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.isFetched("scopes")) {
|
||||||
|
Set<String> data = policy.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
|
||||||
|
this.scopesIds = source -> data;
|
||||||
|
} else {
|
||||||
|
this.scopesIds = new DefaultLazyLoader<>(source -> source.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.isFetched("config")) {
|
||||||
|
Map<String, String> data = new HashMap<>(policy.getConfig());
|
||||||
|
this.config = source -> data;
|
||||||
|
} else {
|
||||||
|
this.config = new DefaultLazyLoader<>(source -> new HashMap<>(source.getConfig()), Collections::emptyMap);
|
||||||
|
}
|
||||||
|
|
||||||
this.owner = policy.getOwner();
|
this.owner = policy.getOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +103,8 @@ public class CachedPolicy extends AbstractRevisioned implements InResourceServer
|
||||||
return this.logic;
|
return this.logic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getConfig() {
|
public Map<String, String> getConfig(Supplier<Policy> policy) {
|
||||||
return this.config;
|
return this.config.get(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -86,16 +115,16 @@ public class CachedPolicy extends AbstractRevisioned implements InResourceServer
|
||||||
return this.description;
|
return this.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAssociatedPoliciesIds() {
|
public Set<String> getAssociatedPoliciesIds(Supplier<Policy> policy) {
|
||||||
return this.associatedPoliciesIds;
|
return this.associatedPoliciesIds.get(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getResourcesIds() {
|
public Set<String> getResourcesIds(Supplier<Policy> policy) {
|
||||||
return this.resourcesIds;
|
return this.resourcesIds.get(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getScopesIds() {
|
public Set<String> getScopesIds(Supplier<Policy> policy) {
|
||||||
return this.scopesIds;
|
return this.scopesIds.get(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResourceServerId() {
|
public String getResourceServerId() {
|
||||||
|
|
|
@ -21,11 +21,16 @@ package org.keycloak.models.cache.infinispan.authorization.entities;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||||
|
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||||
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,29 +38,47 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public class CachedResource extends AbstractRevisioned implements InResourceServer {
|
public class CachedResource extends AbstractRevisioned implements InResourceServer {
|
||||||
|
|
||||||
private String resourceServerId;
|
private final String resourceServerId;
|
||||||
private String iconUri;
|
private final String iconUri;
|
||||||
private String owner;
|
private final String owner;
|
||||||
private String type;
|
private final String type;
|
||||||
private String name;
|
private final String name;
|
||||||
private String displayName;
|
private final String displayName;
|
||||||
private Set<String> uris;
|
private final boolean ownerManagedAccess;
|
||||||
private Set<String> scopesIds;
|
private LazyLoader<Resource, Set<String>> scopesIds;
|
||||||
private boolean ownerManagedAccess;
|
private LazyLoader<Resource, Set<String>> uris;
|
||||||
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
private LazyLoader<Resource, MultivaluedHashMap<String, String>> attributes;
|
||||||
|
|
||||||
public CachedResource(Long revision, Resource resource) {
|
public CachedResource(Long revision, Resource resource) {
|
||||||
super(revision, resource.getId());
|
super(revision, resource.getId());
|
||||||
this.name = resource.getName();
|
this.name = resource.getName();
|
||||||
this.displayName = resource.getDisplayName();
|
this.displayName = resource.getDisplayName();
|
||||||
this.uris = resource.getUris();
|
|
||||||
this.type = resource.getType();
|
this.type = resource.getType();
|
||||||
this.owner = resource.getOwner();
|
this.owner = resource.getOwner();
|
||||||
this.iconUri = resource.getIconUri();
|
this.iconUri = resource.getIconUri();
|
||||||
this.resourceServerId = resource.getResourceServer().getId();
|
this.resourceServerId = resource.getResourceServer().getId();
|
||||||
this.scopesIds = resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
|
|
||||||
ownerManagedAccess = resource.isOwnerManagedAccess();
|
ownerManagedAccess = resource.isOwnerManagedAccess();
|
||||||
this.attributes.putAll(resource.getAttributes());
|
|
||||||
|
if (resource.isFetched("uris")) {
|
||||||
|
Set<String> data = new HashSet<>(resource.getUris());
|
||||||
|
this.uris = source -> data;
|
||||||
|
} else {
|
||||||
|
this.uris = new DefaultLazyLoader<>(source -> new HashSet<>(source.getUris()), Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.isFetched("scopes")) {
|
||||||
|
Set<String> data = resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
|
||||||
|
this.scopesIds = source -> data;
|
||||||
|
} else {
|
||||||
|
this.scopesIds = new DefaultLazyLoader<>(source -> source.getScopes().stream().map(Scope::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.isFetched("attributes")) {
|
||||||
|
MultivaluedHashMap<String, String> data = new MultivaluedHashMap<>(resource.getAttributes());
|
||||||
|
this.attributes = source -> data;
|
||||||
|
} else {
|
||||||
|
this.attributes = new DefaultLazyLoader<>(source -> new MultivaluedHashMap<>(source.getAttributes()), MultivaluedHashMap::new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,8 +90,8 @@ public class CachedResource extends AbstractRevisioned implements InResourceServ
|
||||||
return this.displayName;
|
return this.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getUris() {
|
public Set<String> getUris(Supplier<Resource> source) {
|
||||||
return this.uris;
|
return this.uris.get(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
|
@ -91,11 +114,11 @@ public class CachedResource extends AbstractRevisioned implements InResourceServ
|
||||||
return this.resourceServerId;
|
return this.resourceServerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getScopesIds() {
|
public Set<String> getScopesIds(Supplier<Resource> source) {
|
||||||
return this.scopesIds;
|
return this.scopesIds.get(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes(Supplier<Resource> source) {
|
||||||
return attributes;
|
return attributes.get(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,50 +21,51 @@ import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||||
|
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class CachedGroup extends AbstractRevisioned implements InRealm {
|
public class CachedGroup extends AbstractRevisioned implements InRealm {
|
||||||
private String realm;
|
|
||||||
private String name;
|
private final String realm;
|
||||||
private String parentId;
|
private final String name;
|
||||||
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
private final String parentId;
|
||||||
private Set<String> roleMappings = new HashSet<>();
|
private final LazyLoader<GroupModel, MultivaluedHashMap<String, String>> attributes;
|
||||||
private Set<String> subGroups = new HashSet<>();
|
private final LazyLoader<GroupModel, Set<String>> roleMappings;
|
||||||
|
private final LazyLoader<GroupModel, Set<String>> subGroups;
|
||||||
|
|
||||||
public CachedGroup(Long revision, RealmModel realm, GroupModel group) {
|
public CachedGroup(Long revision, RealmModel realm, GroupModel group) {
|
||||||
super(revision, group.getId());
|
super(revision, group.getId());
|
||||||
this.realm = realm.getId();
|
this.realm = realm.getId();
|
||||||
this.name = group.getName();
|
this.name = group.getName();
|
||||||
this.parentId = group.getParentId();
|
this.parentId = group.getParentId();
|
||||||
|
this.attributes = new DefaultLazyLoader<>(source -> new MultivaluedHashMap<>(source.getAttributes()), MultivaluedHashMap::new);
|
||||||
this.attributes.putAll(group.getAttributes());
|
this.roleMappings = new DefaultLazyLoader<>(source -> source.getRoleMappings().stream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
for (RoleModel role : group.getRoleMappings()) {
|
this.subGroups = new DefaultLazyLoader<>(source -> source.getSubGroups().stream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
roleMappings.add(role.getId());
|
|
||||||
}
|
|
||||||
Set<GroupModel> subGroups1 = group.getSubGroups();
|
|
||||||
if (subGroups1 != null) {
|
|
||||||
for (GroupModel subGroup : subGroups1) {
|
|
||||||
subGroups.add(subGroup.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRealm() {
|
public String getRealm() {
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultivaluedHashMap<String, String> getAttributes() {
|
public MultivaluedHashMap<String, String> getAttributes(Supplier<GroupModel> group) {
|
||||||
return attributes;
|
return attributes.get(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getRoleMappings() {
|
public Set<String> getRoleMappings(Supplier<GroupModel> group) {
|
||||||
return roleMappings;
|
// it may happen that groups were not loaded before so we don't actually need to invalidate entries in the cache
|
||||||
|
if (group == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
return roleMappings.get(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -75,7 +76,7 @@ public class CachedGroup extends AbstractRevisioned implements InRealm {
|
||||||
return parentId;
|
return parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getSubGroups() {
|
public Set<String> getSubGroups(Supplier<GroupModel> group) {
|
||||||
return subGroups;
|
return subGroups.get(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,32 +22,35 @@ import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||||
|
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class CachedUser extends AbstractExtendableRevisioned implements InRealm {
|
public class CachedUser extends AbstractExtendableRevisioned implements InRealm {
|
||||||
private String realm;
|
|
||||||
private String username;
|
|
||||||
private Long createdTimestamp;
|
|
||||||
private String firstName;
|
|
||||||
private String lastName;
|
|
||||||
private String email;
|
|
||||||
private boolean emailVerified;
|
|
||||||
private boolean enabled;
|
|
||||||
private String federationLink;
|
|
||||||
private String serviceAccountClientLink;
|
|
||||||
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
|
||||||
private Set<String> requiredActions = new HashSet<>();
|
|
||||||
private Set<String> roleMappings = new HashSet<>();
|
|
||||||
private Set<String> groups = new HashSet<>();
|
|
||||||
private int notBefore;
|
|
||||||
|
|
||||||
|
|
||||||
|
private final String realm;
|
||||||
|
private final String username;
|
||||||
|
private final Long createdTimestamp;
|
||||||
|
private final String firstName;
|
||||||
|
private final String lastName;
|
||||||
|
private final String email;
|
||||||
|
private final boolean emailVerified;
|
||||||
|
private final boolean enabled;
|
||||||
|
private final String federationLink;
|
||||||
|
private final String serviceAccountClientLink;
|
||||||
|
private final int notBefore;
|
||||||
|
private final LazyLoader<UserModel, Set<String>> requiredActions;
|
||||||
|
private final LazyLoader<UserModel, MultivaluedHashMap<String, String>> attributes;
|
||||||
|
private final LazyLoader<UserModel, Set<String>> roleMappings;
|
||||||
|
private final LazyLoader<UserModel, Set<String>> groups;
|
||||||
|
|
||||||
public CachedUser(Long revision, RealmModel realm, UserModel user, int notBefore) {
|
public CachedUser(Long revision, RealmModel realm, UserModel user, int notBefore) {
|
||||||
super(revision, user.getId());
|
super(revision, user.getId());
|
||||||
|
@ -56,23 +59,16 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
this.createdTimestamp = user.getCreatedTimestamp();
|
this.createdTimestamp = user.getCreatedTimestamp();
|
||||||
this.firstName = user.getFirstName();
|
this.firstName = user.getFirstName();
|
||||||
this.lastName = user.getLastName();
|
this.lastName = user.getLastName();
|
||||||
this.attributes.putAll(user.getAttributes());
|
|
||||||
this.email = user.getEmail();
|
this.email = user.getEmail();
|
||||||
this.emailVerified = user.isEmailVerified();
|
this.emailVerified = user.isEmailVerified();
|
||||||
this.enabled = user.isEnabled();
|
this.enabled = user.isEnabled();
|
||||||
this.federationLink = user.getFederationLink();
|
this.federationLink = user.getFederationLink();
|
||||||
this.serviceAccountClientLink = user.getServiceAccountClientLink();
|
this.serviceAccountClientLink = user.getServiceAccountClientLink();
|
||||||
this.requiredActions.addAll(user.getRequiredActions());
|
|
||||||
for (RoleModel role : user.getRoleMappings()) {
|
|
||||||
roleMappings.add(role.getId());
|
|
||||||
}
|
|
||||||
Set<GroupModel> groupMappings = user.getGroups();
|
|
||||||
if (groupMappings != null) {
|
|
||||||
for (GroupModel group : groupMappings) {
|
|
||||||
groups.add(group.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.notBefore = notBefore;
|
this.notBefore = notBefore;
|
||||||
|
this.requiredActions = new DefaultLazyLoader<>(UserModel::getRequiredActions, Collections::emptySet);
|
||||||
|
this.attributes = new DefaultLazyLoader<>(userModel -> new MultivaluedHashMap<>(userModel.getAttributes()), MultivaluedHashMap::new);
|
||||||
|
this.roleMappings = new DefaultLazyLoader<>(userModel -> userModel.getRoleMappings().stream().map(RoleModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
|
this.groups = new DefaultLazyLoader<>(userModel -> userModel.getGroups().stream().map(GroupModel::getId).collect(Collectors.toSet()), Collections::emptySet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRealm() {
|
public String getRealm() {
|
||||||
|
@ -107,16 +103,16 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultivaluedHashMap<String, String> getAttributes() {
|
public MultivaluedHashMap<String, String> getAttributes(Supplier<UserModel> userModel) {
|
||||||
return attributes;
|
return attributes.get(userModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getRequiredActions() {
|
public Set<String> getRequiredActions(Supplier<UserModel> userModel) {
|
||||||
return requiredActions;
|
return this.requiredActions.get(userModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getRoleMappings() {
|
public Set<String> getRoleMappings(Supplier<UserModel> userModel) {
|
||||||
return roleMappings;
|
return roleMappings.get(userModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFederationLink() {
|
public String getFederationLink() {
|
||||||
|
@ -127,8 +123,8 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
return serviceAccountClientLink;
|
return serviceAccountClientLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getGroups() {
|
public Set<String> getGroups(Supplier<UserModel> userModel) {
|
||||||
return groups;
|
return groups.get(userModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNotBefore() {
|
public int getNotBefore() {
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class HasRolePredicate implements Predicate<Map.Entry<String, Revisioned>
|
||||||
}
|
}
|
||||||
if (value instanceof CachedGroup) {
|
if (value instanceof CachedGroup) {
|
||||||
CachedGroup cachedRole = (CachedGroup)value;
|
CachedGroup cachedRole = (CachedGroup)value;
|
||||||
if (cachedRole.getRoleMappings().contains(role)) return true;
|
if (cachedRole.getRoleMappings(null).contains(role)) return true;
|
||||||
}
|
}
|
||||||
if (value instanceof RoleQuery) {
|
if (value instanceof RoleQuery) {
|
||||||
RoleQuery roleQuery = (RoleQuery)value;
|
RoleQuery roleQuery = (RoleQuery)value;
|
||||||
|
|
|
@ -55,13 +55,13 @@ import org.keycloak.representations.idm.authorization.Logic;
|
||||||
@NamedQueries(
|
@NamedQueries(
|
||||||
{
|
{
|
||||||
@NamedQuery(name="findPolicyIdByServerId", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId "),
|
@NamedQuery(name="findPolicyIdByServerId", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId "),
|
||||||
@NamedQuery(name="findPolicyIdByName", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.name = :name"),
|
@NamedQuery(name="findPolicyIdByName", query="select p from PolicyEntity p left join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and p.name = :name"),
|
||||||
@NamedQuery(name="findPolicyIdByResource", query="select p.id from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
|
@NamedQuery(name="findPolicyIdByResource", query="select p from PolicyEntity p inner join fetch p.resources r left join fetch p.scopes s inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
|
||||||
@NamedQuery(name="findPolicyIdByScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))"),
|
@NamedQuery(name="findPolicyIdByScope", query="select pe from PolicyEntity pe left join fetch pe.resources r inner join fetch pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id))"),
|
||||||
@NamedQuery(name="findPolicyIdByResourceScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds))) and pe.id IN (select p.id from ResourceEntity r inner join r.policies p where r.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and r.id in (:resourceId))))"),
|
@NamedQuery(name="findPolicyIdByResourceScope", query="select pe from PolicyEntity pe inner join fetch pe.resources r inner join fetch pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds) and p.id = pe.id)) and exists (select p.id from ResourceEntity r inner join r.policies p where r.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and r.id in (:resourceId))))"),
|
||||||
@NamedQuery(name="findPolicyIdByNullResourceScope", query="select pe.id from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds))) and pe.resources is empty"),
|
@NamedQuery(name="findPolicyIdByNullResourceScope", query="select pe from PolicyEntity pe left join fetch pe.resources r inner join fetch pe.scopes s inner join fetch pe.associatedPolicies a where pe.resourceServer.id = :serverId and exists (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.id = pe.id and p.type = 'scope' and s.id in (:scopeIds))) and pe.resources is empty"),
|
||||||
@NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"),
|
@NamedQuery(name="findPolicyIdByType", query="select p.id from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type"),
|
||||||
@NamedQuery(name="findPolicyIdByResourceType", query="select p.id from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"),
|
@NamedQuery(name="findPolicyIdByResourceType", query="select p from PolicyEntity p inner join p.config c inner join fetch p.associatedPolicies a where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type"),
|
||||||
@NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"),
|
@NamedQuery(name="findPolicyIdByDependentPolices", query="select p.id from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)"),
|
||||||
@NamedQuery(name="deletePolicyByResourceServer", query="delete from PolicyEntity p where p.resourceServer.id = :serverId")
|
@NamedQuery(name="deletePolicyByResourceServer", query="delete from PolicyEntity p where p.resourceServer.id = :serverId")
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,13 +57,13 @@ import org.hibernate.annotations.FetchMode;
|
||||||
})
|
})
|
||||||
@NamedQueries(
|
@NamedQueries(
|
||||||
{
|
{
|
||||||
@NamedQuery(name="findResourceIdByOwner", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :owner"),
|
@NamedQuery(name="findResourceIdByOwner", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :owner"),
|
||||||
@NamedQuery(name="findAnyResourceIdByOwner", query="select r.id from ResourceEntity r where r.owner = :owner"),
|
@NamedQuery(name="findAnyResourceIdByOwner", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.owner = :owner"),
|
||||||
@NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and :uri in elements(r.uris)"),
|
@NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and :uri in elements(r.uris)"),
|
||||||
@NamedQuery(name="findResourceIdByName", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :ownerId and r.name = :name"),
|
@NamedQuery(name="findResourceIdByName", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :ownerId and r.name = :name"),
|
||||||
@NamedQuery(name="findResourceIdByType", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :ownerId and r.type = :type"),
|
@NamedQuery(name="findResourceIdByType", query="select distinct(r) from ResourceEntity r left join fetch r.scopes s where r.resourceServer.id = :serverId and r.owner = :ownerId and r.type = :type"),
|
||||||
@NamedQuery(name="findResourceIdByServerId", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId "),
|
@NamedQuery(name="findResourceIdByServerId", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId "),
|
||||||
@NamedQuery(name="findResourceIdByScope", query="select r.id from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"),
|
@NamedQuery(name="findResourceIdByScope", query="select r from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))"),
|
||||||
@NamedQuery(name="deleteResourceByResourceServer", query="delete from ResourceEntity r where r.resourceServer.id = :serverId")
|
@NamedQuery(name="deleteResourceByResourceServer", query="delete from ResourceEntity r where r.resourceServer.id = :serverId")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -80,7 +80,7 @@ public class ResourceEntity {
|
||||||
@Column(name = "DISPLAY_NAME")
|
@Column(name = "DISPLAY_NAME")
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ElementCollection(fetch = FetchType.LAZY)
|
||||||
@Column(name = "VALUE")
|
@Column(name = "VALUE")
|
||||||
@CollectionTable(name = "RESOURCE_URIS", joinColumns = { @JoinColumn(name="RESOURCE_ID") })
|
@CollectionTable(name = "RESOURCE_URIS", joinColumns = { @JoinColumn(name="RESOURCE_ID") })
|
||||||
private Set<String> uris = new HashSet<>();
|
private Set<String> uris = new HashSet<>();
|
||||||
|
@ -101,7 +101,7 @@ public class ResourceEntity {
|
||||||
@JoinColumn(name = "RESOURCE_SERVER_ID")
|
@JoinColumn(name = "RESOURCE_SERVER_ID")
|
||||||
private ResourceServerEntity resourceServer;
|
private ResourceServerEntity resourceServer;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
|
@OneToMany(fetch = FetchType.LAZY, cascade = {})
|
||||||
@JoinTable(name = "RESOURCE_SCOPE", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
|
@JoinTable(name = "RESOURCE_SCOPE", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
|
||||||
private List<ScopeEntity> scopes = new LinkedList<>();
|
private List<ScopeEntity> scopes = new LinkedList<>();
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ public class ResourceEntity {
|
||||||
@JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID"))
|
@JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "RESOURCE_ID"), inverseJoinColumns = @JoinColumn(name = "POLICY_ID"))
|
||||||
private List<PolicyEntity> policies = new LinkedList<>();
|
private List<PolicyEntity> policies = new LinkedList<>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="resource")
|
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="resource", fetch = FetchType.LAZY)
|
||||||
@Fetch(FetchMode.SELECT)
|
@Fetch(FetchMode.SELECT)
|
||||||
@BatchSize(size = 20)
|
@BatchSize(size = 20)
|
||||||
private Collection<ResourceAttributeEntity> attributes = new ArrayList<>();
|
private Collection<ResourceAttributeEntity> attributes = new ArrayList<>();
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.authorization.jpa.entities.PolicyEntity;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.store.PolicyStore;
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||||
|
|
||||||
|
@ -90,23 +91,25 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
PolicyEntity entity = entityManager.find(PolicyEntity.class, id);
|
PolicyEntity policyEntity = entityManager.find(PolicyEntity.class, id);
|
||||||
if (entity == null) return null;
|
|
||||||
|
|
||||||
return new PolicyAdapter(entity, entityManager, provider.getStoreFactory());
|
if (policyEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PolicyAdapter(policyEntity, entityManager, provider.getStoreFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy findByName(String name, String resourceServerId) {
|
public Policy findByName(String name, String resourceServerId) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByName", String.class);
|
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByName", PolicyEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
query.setParameter("name", name);
|
query.setParameter("name", name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String id = query.getSingleResult();
|
return new PolicyAdapter(query.getSingleResult(), entityManager, provider.getStoreFactory());
|
||||||
return provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
|
|
||||||
} catch (NoResultException ex) {
|
} catch (NoResultException ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -203,17 +206,16 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByResource", String.class);
|
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByResource", PolicyEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("resourceId", resourceId);
|
query.setParameter("resourceId", resourceId);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> policyStore.findById(id, resourceServerId))
|
.map(entity -> new PolicyAdapter(entity, entityManager, storeFactory))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer::accept);
|
.forEach(consumer::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,17 +230,14 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findByResourceType(String resourceType, String resourceServerId, Consumer<Policy> consumer) {
|
public void findByResourceType(String resourceType, String resourceServerId, Consumer<Policy> consumer) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByResourceType", String.class);
|
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByResourceType", PolicyEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("type", resourceType);
|
query.setParameter("type", resourceType);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> policyStore.findById(id, resourceServerId))
|
.map(id -> new PolicyAdapter(id, entityManager, provider.getStoreFactory()))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer::accept);
|
.forEach(consumer::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,20 +248,19 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use separate subquery to handle DB2 and MSSSQL
|
// Use separate subquery to handle DB2 and MSSSQL
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByScope", String.class);
|
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByScope", PolicyEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("scopeIds", scopeIds);
|
query.setParameter("scopeIds", scopeIds);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
List<String> result = query.getResultList();
|
|
||||||
List<Policy> list = new LinkedList<>();
|
List<Policy> list = new LinkedList<>();
|
||||||
for (String id : result) {
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
Policy policy = provider.getStoreFactory().getPolicyStore().findById(id, resourceServerId);
|
|
||||||
if (Objects.nonNull(policy)) {
|
for (PolicyEntity entity : query.getResultList()) {
|
||||||
list.add(policy);
|
list.add(new PolicyAdapter(entity, entityManager, storeFactory));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,12 +276,12 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
@Override
|
@Override
|
||||||
public void findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
public void findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||||
// Use separate subquery to handle DB2 and MSSSQL
|
// Use separate subquery to handle DB2 and MSSSQL
|
||||||
TypedQuery<String> query;
|
TypedQuery<PolicyEntity> query;
|
||||||
|
|
||||||
if (resourceId == null) {
|
if (resourceId == null) {
|
||||||
query = entityManager.createNamedQuery("findPolicyIdByNullResourceScope", String.class);
|
query = entityManager.createNamedQuery("findPolicyIdByNullResourceScope", PolicyEntity.class);
|
||||||
} else {
|
} else {
|
||||||
query = entityManager.createNamedQuery("findPolicyIdByResourceScope", String.class);
|
query = entityManager.createNamedQuery("findPolicyIdByResourceScope", PolicyEntity.class);
|
||||||
query.setParameter("resourceId", resourceId);
|
query.setParameter("resourceId", resourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,11 +289,10 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
query.setParameter("scopeIds", scopeIds);
|
query.setParameter("scopeIds", scopeIds);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> policyStore.findById(id, resourceServerId))
|
.map(id -> new PolicyAdapter(id, entityManager, storeFactory))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer::accept);
|
.forEach(consumer::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
@ -116,7 +117,7 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
queryName = "findAnyResourceIdByOwner";
|
queryName = "findAnyResourceIdByOwner";
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery(queryName, String.class);
|
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery(queryName, ResourceEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("owner", ownerId);
|
query.setParameter("owner", ownerId);
|
||||||
|
@ -125,11 +126,10 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> resourceStore.findById(id, resourceServerId))
|
.map(id -> new ResourceAdapter(id, entityManager, storeFactory))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer);
|
.forEach(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,17 +247,16 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer) {
|
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByScope", String.class);
|
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery("findResourceIdByScope", ResourceEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("scopeIds", scopes);
|
query.setParameter("scopeIds", scopes);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> resourceStore.findById(id, resourceServerId))
|
.map(id -> new ResourceAdapter(id, entityManager, storeFactory))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer);
|
.forEach(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,16 +267,14 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource findByName(String name, String ownerId, String resourceServerId) {
|
public Resource findByName(String name, String ownerId, String resourceServerId) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByName", String.class);
|
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery("findResourceIdByName", ResourceEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
query.setParameter("name", name);
|
query.setParameter("name", name);
|
||||||
query.setParameter("ownerId", ownerId);
|
query.setParameter("ownerId", ownerId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String id = query.getSingleResult();
|
return new ResourceAdapter(query.getSingleResult(), entityManager, provider.getStoreFactory());
|
||||||
return provider.getStoreFactory().getResourceStore().findById(id, resourceServerId);
|
|
||||||
} catch (NoResultException ex) {
|
} catch (NoResultException ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -294,18 +291,17 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void findByType(String type, String resourceServerId, Consumer<Resource> consumer) {
|
public void findByType(String type, String resourceServerId, Consumer<Resource> consumer) {
|
||||||
TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByType", String.class);
|
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery("findResourceIdByType", ResourceEntity.class);
|
||||||
|
|
||||||
query.setFlushMode(FlushModeType.COMMIT);
|
query.setFlushMode(FlushModeType.COMMIT);
|
||||||
query.setParameter("type", type);
|
query.setParameter("type", type);
|
||||||
query.setParameter("ownerId", resourceServerId);
|
query.setParameter("ownerId", resourceServerId);
|
||||||
query.setParameter("serverId", resourceServerId);
|
query.setParameter("serverId", resourceServerId);
|
||||||
|
|
||||||
ResourceStore resourceStore = provider.getStoreFactory().getResourceStore();
|
StoreFactory storeFactory = provider.getStoreFactory();
|
||||||
|
|
||||||
query.getResultList().stream()
|
query.getResultList().stream()
|
||||||
.map(id -> resourceStore.findById(id, resourceServerId))
|
.map(entity -> new ResourceAdapter(entity, entityManager, storeFactory))
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(consumer);
|
.forEach(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,8 +151,7 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
public Set<Policy> getAssociatedPolicies() {
|
public Set<Policy> getAssociatedPolicies() {
|
||||||
Set<Policy> result = new HashSet<>();
|
Set<Policy> result = new HashSet<>();
|
||||||
for (PolicyEntity policy : entity.getAssociatedPolicies()) {
|
for (PolicyEntity policy : entity.getAssociatedPolicies()) {
|
||||||
Policy p = storeFactory.getPolicyStore().findById(policy.getId(), entity.getResourceServer().getId());
|
result.add(new PolicyAdapter(policy, em, storeFactory));
|
||||||
result.add(p);
|
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableSet(result);
|
return Collections.unmodifiableSet(result);
|
||||||
}
|
}
|
||||||
|
@ -239,6 +238,8 @@ public class PolicyAdapter implements Policy, JpaModel<PolicyEntity> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFetched(String association) {
|
||||||
|
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(entity, association);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,11 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
|
||||||
entity.getAttributes().removeAll(toRemove);
|
entity.getAttributes().removeAll(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFetched(String association) {
|
||||||
|
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(this, association);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ResourceEntity toEntity(EntityManager em, Resource resource) {
|
public static ResourceEntity toEntity(EntityManager em, Resource resource) {
|
||||||
if (resource instanceof ResourceAdapter) {
|
if (resource instanceof ResourceAdapter) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.models.jpa;
|
package org.keycloak.models.jpa;
|
||||||
|
|
||||||
|
import org.keycloak.authorization.jpa.entities.ResourceEntity;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
@ -42,6 +43,7 @@ import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
|
import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
import org.keycloak.models.jpa.entities.UserConsentEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
|
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
|
||||||
import org.keycloak.models.utils.DefaultRoles;
|
import org.keycloak.models.utils.DefaultRoles;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
|
@ -50,6 +52,11 @@ import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Subquery;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -510,12 +517,9 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserById(String id, RealmModel realm) {
|
public UserModel getUserById(String id, RealmModel realm) {
|
||||||
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
|
UserEntity userEntity = em.find(UserEntity.class, id);
|
||||||
query.setParameter("id", id);
|
if (userEntity == null) return null;
|
||||||
query.setParameter("realmId", realm.getId());
|
return new UserAdapter(session, realm, em, userEntity);
|
||||||
List<UserEntity> entities = query.getResultList();
|
|
||||||
if (entities.size() == 0) return null;
|
|
||||||
return new UserAdapter(session, realm, em, entities.get(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -700,55 +704,86 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||||
StringBuilder builder = new StringBuilder("select u from UserEntity u where u.realmId = :realmId");
|
CriteriaBuilder builder = em.getCriteriaBuilder();
|
||||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
CriteriaQuery<UserEntity> queryBuilder = builder.createQuery(UserEntity.class);
|
||||||
String attribute = null;
|
Root<UserEntity> root = queryBuilder.from(UserEntity.class);
|
||||||
String parameterName = null;
|
|
||||||
if (entry.getKey().equals(UserModel.USERNAME)) {
|
List<Predicate> predicates = new ArrayList();
|
||||||
attribute = "lower(u.username)";
|
|
||||||
parameterName = JpaUserProvider.USERNAME;
|
predicates.add(builder.equal(root.get("realmId"), realm.getId()));
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
|
|
||||||
attribute = "lower(u.firstName)";
|
if (!session.getAttributeOrDefault(UserModel.INCLUDE_SERVICE_ACCOUNT, true)) {
|
||||||
parameterName = JpaUserProvider.FIRST_NAME;
|
predicates.add(root.get("serviceAccountClientLink").isNull());
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
|
|
||||||
attribute = "lower(u.lastName)";
|
|
||||||
parameterName = JpaUserProvider.LAST_NAME;
|
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
|
|
||||||
attribute = "lower(u.email)";
|
|
||||||
parameterName = JpaUserProvider.EMAIL;
|
|
||||||
}
|
|
||||||
if (attribute == null) continue;
|
|
||||||
builder.append(" and ");
|
|
||||||
builder.append(attribute).append(" like :").append(parameterName);
|
|
||||||
}
|
}
|
||||||
builder.append(" order by u.username");
|
|
||||||
String q = builder.toString();
|
|
||||||
TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
|
|
||||||
query.setParameter("realmId", realm.getId());
|
|
||||||
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||||
String parameterName = null;
|
String key = entry.getKey();
|
||||||
if (entry.getKey().equals(UserModel.USERNAME)) {
|
String value = entry.getValue();
|
||||||
parameterName = JpaUserProvider.USERNAME;
|
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
|
if (value == null) {
|
||||||
parameterName = JpaUserProvider.FIRST_NAME;
|
continue;
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
|
}
|
||||||
parameterName = JpaUserProvider.LAST_NAME;
|
|
||||||
} else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
|
switch (key) {
|
||||||
parameterName = JpaUserProvider.EMAIL;
|
case UserModel.USERNAME:
|
||||||
|
case UserModel.FIRST_NAME:
|
||||||
|
case UserModel.LAST_NAME:
|
||||||
|
case UserModel.EMAIL:
|
||||||
|
predicates.add(builder.like(builder.lower(root.get(key)), "%" + value.toLowerCase() + "%"));
|
||||||
}
|
}
|
||||||
if (parameterName == null) continue;
|
|
||||||
query.setParameter(parameterName, "%" + entry.getValue().toLowerCase() + "%");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> userGroups = (Set<String>) session.getAttribute(UserModel.GROUPS);
|
||||||
|
|
||||||
|
if (userGroups != null) {
|
||||||
|
Subquery subquery = queryBuilder.subquery(String.class);
|
||||||
|
Root<UserGroupMembershipEntity> from = subquery.from(UserGroupMembershipEntity.class);
|
||||||
|
|
||||||
|
subquery.select(builder.literal(1));
|
||||||
|
|
||||||
|
List<Predicate> subPredicates = new ArrayList<>();
|
||||||
|
|
||||||
|
subPredicates.add(from.get("groupId").in(userGroups));
|
||||||
|
subPredicates.add(builder.equal(from.get("user").get("id"), root.get("id")));
|
||||||
|
|
||||||
|
Subquery subquery1 = queryBuilder.subquery(String.class);
|
||||||
|
|
||||||
|
subquery1.select(builder.literal(1));
|
||||||
|
Root from1 = subquery1.from(ResourceEntity.class);
|
||||||
|
|
||||||
|
List<Predicate> subs = new ArrayList<>();
|
||||||
|
|
||||||
|
subs.add(builder.like(from1.get("name"), builder.concat("group.resource.", from.get("groupId"))));
|
||||||
|
|
||||||
|
subquery1.where(subs.toArray(new Predicate[subs.size()]));
|
||||||
|
|
||||||
|
subPredicates.add(builder.exists(subquery1));
|
||||||
|
|
||||||
|
subquery.where(subPredicates.toArray(new Predicate[subPredicates.size()]));
|
||||||
|
|
||||||
|
predicates.add(builder.exists(subquery));
|
||||||
|
}
|
||||||
|
|
||||||
|
queryBuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get(UserModel.USERNAME)));
|
||||||
|
|
||||||
|
TypedQuery<UserEntity> query = em.createQuery(queryBuilder);
|
||||||
|
|
||||||
if (firstResult != -1) {
|
if (firstResult != -1) {
|
||||||
query.setFirstResult(firstResult);
|
query.setFirstResult(firstResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxResults != -1) {
|
if (maxResults != -1) {
|
||||||
query.setMaxResults(maxResults);
|
query.setMaxResults(maxResults);
|
||||||
}
|
}
|
||||||
List<UserEntity> results = query.getResultList();
|
|
||||||
List<UserModel> users = new ArrayList<UserModel>();
|
List<UserModel> results = new ArrayList<>();
|
||||||
for (UserEntity entity : results) users.add(new UserAdapter(session, realm, em, entity));
|
UserProvider users = session.users();
|
||||||
return users;
|
|
||||||
|
for (UserEntity entity : query.getResultList()) {
|
||||||
|
results.add(users.getUserById(entity.getId(), realm));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -46,7 +46,6 @@ import java.util.Collection;
|
||||||
@NamedQuery(name="getAllUsersByRealmExcludeServiceAccount", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) order by u.username"),
|
@NamedQuery(name="getAllUsersByRealmExcludeServiceAccount", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) order by u.username"),
|
||||||
@NamedQuery(name="searchForUser", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
|
@NamedQuery(name="searchForUser", query="select u from UserEntity u where u.realmId = :realmId and (u.serviceAccountClientLink is null) and " +
|
||||||
"( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search ) order by u.username"),
|
"( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search ) order by u.username"),
|
||||||
@NamedQuery(name="getRealmUserById", query="select u from UserEntity u where u.id = :id and u.realmId = :realmId"),
|
|
||||||
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
|
@NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realmId = :realmId"),
|
||||||
@NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"),
|
@NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realmId = :realmId"),
|
||||||
@NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"),
|
@NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realmId = :realmId"),
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--
|
||||||
|
~ * 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.
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
|
||||||
|
<changeSet author="psilva@redhat.com" id="4.6.0-KEYCLOAK-7950">
|
||||||
|
<update tableName="RESOURCE_SERVER_RESOURCE">
|
||||||
|
<column name="TYPE" value="Group"/>
|
||||||
|
<where>NAME LIKE 'group.resource.%'</where>
|
||||||
|
</update>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
|
@ -59,4 +59,5 @@
|
||||||
<include file="META-INF/jpa-changelog-authz-4.2.0.Final.xml"/>
|
<include file="META-INF/jpa-changelog-authz-4.2.0.Final.xml"/>
|
||||||
<include file="META-INF/jpa-changelog-4.2.0.xml"/>
|
<include file="META-INF/jpa-changelog-4.2.0.xml"/>
|
||||||
<include file="META-INF/jpa-changelog-4.3.0.xml"/>
|
<include file="META-INF/jpa-changelog-4.3.0.xml"/>
|
||||||
|
<include file="META-INF/jpa-changelog-4.6.0.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -163,4 +163,6 @@ public interface Policy {
|
||||||
void addResource(Resource resource);
|
void addResource(Resource resource);
|
||||||
|
|
||||||
void removeResource(Resource resource);
|
void removeResource(Resource resource);
|
||||||
|
|
||||||
|
boolean isFetched(String association);
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,4 +180,6 @@ public interface Resource {
|
||||||
void setAttribute(String name, List<String> values);
|
void setAttribute(String name, List<String> values);
|
||||||
|
|
||||||
void removeAttribute(String name);
|
void removeAttribute(String name);
|
||||||
|
|
||||||
|
boolean isFetched(String association);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,15 @@ public interface KeycloakSession {
|
||||||
|
|
||||||
Object getAttribute(String attribute);
|
Object getAttribute(String attribute);
|
||||||
<T> T getAttribute(String attribute, Class<T> clazz);
|
<T> T getAttribute(String attribute, Class<T> clazz);
|
||||||
|
default <T> T getAttributeOrDefault(String attribute, T defaultValue) {
|
||||||
|
T value = (T) getAttribute(attribute);
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
Object removeAttribute(String attribute);
|
Object removeAttribute(String attribute);
|
||||||
void setAttribute(String name, Object value);
|
void setAttribute(String name, Object value);
|
||||||
|
|
|
@ -33,6 +33,8 @@ public interface UserModel extends RoleMapperModel {
|
||||||
String FIRST_NAME = "firstName";
|
String FIRST_NAME = "firstName";
|
||||||
String EMAIL = "email";
|
String EMAIL = "email";
|
||||||
String LOCALE = "locale";
|
String LOCALE = "locale";
|
||||||
|
String INCLUDE_SERVICE_ACCOUNT = "keycloak.session.realm.users.query.include_service_account";
|
||||||
|
String GROUPS = "keycloak.session.realm.users.query.groups";
|
||||||
|
|
||||||
interface UserRemovedEvent extends ProviderEvent {
|
interface UserRemovedEvent extends ProviderEvent {
|
||||||
RealmModel getRealm();
|
RealmModel getRealm();
|
||||||
|
|
|
@ -35,6 +35,8 @@ import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.ErrorResponse;
|
import org.keycloak.services.ErrorResponse;
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.UserPermissionEvaluator;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -47,7 +49,7 @@ import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -55,6 +57,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base resource for managing users
|
* Base resource for managing users
|
||||||
|
@ -181,12 +184,13 @@ public class UsersResource {
|
||||||
@QueryParam("first") Integer firstResult,
|
@QueryParam("first") Integer firstResult,
|
||||||
@QueryParam("max") Integer maxResults,
|
@QueryParam("max") Integer maxResults,
|
||||||
@QueryParam("briefRepresentation") Boolean briefRepresentation) {
|
@QueryParam("briefRepresentation") Boolean briefRepresentation) {
|
||||||
auth.users().requireQuery();
|
UserPermissionEvaluator userPermissionEvaluator = auth.users();
|
||||||
|
|
||||||
|
userPermissionEvaluator.requireQuery();
|
||||||
|
|
||||||
firstResult = firstResult != null ? firstResult : -1;
|
firstResult = firstResult != null ? firstResult : -1;
|
||||||
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
|
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
|
||||||
|
|
||||||
List<UserRepresentation> results = new ArrayList<UserRepresentation>();
|
|
||||||
List<UserModel> userModels = Collections.emptyList();
|
List<UserModel> userModels = Collections.emptyList();
|
||||||
if (search != null) {
|
if (search != null) {
|
||||||
if (search.startsWith(SEARCH_ID_PARAMETER)) {
|
if (search.startsWith(SEARCH_ID_PARAMETER)) {
|
||||||
|
@ -198,7 +202,7 @@ public class UsersResource {
|
||||||
userModels = session.users().searchForUser(search.trim(), realm, firstResult, maxResults);
|
userModels = session.users().searchForUser(search.trim(), realm, firstResult, maxResults);
|
||||||
}
|
}
|
||||||
} else if (last != null || first != null || email != null || username != null) {
|
} else if (last != null || first != null || email != null || username != null) {
|
||||||
Map<String, String> attributes = new HashMap<String, String>();
|
Map<String, String> attributes = new HashMap<>();
|
||||||
if (last != null) {
|
if (last != null) {
|
||||||
attributes.put(UserModel.LAST_NAME, last);
|
attributes.put(UserModel.LAST_NAME, last);
|
||||||
}
|
}
|
||||||
|
@ -211,22 +215,12 @@ public class UsersResource {
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
attributes.put(UserModel.USERNAME, username);
|
attributes.put(UserModel.USERNAME, username);
|
||||||
}
|
}
|
||||||
userModels = session.users().searchForUser(attributes, realm, firstResult, maxResults);
|
return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult, maxResults, true);
|
||||||
} else {
|
} else {
|
||||||
userModels = session.users().getUsers(realm, firstResult, maxResults, false);
|
return searchForUser(new HashMap<>(), realm, userPermissionEvaluator, briefRepresentation, firstResult, maxResults, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean canViewGlobal = auth.users().canView();
|
return toRepresentation(realm, userPermissionEvaluator, briefRepresentation, userModels);
|
||||||
boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
|
|
||||||
for (UserModel user : userModels) {
|
|
||||||
if (!canViewGlobal && !auth.users().canView(user)) continue;
|
|
||||||
UserRepresentation userRep = briefRepresentationB
|
|
||||||
? ModelToRepresentation.toBriefRepresentation(user)
|
|
||||||
: ModelToRepresentation.toRepresentation(session, realm, user);
|
|
||||||
userRep.setAccess(auth.users().getAccess(user));
|
|
||||||
results.add(userRep);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("count")
|
@Path("count")
|
||||||
|
@ -238,4 +232,42 @@ public class UsersResource {
|
||||||
|
|
||||||
return session.users().getUsersCount(realm);
|
return session.users().getUsersCount(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<UserRepresentation> searchForUser(Map<String, String> attributes, RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, Integer firstResult, Integer maxResults, Boolean includeServiceAccounts) {
|
||||||
|
session.setAttribute(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts);
|
||||||
|
|
||||||
|
if (!auth.users().canView()) {
|
||||||
|
Set<String> groupModels = auth.groups().getGroupsWithViewPermission();
|
||||||
|
|
||||||
|
if (!groupModels.isEmpty()) {
|
||||||
|
session.setAttribute(UserModel.GROUPS, groupModels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserModel> userModels = session.users().searchForUser(attributes, realm, firstResult, maxResults);
|
||||||
|
|
||||||
|
return toRepresentation(realm, usersEvaluator, briefRepresentation, userModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<UserRepresentation> toRepresentation(RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, List<UserModel> userModels) {
|
||||||
|
boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
|
||||||
|
List<UserRepresentation> results = new ArrayList<>();
|
||||||
|
boolean canViewGlobal = usersEvaluator.canView();
|
||||||
|
|
||||||
|
usersEvaluator.grantIfNoPermission(session.getAttribute(UserModel.GROUPS) != null);
|
||||||
|
|
||||||
|
for (UserModel user : userModels) {
|
||||||
|
if (!canViewGlobal) {
|
||||||
|
if (!usersEvaluator.canView(user)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UserRepresentation userRep = briefRepresentationB
|
||||||
|
? ModelToRepresentation.toBriefRepresentation(user)
|
||||||
|
: ModelToRepresentation.toRepresentation(session, realm, user);
|
||||||
|
userRep.setAccess(usersEvaluator.getAccess(user));
|
||||||
|
results.add(userRep);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,7 +342,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
return root.evaluatePermission(resource, scope, server, context);
|
return root.evaluatePermission(resource, server, context, scope);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +376,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = manageScope(server);
|
Scope scope = manageScope(server);
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -404,7 +404,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = configureScope(server);
|
Scope scope = configureScope(server);
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void requireConfigure(ClientModel client) {
|
public void requireConfigure(ClientModel client) {
|
||||||
|
@ -450,7 +450,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = viewScope(server);
|
Scope scope = viewScope(server);
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -529,7 +529,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = mapRolesScope(server);
|
Scope scope = mapRolesScope(server);
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -606,7 +606,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
|
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean canMapClientScopeRoles(ClientModel client) {
|
public boolean canMapClientScopeRoles(ClientModel client) {
|
||||||
|
@ -628,7 +628,7 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
|
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
|
||||||
return root.evaluatePermission(resource, scope, server);
|
return root.evaluatePermission(resource, server, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.services.resources.admin.permissions;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -45,17 +46,17 @@ public interface GroupPermissionEvaluator {
|
||||||
|
|
||||||
void requireView();
|
void requireView();
|
||||||
|
|
||||||
boolean canViewMembers(GroupModel group);
|
boolean getGroupsWithViewPermission(GroupModel group);
|
||||||
|
|
||||||
void requireViewMembers(GroupModel group);
|
void requireViewMembers(GroupModel group);
|
||||||
|
|
||||||
boolean canManageMembers(GroupModel group);
|
boolean canManageMembers(GroupModel group);
|
||||||
|
|
||||||
void requireManageMembers(GroupModel group);
|
|
||||||
|
|
||||||
boolean canManageMembership(GroupModel group);
|
boolean canManageMembership(GroupModel group);
|
||||||
|
|
||||||
void requireManageMembership(GroupModel group);
|
void requireManageMembership(GroupModel group);
|
||||||
|
|
||||||
Map<String, Boolean> getAccess(GroupModel group);
|
Map<String, Boolean> getAccess(GroupModel group);
|
||||||
|
|
||||||
|
Set<String> getGroupsWithViewPermission();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,21 +16,27 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin.permissions;
|
package org.keycloak.services.resources.admin.permissions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -39,45 +45,46 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManagement {
|
class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManagement {
|
||||||
private static final Logger logger = Logger.getLogger(GroupPermissions.class);
|
|
||||||
public static final String MAP_ROLE_SCOPE = "map-role";
|
|
||||||
public static final String MANAGE_MEMBERSHIP_SCOPE = "manage-membership";
|
|
||||||
public static final String MANAGE_MEMBERS_SCOPE = "manage-members";
|
|
||||||
public static final String VIEW_MEMBERS_SCOPE = "view-members";
|
|
||||||
protected final KeycloakSession session;
|
|
||||||
protected final RealmModel realm;
|
|
||||||
protected final AuthorizationProvider authz;
|
|
||||||
protected final MgmtPermissions root;
|
|
||||||
|
|
||||||
public GroupPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
|
private static final String MANAGE_MEMBERSHIP_SCOPE = "manage-membership";
|
||||||
this.session = session;
|
private static final String MANAGE_MEMBERS_SCOPE = "manage-members";
|
||||||
this.realm = realm;
|
private static final String VIEW_MEMBERS_SCOPE = "view-members";
|
||||||
|
private static final String RESOURCE_NAME_PREFIX = "group.resource.";
|
||||||
|
|
||||||
|
private final AuthorizationProvider authz;
|
||||||
|
private final MgmtPermissions root;
|
||||||
|
private final ResourceStore resourceStore;
|
||||||
|
private final PolicyStore policyStore;
|
||||||
|
|
||||||
|
GroupPermissions(AuthorizationProvider authz, MgmtPermissions root) {
|
||||||
this.authz = authz;
|
this.authz = authz;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
resourceStore = authz.getStoreFactory().getResourceStore();
|
||||||
|
policyStore = authz.getStoreFactory().getPolicyStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getGroupResourceName(GroupModel group) {
|
private static String getGroupResourceName(GroupModel group) {
|
||||||
return "group.resource." + group.getId();
|
return RESOURCE_NAME_PREFIX + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getManagePermissionGroup(GroupModel group) {
|
private static String getManagePermissionGroup(GroupModel group) {
|
||||||
return "manage.permission.group." + group.getId();
|
return "manage.permission.group." + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getManageMembersPermissionGroup(GroupModel group) {
|
private static String getManageMembersPermissionGroup(GroupModel group) {
|
||||||
return "manage.members.permission.group." + group.getId();
|
return "manage.members.permission.group." + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getManageMembershipPermissionGroup(GroupModel group) {
|
private static String getManageMembershipPermissionGroup(GroupModel group) {
|
||||||
return "manage.membership.permission.group." + group.getId();
|
return "manage.membership.permission.group." + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getViewPermissionGroup(GroupModel group) {
|
private static String getViewPermissionGroup(GroupModel group) {
|
||||||
return "view.permission.group." + group.getId();
|
return "view.permission.group." + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getViewMembersPermissionGroup(GroupModel group) {
|
private static String getViewMembersPermissionGroup(GroupModel group) {
|
||||||
return "view.members.permission.group." + group.getId();
|
return "view.members.permission.group." + group.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +99,9 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
Scope manageMembershipScope = root.initializeRealmScope(MANAGE_MEMBERSHIP_SCOPE);
|
Scope manageMembershipScope = root.initializeRealmScope(MANAGE_MEMBERSHIP_SCOPE);
|
||||||
|
|
||||||
String groupResourceName = getGroupResourceName(group);
|
String groupResourceName = getGroupResourceName(group);
|
||||||
Resource groupResource = authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId());
|
Resource groupResource = resourceStore.findByName(groupResourceName, server.getId());
|
||||||
if (groupResource == null) {
|
if (groupResource == null) {
|
||||||
groupResource = authz.getStoreFactory().getResourceStore().create(groupResourceName, server, server.getId());
|
groupResource = resourceStore.create(groupResourceName, server, server.getId());
|
||||||
Set<Scope> scopeset = new HashSet<>();
|
Set<Scope> scopeset = new HashSet<>();
|
||||||
scopeset.add(manageScope);
|
scopeset.add(manageScope);
|
||||||
scopeset.add(viewScope);
|
scopeset.add(viewScope);
|
||||||
|
@ -102,29 +109,30 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
scopeset.add(manageMembershipScope);
|
scopeset.add(manageMembershipScope);
|
||||||
scopeset.add(manageMembersScope);
|
scopeset.add(manageMembersScope);
|
||||||
groupResource.updateScopes(scopeset);
|
groupResource.updateScopes(scopeset);
|
||||||
|
groupResource.setType("Group");
|
||||||
}
|
}
|
||||||
String managePermissionName = getManagePermissionGroup(group);
|
String managePermissionName = getManagePermissionGroup(group);
|
||||||
Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
|
Policy managePermission = policyStore.findByName(managePermissionName, server.getId());
|
||||||
if (managePermission == null) {
|
if (managePermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, managePermissionName, groupResource, manageScope);
|
Helper.addEmptyScopePermission(authz, server, managePermissionName, groupResource, manageScope);
|
||||||
}
|
}
|
||||||
String viewPermissionName = getViewPermissionGroup(group);
|
String viewPermissionName = getViewPermissionGroup(group);
|
||||||
Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
|
Policy viewPermission = policyStore.findByName(viewPermissionName, server.getId());
|
||||||
if (viewPermission == null) {
|
if (viewPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, viewPermissionName, groupResource, viewScope);
|
Helper.addEmptyScopePermission(authz, server, viewPermissionName, groupResource, viewScope);
|
||||||
}
|
}
|
||||||
String manageMembersPermissionName = getManageMembersPermissionGroup(group);
|
String manageMembersPermissionName = getManageMembersPermissionGroup(group);
|
||||||
Policy manageMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId());
|
Policy manageMembersPermission = policyStore.findByName(manageMembersPermissionName, server.getId());
|
||||||
if (manageMembersPermission == null) {
|
if (manageMembersPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, manageMembersPermissionName, groupResource, manageMembersScope);
|
Helper.addEmptyScopePermission(authz, server, manageMembersPermissionName, groupResource, manageMembersScope);
|
||||||
}
|
}
|
||||||
String viewMembersPermissionName = getViewMembersPermissionGroup(group);
|
String viewMembersPermissionName = getViewMembersPermissionGroup(group);
|
||||||
Policy viewMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId());
|
Policy viewMembersPermission = policyStore.findByName(viewMembersPermissionName, server.getId());
|
||||||
if (viewMembersPermission == null) {
|
if (viewMembersPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, viewMembersPermissionName, groupResource, viewMembersScope);
|
Helper.addEmptyScopePermission(authz, server, viewMembersPermissionName, groupResource, viewMembersScope);
|
||||||
}
|
}
|
||||||
String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
|
String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
|
||||||
Policy manageMembershipPermission = authz.getStoreFactory().getPolicyStore().findByName(manageMembershipPermissionName, server.getId());
|
Policy manageMembershipPermission = policyStore.findByName(manageMembershipPermissionName, server.getId());
|
||||||
if (manageMembershipPermission == null) {
|
if (manageMembershipPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, manageMembershipPermissionName, groupResource, manageMembershipScope);
|
Helper.addEmptyScopePermission(authz, server, manageMembershipPermissionName, groupResource, manageMembershipScope);
|
||||||
}
|
}
|
||||||
|
@ -143,21 +151,12 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPermissionsEnabled(GroupModel group) {
|
public boolean isPermissionsEnabled(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return false;
|
if (server == null) return false;
|
||||||
|
|
||||||
return authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()) != null;
|
return resourceStore.findByName(getGroupResourceName(group), server.getId()) != null;
|
||||||
}
|
|
||||||
|
|
||||||
private Resource groupResource(GroupModel group) {
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
|
||||||
if (server == null) return null;
|
|
||||||
String groupResourceName = getGroupResourceName(group);
|
|
||||||
return authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -169,78 +168,46 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deletePermissions(GroupModel group) {
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
|
||||||
if (server == null) return;
|
|
||||||
Policy managePermission = managePermission(group);
|
|
||||||
if (managePermission != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(managePermission.getId());
|
|
||||||
}
|
|
||||||
Policy viewPermission = viewPermission(group);
|
|
||||||
if (viewPermission != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId());
|
|
||||||
}
|
|
||||||
Policy manageMembersPermission = manageMembersPermission(group);
|
|
||||||
if (manageMembersPermission != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(manageMembersPermission.getId());
|
|
||||||
}
|
|
||||||
Policy viewMembersPermission = viewMembersPermission(group);
|
|
||||||
if (viewMembersPermission != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(viewMembersPermission.getId());
|
|
||||||
}
|
|
||||||
Policy manageMembershipPermission = manageMembershipPermission(group);
|
|
||||||
if (manageMembershipPermission != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(manageMembershipPermission.getId());
|
|
||||||
}
|
|
||||||
Resource resource = groupResource(group);
|
|
||||||
if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy viewMembersPermission(GroupModel group) {
|
public Policy viewMembersPermission(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
String viewMembersPermissionName = getViewMembersPermissionGroup(group);
|
return policyStore.findByName(getViewMembersPermissionGroup(group), server.getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy manageMembersPermission(GroupModel group) {
|
public Policy manageMembersPermission(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
String manageMembersPermissionName = getManageMembersPermissionGroup(group);
|
return policyStore.findByName(getManageMembersPermissionGroup(group), server.getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy manageMembershipPermission(GroupModel group) {
|
public Policy manageMembershipPermission(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
|
return policyStore.findByName(getManageMembershipPermissionGroup(group), server.getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(manageMembershipPermissionName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy viewPermission(GroupModel group) {
|
public Policy viewPermission(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
String viewPermissionName = getViewPermissionGroup(group);
|
return policyStore.findByName(getViewPermissionGroup(group), server.getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy managePermission(GroupModel group) {
|
public Policy managePermission(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
String managePermissionName = getManagePermissionGroup(group);
|
return policyStore.findByName(getManagePermissionGroup(group), server.getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource resource(GroupModel group) {
|
public Resource resource(GroupModel group) {
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
Resource resource = resourceStore.findByName(getGroupResourceName(group), server.getId());
|
||||||
if (resource == null) return null;
|
if (resource == null) return null;
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
@ -257,35 +224,17 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canManage(GroupModel group) {
|
public boolean canManage(GroupModel group) {
|
||||||
if (canManage()) return true;
|
if (canManage()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!root.isAdminSameRealm()) {
|
if (!root.isAdminSameRealm()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(group, MgmtPermissions.MANAGE_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = managePermission(group);
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmManageScope();
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -294,37 +243,18 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canView(GroupModel group) {
|
public boolean canView(GroupModel group) {
|
||||||
return hasView(group) || canManage(group);
|
if (canView() || canManage()) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
private boolean hasView(GroupModel group) {
|
|
||||||
if (canView()) return true;
|
|
||||||
|
|
||||||
if (!root.isAdminSameRealm()) {
|
if (!root.isAdminSameRealm()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(group, MgmtPermissions.VIEW_SCOPE, MgmtPermissions.MANAGE_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = viewPermission(group);
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then abort
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmViewScope();
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -357,15 +287,11 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canViewMembers(GroupModel group) {
|
public boolean getGroupsWithViewPermission(GroupModel group) {
|
||||||
return canViewMembersEvaluation(group) || canManageMembers(group);
|
if (root.users().canView() || root.users().canManage()) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
private boolean canViewMembersEvaluation(GroupModel group) {
|
|
||||||
if (root.users().canView()) return true;
|
|
||||||
|
|
||||||
if (!root.isAdminSameRealm()) {
|
if (!root.isAdminSameRealm()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -374,34 +300,41 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return false;
|
if (server == null) return false;
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
return hasPermission(group, VIEW_MEMBERS_SCOPE, MANAGE_MEMBERS_SCOPE);
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = viewMembersPermission(group);
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = authz.getStoreFactory().getScopeStore().findByName(VIEW_MEMBERS_SCOPE, server.getId());
|
|
||||||
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getGroupsWithViewPermission() {
|
||||||
|
if (root.users().canView() || root.users().canManage()) return Collections.emptySet();
|
||||||
|
|
||||||
|
if (!root.isAdminSameRealm()) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
|
||||||
|
if (server == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> granted = new HashSet<>();
|
||||||
|
|
||||||
|
resourceStore.findByType("Group", server.getId(), resource -> {
|
||||||
|
if (hasPermission(resource, null, VIEW_MEMBERS_SCOPE, MANAGE_MEMBERS_SCOPE)) {
|
||||||
|
granted.add(resource.getName().substring(RESOURCE_NAME_PREFIX.length()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return granted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requireViewMembers(GroupModel group) {
|
public void requireViewMembers(GroupModel group) {
|
||||||
if (!canViewMembers(group)) {
|
if (!getGroupsWithViewPermission(group)) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canManageMembers(GroupModel group) {
|
public boolean canManageMembers(GroupModel group) {
|
||||||
if (root.users().canManage()) return true;
|
if (root.users().canManage()) return true;
|
||||||
|
@ -413,29 +346,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return false;
|
if (server == null) return false;
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
return hasPermission(group, MANAGE_MEMBERS_SCOPE);
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = manageMembersPermission(group);
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MANAGE_MEMBERS_SCOPE, server.getId());
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requireManageMembers(GroupModel group) {
|
|
||||||
if (!canManageMembers(group)) {
|
|
||||||
throw new ForbiddenException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -446,25 +357,7 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(group, MANAGE_MEMBERSHIP_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = manageMembershipPermission(group);
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MANAGE_MEMBERSHIP_SCOPE, server.getId());
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -483,7 +376,81 @@ class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManag
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(GroupModel group, String... scopes) {
|
||||||
|
return hasPermission(group, null, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(GroupModel group, EvaluationContext context, String... scopes) {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
|
||||||
|
if (server == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = resourceStore.findByName(getGroupResourceName(group), server.getId());
|
||||||
|
|
||||||
|
if (resource == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasPermission(resource, context, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(Resource resource, EvaluationContext context, String... scopes) {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
Collection<Permission> permissions;
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server);
|
||||||
|
} else {
|
||||||
|
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> expectedScopes = Arrays.asList(scopes);
|
||||||
|
|
||||||
|
|
||||||
|
for (Permission permission : permissions) {
|
||||||
|
for (String scope : permission.getScopes()) {
|
||||||
|
if (expectedScopes.contains(scope)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Resource groupResource(GroupModel group) {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
if (server == null) return null;
|
||||||
|
String groupResourceName = getGroupResourceName(group);
|
||||||
|
return resourceStore.findByName(groupResourceName, server.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePermissions(GroupModel group) {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
if (server == null) return;
|
||||||
|
Policy managePermission = managePermission(group);
|
||||||
|
if (managePermission != null) {
|
||||||
|
policyStore.delete(managePermission.getId());
|
||||||
|
}
|
||||||
|
Policy viewPermission = viewPermission(group);
|
||||||
|
if (viewPermission != null) {
|
||||||
|
policyStore.delete(viewPermission.getId());
|
||||||
|
}
|
||||||
|
Policy manageMembersPermission = manageMembersPermission(group);
|
||||||
|
if (manageMembersPermission != null) {
|
||||||
|
policyStore.delete(manageMembersPermission.getId());
|
||||||
|
}
|
||||||
|
Policy viewMembersPermission = viewMembersPermission(group);
|
||||||
|
if (viewMembersPermission != null) {
|
||||||
|
policyStore.delete(viewMembersPermission.getId());
|
||||||
|
}
|
||||||
|
Policy manageMembershipPermission = manageMembershipPermission(group);
|
||||||
|
if (manageMembershipPermission != null) {
|
||||||
|
policyStore.delete(manageMembershipPermission.getId());
|
||||||
|
}
|
||||||
|
Resource resource = groupResource(group);
|
||||||
|
if (resource != null) resourceStore.delete(resource.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ class IdentityProviderPermissions implements IdentityProviderPermissionManageme
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
return root.evaluatePermission(resource, scope, server, context);
|
return root.evaluatePermission(resource, server, context, scope);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,8 @@ import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.permission.ResourcePermission;
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.permission.evaluator.PermissionEvaluator;
|
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.authorization.store.ResourceServerStore;
|
import org.keycloak.authorization.store.ResourceServerStore;
|
||||||
import org.keycloak.authorization.util.Permissions;
|
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -39,11 +37,14 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.resources.admin.AdminAuth;
|
import org.keycloak.services.resources.admin.AdminAuth;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -205,7 +206,7 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
@Override
|
@Override
|
||||||
public UserPermissions users() {
|
public UserPermissions users() {
|
||||||
if (users != null) return users;
|
if (users != null) return users;
|
||||||
users = new UserPermissions(session, realm, authz, this);
|
users = new UserPermissions(session, authz, this);
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +234,7 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
@Override
|
@Override
|
||||||
public GroupPermissions groups() {
|
public GroupPermissions groups() {
|
||||||
if (groups != null) return groups;
|
if (groups != null) return groups;
|
||||||
groups = new GroupPermissions(session, realm, authz, this);
|
groups = new GroupPermissions(authz, this);
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,25 +314,36 @@ class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManage
|
||||||
return authz.getStoreFactory().getScopeStore().findByName(scope, server.getId());
|
return authz.getStoreFactory().getScopeStore().findByName(scope, server.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer) {
|
public boolean evaluatePermission(Resource resource, ResourceServer resourceServer, Scope... scope) {
|
||||||
Identity identity = identity();
|
Identity identity = identity();
|
||||||
if (identity == null) {
|
if (identity == null) {
|
||||||
throw new RuntimeException("Identity of admin is not set for permission query");
|
throw new RuntimeException("Identity of admin is not set for permission query");
|
||||||
}
|
}
|
||||||
return evaluatePermission(resource, scope, resourceServer, identity);
|
return evaluatePermission(resource, resourceServer, identity, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, Identity identity) {
|
public Collection<Permission> evaluatePermission(ResourcePermission permission, ResourceServer resourceServer) {
|
||||||
|
return evaluatePermission(permission, resourceServer, new DefaultEvaluationContext(identity, session));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Permission> evaluatePermission(ResourcePermission permission, ResourceServer resourceServer, EvaluationContext context) {
|
||||||
|
return evaluatePermission(Arrays.asList(permission), resourceServer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean evaluatePermission(Resource resource, ResourceServer resourceServer, Identity identity, Scope... scope) {
|
||||||
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
||||||
return evaluatePermission(resource, scope, resourceServer, context);
|
return evaluatePermission(resource, resourceServer, context, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, EvaluationContext context) {
|
public boolean evaluatePermission(Resource resource, ResourceServer resourceServer, EvaluationContext context, Scope... scope) {
|
||||||
|
return !evaluatePermission(Arrays.asList(new ResourcePermission(resource, Arrays.asList(scope), resourceServer)), resourceServer, context).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Permission> evaluatePermission(List<ResourcePermission> permissions, ResourceServer resourceServer, EvaluationContext context) {
|
||||||
RealmModel oldRealm = session.getContext().getRealm();
|
RealmModel oldRealm = session.getContext().getRealm();
|
||||||
try {
|
try {
|
||||||
session.getContext().setRealm(realm);
|
session.getContext().setRealm(realm);
|
||||||
ResourcePermission permission = Permissions.permission(resourceServer, resource, scope);
|
return authz.evaluators().from(permissions, context).evaluate(resourceServer, null);
|
||||||
return !authz.evaluators().from(Arrays.asList(permission), context).evaluate(resourceServer, null).isEmpty();
|
|
||||||
} finally {
|
} finally {
|
||||||
session.getContext().setRealm(oldRealm);
|
session.getContext().setRealm(oldRealm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,7 +307,7 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
|
||||||
|
|
||||||
Resource roleResource = resource(role);
|
Resource roleResource = resource(role);
|
||||||
Scope mapRoleScope = mapRoleScope(resourceServer);
|
Scope mapRoleScope = mapRoleScope(resourceServer);
|
||||||
if (root.evaluatePermission(roleResource, mapRoleScope, resourceServer)) {
|
if (root.evaluatePermission(roleResource, resourceServer, mapRoleScope)) {
|
||||||
return checkAdminRoles(role);
|
return checkAdminRoles(role);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -391,7 +391,7 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
|
||||||
|
|
||||||
Resource roleResource = resource(role);
|
Resource roleResource = resource(role);
|
||||||
Scope scope = mapCompositeScope(resourceServer);
|
Scope scope = mapCompositeScope(resourceServer);
|
||||||
if (root.evaluatePermission(roleResource, scope, resourceServer)) {
|
if (root.evaluatePermission(roleResource, resourceServer, scope)) {
|
||||||
return checkAdminRoles(role);
|
return checkAdminRoles(role);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -430,7 +430,7 @@ class RolePermissions implements RolePermissionEvaluator, RolePermissionManageme
|
||||||
|
|
||||||
Resource roleResource = resource(role);
|
Resource roleResource = resource(role);
|
||||||
Scope scope = mapClientScope(resourceServer);
|
Scope scope = mapClientScope(resourceServer);
|
||||||
return root.evaluatePermission(roleResource, scope, resourceServer);
|
return root.evaluatePermission(roleResource, resourceServer, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin.permissions;
|
package org.keycloak.services.resources.admin.permissions;
|
||||||
|
|
||||||
import org.keycloak.authorization.model.Policy;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -26,42 +25,30 @@ import java.util.Map;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface UserPermissionEvaluator {
|
public interface UserPermissionEvaluator {
|
||||||
boolean canManage();
|
|
||||||
|
|
||||||
void requireManage();
|
void requireManage();
|
||||||
|
|
||||||
boolean canManage(UserModel user);
|
|
||||||
void requireManage(UserModel user);
|
void requireManage(UserModel user);
|
||||||
|
boolean canManage();
|
||||||
boolean canQuery();
|
boolean canManage(UserModel user);
|
||||||
|
|
||||||
void requireQuery();
|
void requireQuery();
|
||||||
|
boolean canQuery();
|
||||||
boolean canQuery(UserModel user);
|
|
||||||
|
|
||||||
void requireQuery(UserModel user);
|
|
||||||
|
|
||||||
boolean canView();
|
|
||||||
boolean canView(UserModel user);
|
|
||||||
void requireView(UserModel user);
|
|
||||||
|
|
||||||
void requireView();
|
void requireView();
|
||||||
|
void requireView(UserModel user);
|
||||||
boolean canImpersonate(UserModel user);
|
boolean canView();
|
||||||
|
boolean canView(UserModel user);
|
||||||
boolean isImpersonatable(UserModel user);
|
|
||||||
|
|
||||||
boolean canImpersonate();
|
|
||||||
|
|
||||||
void requireImpersonate(UserModel user);
|
void requireImpersonate(UserModel user);
|
||||||
|
boolean canImpersonate();
|
||||||
|
boolean canImpersonate(UserModel user);
|
||||||
|
boolean isImpersonatable(UserModel user);
|
||||||
|
|
||||||
Map<String, Boolean> getAccess(UserModel user);
|
Map<String, Boolean> getAccess(UserModel user);
|
||||||
|
|
||||||
|
void requireMapRoles(UserModel user);
|
||||||
boolean canMapRoles(UserModel user);
|
boolean canMapRoles(UserModel user);
|
||||||
|
|
||||||
void requireMapRoles(UserModel user);
|
|
||||||
|
|
||||||
boolean canManageGroupMembership(UserModel user);
|
|
||||||
|
|
||||||
void requireManageGroupMembership(UserModel user);
|
void requireManageGroupMembership(UserModel user);
|
||||||
|
boolean canManageGroupMembership(UserModel user);
|
||||||
|
void grantIfNoPermission(boolean grantIfNoPermission);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin.permissions;
|
package org.keycloak.services.resources.admin.permissions;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.common.ClientModelIdentity;
|
import org.keycloak.authorization.common.ClientModelIdentity;
|
||||||
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
|
@ -26,23 +25,30 @@ import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.models.ImpersonationConstants;
|
import org.keycloak.models.ImpersonationConstants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
import org.keycloak.services.ForbiddenException;
|
import org.keycloak.services.ForbiddenException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages default policies for all users.
|
* Manages default policies for all users.
|
||||||
|
@ -52,28 +58,32 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
class UserPermissions implements UserPermissionEvaluator, UserPermissionManagement {
|
class UserPermissions implements UserPermissionEvaluator, UserPermissionManagement {
|
||||||
private static final Logger logger = Logger.getLogger(UserPermissions.class);
|
|
||||||
public static final String MAP_ROLES_SCOPE="map-roles";
|
|
||||||
public static final String IMPERSONATE_SCOPE="impersonate";
|
|
||||||
public static final String USER_IMPERSONATED_SCOPE="user-impersonated";
|
|
||||||
public static final String MANAGE_GROUP_MEMBERSHIP_SCOPE="manage-group-membership";
|
|
||||||
public static final String MAP_ROLES_PERMISSION_USERS = "map-roles.permission.users";
|
|
||||||
public static final String ADMIN_IMPERSONATING_PERMISSION = "admin-impersonating.permission.users";
|
|
||||||
public static final String USER_IMPERSONATED_PERMISSION = "user-impersonated.permission.users";
|
|
||||||
public static final String MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS = "manage-group-membership.permission.users";
|
|
||||||
public static final String MANAGE_PERMISSION_USERS = "manage.permission.users";
|
|
||||||
public static final String VIEW_PERMISSION_USERS = "view.permission.users";
|
|
||||||
public static final String USERS_RESOURCE = "Users";
|
|
||||||
protected final KeycloakSession session;
|
|
||||||
protected final RealmModel realm;
|
|
||||||
protected final AuthorizationProvider authz;
|
|
||||||
protected final MgmtPermissions root;
|
|
||||||
|
|
||||||
public UserPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
|
private static final String MAP_ROLES_SCOPE="map-roles";
|
||||||
|
private static final String IMPERSONATE_SCOPE="impersonate";
|
||||||
|
private static final String USER_IMPERSONATED_SCOPE="user-impersonated";
|
||||||
|
private static final String MANAGE_GROUP_MEMBERSHIP_SCOPE="manage-group-membership";
|
||||||
|
private static final String MAP_ROLES_PERMISSION_USERS = "map-roles.permission.users";
|
||||||
|
private static final String ADMIN_IMPERSONATING_PERMISSION = "admin-impersonating.permission.users";
|
||||||
|
private static final String USER_IMPERSONATED_PERMISSION = "user-impersonated.permission.users";
|
||||||
|
private static final String MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS = "manage-group-membership.permission.users";
|
||||||
|
private static final String MANAGE_PERMISSION_USERS = "manage.permission.users";
|
||||||
|
private static final String VIEW_PERMISSION_USERS = "view.permission.users";
|
||||||
|
private static final String USERS_RESOURCE = "Users";
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private final AuthorizationProvider authz;
|
||||||
|
private final MgmtPermissions root;
|
||||||
|
private final PolicyStore policyStore;
|
||||||
|
private final ResourceStore resourceStore;
|
||||||
|
private boolean grantIfNoPermission = false;
|
||||||
|
|
||||||
|
UserPermissions(KeycloakSession session, AuthorizationProvider authz, MgmtPermissions root) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.realm = realm;
|
|
||||||
this.authz = authz;
|
this.authz = authz;
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
policyStore = authz.getStoreFactory().getPolicyStore();
|
||||||
|
resourceStore = authz.getStoreFactory().getResourceStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,9 +98,9 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
Scope userImpersonatedScope = root.initializeRealmScope(USER_IMPERSONATED_SCOPE);
|
Scope userImpersonatedScope = root.initializeRealmScope(USER_IMPERSONATED_SCOPE);
|
||||||
Scope manageGroupMembershipScope = root.initializeRealmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
|
Scope manageGroupMembershipScope = root.initializeRealmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
|
||||||
|
|
||||||
Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
Resource usersResource = resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
if (usersResource == null) {
|
if (usersResource == null) {
|
||||||
usersResource = authz.getStoreFactory().getResourceStore().create(USERS_RESOURCE, server, server.getId());
|
usersResource = resourceStore.create(USERS_RESOURCE, server, server.getId());
|
||||||
Set<Scope> scopeset = new HashSet<>();
|
Set<Scope> scopeset = new HashSet<>();
|
||||||
scopeset.add(manageScope);
|
scopeset.add(manageScope);
|
||||||
scopeset.add(viewScope);
|
scopeset.add(viewScope);
|
||||||
|
@ -100,27 +110,27 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
scopeset.add(userImpersonatedScope);
|
scopeset.add(userImpersonatedScope);
|
||||||
usersResource.updateScopes(scopeset);
|
usersResource.updateScopes(scopeset);
|
||||||
}
|
}
|
||||||
Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
|
Policy managePermission = policyStore.findByName(MANAGE_PERMISSION_USERS, server.getId());
|
||||||
if (managePermission == null) {
|
if (managePermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, MANAGE_PERMISSION_USERS, usersResource, manageScope);
|
Helper.addEmptyScopePermission(authz, server, MANAGE_PERMISSION_USERS, usersResource, manageScope);
|
||||||
}
|
}
|
||||||
Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
|
Policy viewPermission = policyStore.findByName(VIEW_PERMISSION_USERS, server.getId());
|
||||||
if (viewPermission == null) {
|
if (viewPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, VIEW_PERMISSION_USERS, usersResource, viewScope);
|
Helper.addEmptyScopePermission(authz, server, VIEW_PERMISSION_USERS, usersResource, viewScope);
|
||||||
}
|
}
|
||||||
Policy mapRolesPermission = authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
|
Policy mapRolesPermission = policyStore.findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
|
||||||
if (mapRolesPermission == null) {
|
if (mapRolesPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, MAP_ROLES_PERMISSION_USERS, usersResource, mapRolesScope);
|
Helper.addEmptyScopePermission(authz, server, MAP_ROLES_PERMISSION_USERS, usersResource, mapRolesScope);
|
||||||
}
|
}
|
||||||
Policy membershipPermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
|
Policy membershipPermission = policyStore.findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
|
||||||
if (membershipPermission == null) {
|
if (membershipPermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, usersResource, manageGroupMembershipScope);
|
Helper.addEmptyScopePermission(authz, server, MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, usersResource, manageGroupMembershipScope);
|
||||||
}
|
}
|
||||||
Policy impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
|
Policy impersonatePermission = policyStore.findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
|
||||||
if (impersonatePermission == null) {
|
if (impersonatePermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, ADMIN_IMPERSONATING_PERMISSION, usersResource, impersonateScope);
|
Helper.addEmptyScopePermission(authz, server, ADMIN_IMPERSONATING_PERMISSION, usersResource, impersonateScope);
|
||||||
}
|
}
|
||||||
impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
|
impersonatePermission = policyStore.findByName(USER_IMPERSONATED_PERMISSION, server.getId());
|
||||||
if (impersonatePermission == null) {
|
if (impersonatePermission == null) {
|
||||||
Helper.addEmptyScopePermission(authz, server, USER_IMPERSONATED_PERMISSION, usersResource, userImpersonatedScope);
|
Helper.addEmptyScopePermission(authz, server, USER_IMPERSONATED_PERMISSION, usersResource, userImpersonatedScope);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +154,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return false;
|
if (server == null) return false;
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
Resource resource = resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
if (resource == null) return false;
|
if (resource == null) return false;
|
||||||
|
|
||||||
Policy policy = managePermission();
|
Policy policy = managePermission();
|
||||||
|
@ -161,45 +171,6 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deletePermissionSetup() {
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
|
||||||
if (server == null) return;
|
|
||||||
Policy policy = managePermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
policy = viewPermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
policy = mapRolesPermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
policy = manageGroupMembershipPermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
policy = adminImpersonatingPermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
policy = userImpersonatedPermission();
|
|
||||||
if (policy != null) {
|
|
||||||
authz.getStoreFactory().getPolicyStore().delete(policy.getId());
|
|
||||||
|
|
||||||
}
|
|
||||||
Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
|
||||||
if (usersResource != null) {
|
|
||||||
authz.getStoreFactory().getResourceStore().delete(usersResource.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canManageDefault() {
|
public boolean canManageDefault() {
|
||||||
return root.hasOneAdminRole(AdminRoles.MANAGE_USERS);
|
return root.hasOneAdminRole(AdminRoles.MANAGE_USERS);
|
||||||
}
|
}
|
||||||
|
@ -209,48 +180,40 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return null;
|
if (server == null) return null;
|
||||||
|
|
||||||
return authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
return resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy managePermission() {
|
public Policy managePermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(MANAGE_PERMISSION_USERS, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy viewPermission() {
|
public Policy viewPermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(VIEW_PERMISSION_USERS, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy manageGroupMembershipPermission() {
|
public Policy manageGroupMembershipPermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy mapRolesPermission() {
|
public Policy mapRolesPermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(MAP_ROLES_PERMISSION_USERS, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy adminImpersonatingPermission() {
|
public Policy adminImpersonatingPermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(ADMIN_IMPERSONATING_PERMISSION, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy userImpersonatedPermission() {
|
public Policy userImpersonatedPermission() {
|
||||||
ResourceServer server = root.realmResourceServer();
|
return policyStore.findByName(USER_IMPERSONATED_PERMISSION, root.realmResourceServer().getId());
|
||||||
return authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is admin allowed to manage all users? In Authz terms, does the admin have the "manage" scope for the Users Authz resource?
|
* Is admin allowed to manage all users? In Authz terms, does the admin have the "manage" scope for the Users Authz resource?
|
||||||
*
|
*
|
||||||
|
@ -266,31 +229,15 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canManage() {
|
public boolean canManage() {
|
||||||
if (canManageDefault()) return true;
|
if (canManageDefault()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!root.isAdminSameRealm()) {
|
if (!root.isAdminSameRealm()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(MgmtPermissions.MANAGE_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmManageScope();
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -319,63 +266,6 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface EvaluateGroup {
|
|
||||||
boolean evaluate(GroupModel group);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean evaluateGroups(UserModel user, EvaluateGroup eval) {
|
|
||||||
for (GroupModel group : user.getGroups()) {
|
|
||||||
if (eval.evaluate(group)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean evaluateHierarchy(UserModel user, EvaluateGroup eval) {
|
|
||||||
Set<GroupModel> visited = new HashSet<>();
|
|
||||||
for (GroupModel group : user.getGroups()) {
|
|
||||||
if (evaluateHierarchy(eval, group, visited)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean evaluateHierarchy(EvaluateGroup eval, GroupModel group, Set<GroupModel> visited) {
|
|
||||||
if (visited.contains(group)) return false;
|
|
||||||
if (eval.evaluate(group)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
visited.add(group);
|
|
||||||
if (group.getParent() == null) return false;
|
|
||||||
return evaluateHierarchy(eval, group.getParent(), visited);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canManageByGroup(UserModel user) {
|
|
||||||
/* no inheritance
|
|
||||||
return evaluateGroups(user,
|
|
||||||
(group) -> root.groups().canViewMembers(group)
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* inheritance
|
|
||||||
*/
|
|
||||||
return evaluateHierarchy(user, (group) -> root.groups().canManageMembers(group));
|
|
||||||
|
|
||||||
}
|
|
||||||
private boolean canViewByGroup(UserModel user) {
|
|
||||||
/* no inheritance
|
|
||||||
return evaluateGroups(user,
|
|
||||||
(group) -> root.groups().canViewMembers(group)
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* inheritance
|
|
||||||
*/
|
|
||||||
return evaluateHierarchy(user, (group) -> root.groups().canViewMembers(group));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canViewDefault() {
|
|
||||||
return root.hasOneAdminRole(AdminRoles.MANAGE_USERS, AdminRoles.VIEW_USERS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canQuery() {
|
public boolean canQuery() {
|
||||||
return canView() || root.hasOneAdminRole(AdminRoles.QUERY_USERS);
|
return canView() || root.hasOneAdminRole(AdminRoles.QUERY_USERS);
|
||||||
|
@ -388,21 +278,6 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canQuery(UserModel user) {
|
|
||||||
return canView(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requireQuery(UserModel user) {
|
|
||||||
if (!canQuery(user)) {
|
|
||||||
throw new ForbiddenException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is admin allowed to view all users? In Authz terms, does the admin have the "view" scope for the Users Authz resource?
|
* Is admin allowed to view all users? In Authz terms, does the admin have the "view" scope for the Users Authz resource?
|
||||||
*
|
*
|
||||||
|
@ -418,34 +293,15 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean canView() {
|
public boolean canView() {
|
||||||
if (canViewDefault()) return true;
|
if (canViewDefault() || canManageDefault()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!root.isAdminSameRealm()) {
|
if (!root.isAdminSameRealm()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasViewPermission() || canManage();
|
return hasPermission(MgmtPermissions.VIEW_SCOPE, MgmtPermissions.MANAGE_SCOPE);
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasViewPermission() {
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
|
||||||
if (server == null) return canViewDefault();
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
|
||||||
if (resource == null) return canViewDefault();
|
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
|
|
||||||
if (policy == null) {
|
|
||||||
return canViewDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return canViewDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmViewScope();
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -505,15 +361,20 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isImpersonatable(UserModel user) {
|
public boolean isImpersonatable(UserModel user) {
|
||||||
Identity userIdentity = new UserModelIdentity(root.realm, user);
|
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
ResourceServer server = root.realmResourceServer();
|
||||||
if (server == null) return true;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
if (server == null) {
|
||||||
if (resource == null) return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
|
|
||||||
|
if (resource == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
|
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
|
||||||
|
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -524,8 +385,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope scope = root.realmScope(USER_IMPERSONATED_SCOPE);
|
return hasPermission(new DefaultEvaluationContext(new UserModelIdentity(root.realm, user), session), USER_IMPERSONATED_SCOPE);
|
||||||
return root.evaluatePermission(resource, scope, server, userIdentity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -538,31 +398,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
return canImpersonate(new DefaultEvaluationContext(identity, session));
|
||||||
return canImpersonate(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean canImpersonate(EvaluationContext context) {
|
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmScope(IMPERSONATE_SCOPE);
|
|
||||||
return root.evaluatePermission(resource, scope, server, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -591,25 +427,7 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(MAP_ROLES_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmScope(MAP_ROLES_SCOPE);
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,7 +439,6 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canManageGroupMembership(UserModel user) {
|
public boolean canManageGroupMembership(UserModel user) {
|
||||||
if (canManage(user)) return true;
|
if (canManage(user)) return true;
|
||||||
|
@ -630,26 +447,13 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceServer server = root.realmResourceServer();
|
return hasPermission(MANAGE_GROUP_MEMBERSHIP_SCOPE);
|
||||||
if (server == null) return false;
|
|
||||||
|
|
||||||
Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
|
}
|
||||||
if (resource == null) return false;
|
|
||||||
|
|
||||||
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
|
|
||||||
if (policy == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
// if no policies attached to permission then just do default behavior
|
|
||||||
if (associatedPolicies == null || associatedPolicies.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Scope scope = root.realmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
|
|
||||||
return root.evaluatePermission(resource, scope, server);
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantIfNoPermission(boolean grantIfNoPermission) {
|
||||||
|
this.grantIfNoPermission = grantIfNoPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -660,9 +464,113 @@ class UserPermissions implements UserPermissionEvaluator, UserPermissionManageme
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(String... scopes) {
|
||||||
|
return hasPermission(null, scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission(EvaluationContext context, String... scopes) {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
|
||||||
|
if (server == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource resource = resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
|
List<String> expectedScopes = Arrays.asList(scopes);
|
||||||
|
|
||||||
|
if (resource == null) {
|
||||||
|
return grantIfNoPermission && expectedScopes.contains(MgmtPermissions.MANAGE_SCOPE) && expectedScopes.contains(MgmtPermissions.VIEW_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Permission> permissions;
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server);
|
||||||
|
} else {
|
||||||
|
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Permission permission : permissions) {
|
||||||
|
for (String scope : permission.getScopes()) {
|
||||||
|
if (expectedScopes.contains(scope)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePermissionSetup() {
|
||||||
|
ResourceServer server = root.realmResourceServer();
|
||||||
|
if (server == null) return;
|
||||||
|
Policy policy = managePermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
policy = viewPermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
policy = mapRolesPermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
policy = manageGroupMembershipPermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
policy = adminImpersonatingPermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
policy = userImpersonatedPermission();
|
||||||
|
if (policy != null) {
|
||||||
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
|
}
|
||||||
|
Resource usersResource = resourceStore.findByName(USERS_RESOURCE, server.getId());
|
||||||
|
if (usersResource != null) {
|
||||||
|
resourceStore.delete(usersResource.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canImpersonate(EvaluationContext context) {
|
||||||
|
return hasPermission(context, IMPERSONATE_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean evaluateHierarchy(UserModel user, Predicate<GroupModel> eval) {
|
||||||
|
Set<GroupModel> visited = new HashSet<>();
|
||||||
|
for (GroupModel group : user.getGroups()) {
|
||||||
|
if (evaluateHierarchy(eval, group, visited)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean evaluateHierarchy(Predicate<GroupModel> eval, GroupModel group, Set<GroupModel> visited) {
|
||||||
|
if (visited.contains(group)) return false;
|
||||||
|
if (eval.test(group)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
visited.add(group);
|
||||||
|
if (group.getParent() == null) return false;
|
||||||
|
return evaluateHierarchy(eval, group.getParent(), visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canManageByGroup(UserModel user) {
|
||||||
|
return evaluateHierarchy(user, (group) -> root.groups().canManageMembers(group));
|
||||||
|
|
||||||
|
}
|
||||||
|
private boolean canViewByGroup(UserModel user) {
|
||||||
|
return evaluateHierarchy(user, (group) -> root.groups().getGroupsWithViewPermission(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canViewDefault() {
|
||||||
|
return root.hasOneAdminRole(AdminRoles.MANAGE_USERS, AdminRoles.VIEW_USERS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,6 @@ public class UserPropertyFileStorage implements UserLookupProvider, UserStorageP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
public List<UserModel> searchForUser(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
|
||||||
if (attributes.size() != 1) return Collections.EMPTY_LIST;
|
|
||||||
String username = attributes.get(UserModel.USERNAME);
|
String username = attributes.get(UserModel.USERNAME);
|
||||||
if (username == null) return Collections.EMPTY_LIST;
|
if (username == null) return Collections.EMPTY_LIST;
|
||||||
return searchForUser(username, realm, firstResult, maxResults);
|
return searchForUser(username, realm, firstResult, maxResults);
|
||||||
|
|
|
@ -16,16 +16,19 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.admin;
|
package org.keycloak.testsuite.admin;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.jboss.arquillian.container.test.api.Deployment;
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.client.admin.cli.util.ConfigUtil;
|
import org.keycloak.client.admin.cli.util.ConfigUtil;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.models.*;
|
import org.keycloak.models.*;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||||
|
@ -41,6 +44,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.GroupPermissionManagement;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.ProfileAssume;
|
import org.keycloak.testsuite.ProfileAssume;
|
||||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||||
|
@ -898,6 +902,92 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
|
||||||
Assert.assertNotNull(client.realm("master").roles().get("offline_access"));
|
Assert.assertNotNull(client.realm("master").roles().get("offline_access"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserPagination() {
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
|
||||||
|
session.getContext().setRealm(realm);
|
||||||
|
|
||||||
|
GroupModel customerAGroup = session.realms().createGroup(realm, "Customer A");
|
||||||
|
UserModel customerAManager = session.users().addUser(realm, "customer-a-manager");
|
||||||
|
session.userCredentialManager().updateCredential(realm, customerAManager, UserCredentialModel.password("password"));
|
||||||
|
customerAManager.joinGroup(customerAGroup);
|
||||||
|
ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
||||||
|
customerAManager.grantRole(realmAdminClient.getRole(AdminRoles.QUERY_USERS));
|
||||||
|
customerAManager.setEnabled(true);
|
||||||
|
UserModel regularAdminUser = session.users().addUser(realm, "regular-admin-user");
|
||||||
|
session.userCredentialManager().updateCredential(realm, regularAdminUser, UserCredentialModel.password("password"));
|
||||||
|
regularAdminUser.grantRole(realmAdminClient.getRole(AdminRoles.VIEW_USERS));
|
||||||
|
regularAdminUser.setEnabled(true);
|
||||||
|
|
||||||
|
AdminPermissionManagement management = AdminPermissions.management(session, realm);
|
||||||
|
|
||||||
|
GroupPermissionManagement groupPermission = management.groups();
|
||||||
|
|
||||||
|
groupPermission.setPermissionsEnabled(customerAGroup, true);
|
||||||
|
|
||||||
|
UserPolicyRepresentation userPolicyRepresentation = new UserPolicyRepresentation();
|
||||||
|
|
||||||
|
userPolicyRepresentation.setName("Only " + customerAManager.getUsername());
|
||||||
|
userPolicyRepresentation.addUser(customerAManager.getId());
|
||||||
|
|
||||||
|
Policy policy = groupPermission.viewMembersPermission(customerAGroup);
|
||||||
|
|
||||||
|
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
|
||||||
|
|
||||||
|
Policy userPolicy = provider.getStoreFactory().getPolicyStore().create(userPolicyRepresentation, management.realmResourceServer());
|
||||||
|
|
||||||
|
policy.addAssociatedPolicy(RepresentationToModel.toModel(userPolicyRepresentation, provider, userPolicy));
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
UserModel userModel = session.users().addUser(realm, "a" + i);
|
||||||
|
userModel.setFirstName("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 20; i < 40; i++) {
|
||||||
|
UserModel userModel = session.users().addUser(realm, "b" + i);
|
||||||
|
userModel.setFirstName("test");
|
||||||
|
userModel.joinGroup(customerAGroup);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Keycloak client = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
||||||
|
"test", "customer-a-manager", "password", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
|
||||||
|
List<UserRepresentation> result = client.realm("test").users().search(null, "test", null, null, -1, 20);
|
||||||
|
|
||||||
|
Assert.assertEquals(20, result.size());
|
||||||
|
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("b"))));
|
||||||
|
|
||||||
|
result = client.realm("test").users().search(null, "test", null, null, 20, 40);
|
||||||
|
|
||||||
|
Assert.assertEquals(0, result.size());
|
||||||
|
|
||||||
|
client = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
||||||
|
"test", "regular-admin-user", "password", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
|
||||||
|
result = client.realm("test").users().search(null, "test", null, null, -1, 20);
|
||||||
|
|
||||||
|
Assert.assertEquals(20, result.size());
|
||||||
|
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("a"))));
|
||||||
|
|
||||||
|
client.realm("test").users().search(null, null, null, null, -1, -1);
|
||||||
|
|
||||||
|
Assert.assertEquals(20, result.size());
|
||||||
|
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("a"))));
|
||||||
|
|
||||||
|
client = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
||||||
|
"test", "customer-a-manager", "password", Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
|
|
||||||
|
result = client.realm("test").users().search(null, null, null, null, -1, 20);
|
||||||
|
|
||||||
|
Assert.assertEquals(20, result.size());
|
||||||
|
Assert.assertThat(result, Matchers.everyItem(Matchers.hasProperty("username", Matchers.startsWith("b"))));
|
||||||
|
|
||||||
|
result = client.realm("test").users().search("a", -1, 20, false);
|
||||||
|
|
||||||
|
Assert.assertEquals(0, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,24 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void testMigrationTo4_6_0(boolean supportsAuthzService, boolean checkMigrationData) {
|
||||||
|
if (supportsAuthzService && checkMigrationData) {
|
||||||
|
testGroupPolicyTypeFineGrainedAdminPermission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGroupPolicyTypeFineGrainedAdminPermission() {
|
||||||
|
ClientsResource clients = migrationRealm.clients();
|
||||||
|
ClientRepresentation clientRepresentation = clients.findByClientId("realm-management").get(0);
|
||||||
|
List<ResourceRepresentation> resources = clients.get(clientRepresentation.getId()).authorization().resources().resources();
|
||||||
|
|
||||||
|
assertEquals(5, resources.size());
|
||||||
|
|
||||||
|
for (ResourceRepresentation resource : resources) {
|
||||||
|
assertEquals("Group", resource.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void testCliConsoleScopeSize(RealmResource realm) {
|
private void testCliConsoleScopeSize(RealmResource realm) {
|
||||||
ClientRepresentation cli = realm.clients().findByClientId(Constants.ADMIN_CLI_CLIENT_ID).get(0);
|
ClientRepresentation cli = realm.clients().findByClientId(Constants.ADMIN_CLI_CLIENT_ID).get(0);
|
||||||
ClientRepresentation console = realm.clients().findByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID).get(0);
|
ClientRepresentation console = realm.clients().findByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID).get(0);
|
||||||
|
@ -543,12 +561,13 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
testMigrationTo3_4_2();
|
testMigrationTo3_4_2();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testMigrationTo4_x(boolean supportsAuthzServices) {
|
protected void testMigrationTo4_x(boolean supportsAuthzServices, boolean checkMigrationData) {
|
||||||
testMigrationTo4_0_0();
|
testMigrationTo4_0_0();
|
||||||
testMigrationTo4_2_0(supportsAuthzServices);
|
testMigrationTo4_2_0(supportsAuthzServices);
|
||||||
|
testMigrationTo4_6_0(supportsAuthzServices, checkMigrationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testMigrationTo4_x() {
|
protected void testMigrationTo4_x() {
|
||||||
testMigrationTo4_x(true);
|
testMigrationTo4_x(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
||||||
testMigrationTo2_5_0();
|
testMigrationTo2_5_0();
|
||||||
//testMigrationTo2_5_1(); // Offline tokens migration is skipped for JSON
|
//testMigrationTo2_5_1(); // Offline tokens migration is skipped for JSON
|
||||||
testMigrationTo3_x();
|
testMigrationTo3_x();
|
||||||
testMigrationTo4_x(false);
|
testMigrationTo4_x(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
||||||
@Test
|
@Test
|
||||||
public void migration2_5_5Test() throws Exception {
|
public void migration2_5_5Test() throws Exception {
|
||||||
testMigrationTo3_x();
|
testMigrationTo3_x();
|
||||||
testMigrationTo4_x();
|
testMigrationTo4_x(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void migration3_4_3Test() throws Exception {
|
public void migration3_4_3Test() throws Exception {
|
||||||
testMigrationTo4_x();
|
testMigrationTo4_x(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigratedData(false);
|
testMigratedData(false);
|
||||||
testMigrationTo2_x();
|
testMigrationTo2_x();
|
||||||
testMigrationTo3_x();
|
testMigrationTo3_x();
|
||||||
testMigrationTo4_x(false);
|
testMigrationTo4_x(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -858,7 +858,33 @@
|
||||||
} ],
|
} ],
|
||||||
"useTemplateConfig" : false,
|
"useTemplateConfig" : false,
|
||||||
"useTemplateScope" : false,
|
"useTemplateScope" : false,
|
||||||
"useTemplateMappers" : false
|
"useTemplateMappers" : false,
|
||||||
|
"serviceAccountsEnabled": true,
|
||||||
|
"authorizationServicesEnabled": true,
|
||||||
|
"authorizationSettings": {
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"name": "group.resource.a",
|
||||||
|
"scopes": ["view-members"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "group.resource.b",
|
||||||
|
"scopes": ["view-members"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "group.resource.c",
|
||||||
|
"scopes": ["view-members"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "group.resource.d",
|
||||||
|
"scopes": ["view-members"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "group.resource.e",
|
||||||
|
"scopes": ["view-members"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
"id" : "35d39054-e7e5-4dbb-8948-1738094679e8",
|
"id" : "35d39054-e7e5-4dbb-8948-1738094679e8",
|
||||||
"clientId" : "security-admin-console",
|
"clientId" : "security-admin-console",
|
||||||
|
|
|
@ -235,12 +235,8 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
UserSessionStatus userSessionStatus = retrieveSessionStatus();
|
UserSessionStatus userSessionStatus = retrieveSessionStatus();
|
||||||
IDToken idToken = userSessionStatus.getIdToken();
|
IDToken idToken = userSessionStatus.getIdToken();
|
||||||
KeycloakSession samlServerSession = brokerServerRule.startSession();
|
KeycloakSession samlServerSession = brokerServerRule.startSession();
|
||||||
try {
|
RealmModel brokerRealm = samlServerSession.realms().getRealm("realm-with-broker");
|
||||||
RealmModel brokerRealm = samlServerSession.realms().getRealm("realm-with-broker");
|
return samlServerSession.users().getUserById(idToken.getSubject(), brokerRealm);
|
||||||
return samlServerSession.users().getUserById(idToken.getSubject(), brokerRealm);
|
|
||||||
} finally {
|
|
||||||
brokerServerRule.stopSession(samlServerSession, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doAfterProviderAuthentication() {
|
protected void doAfterProviderAuthentication() {
|
||||||
|
|
Loading…
Reference in a new issue