Do not cache policies if they no longer exist (#12797)
Closes #12657 Co-authored-by: Michal Hajas <mhajas@redhat.com> Co-authored-by: Michal Hajas <mhajas@redhat.com>
This commit is contained in:
parent
2b9a0bff51
commit
2cc4b54404
11 changed files with 248 additions and 19 deletions
|
@ -19,7 +19,7 @@ package org.keycloak.authorization.policy.provider.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -67,7 +67,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
|
||||||
@Override
|
@Override
|
||||||
public ClientPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
|
public ClientPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
|
||||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||||
representation.setClients(new HashSet<>(Arrays.asList(getClients(policy))));
|
representation.setClients(getClients(policy));
|
||||||
return representation;
|
return representation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
|
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||||
updateClients(policy, new HashSet<>(Arrays.asList(getClients(policy))), authorization);
|
updateClients(policy, getClients(policy), authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -193,17 +193,17 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] getClients(Policy policy) {
|
private Set<String> getClients(Policy policy) {
|
||||||
String clients = policy.getConfig().get("clients");
|
String clients = policy.getConfig().get("clients");
|
||||||
|
|
||||||
if (clients != null) {
|
if (clients != null) {
|
||||||
try {
|
try {
|
||||||
return JsonSerialization.readValue(clients.getBytes(), String[].class);
|
return JsonSerialization.readValue(clients, Set.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Could not parse clients [" + clients + "] from policy config [" + policy.getName() + "].", e);
|
throw new RuntimeException("Could not parse clients [" + clients + "] from policy config [" + policy.getName() + "].", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String[]{};
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.authorization.policy.provider.clientscope;
|
||||||
import java.io.IOException;
|
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.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -151,9 +152,15 @@ public class ClientScopePolicyProviderFactory implements PolicyProviderFactory<C
|
||||||
ClientScopePolicyRepresentation representation = new ClientScopePolicyRepresentation();
|
ClientScopePolicyRepresentation representation = new ClientScopePolicyRepresentation();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
representation
|
String clientScopes = policy.getConfig().get("clientScopes");
|
||||||
.setClientScopes(new HashSet<>(Arrays.asList(JsonSerialization.readValue(policy.getConfig().get("clientScopes"),
|
|
||||||
ClientScopePolicyRepresentation.ClientScopeDefinition[].class))));
|
if (clientScopes == null) {
|
||||||
|
representation.setClientScopes(Collections.emptySet());
|
||||||
|
} else {
|
||||||
|
representation
|
||||||
|
.setClientScopes(new HashSet<>(Arrays.asList(JsonSerialization.readValue(clientScopes,
|
||||||
|
ClientScopePolicyRepresentation.ClientScopeDefinition[].class))));
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Failed to deserialize client scopes", e);
|
throw new RuntimeException("Failed to deserialize client scopes", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.authorization.policy.provider.group;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -213,7 +214,13 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
|
||||||
policy.setConfig(config);
|
policy.setConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashSet<GroupPolicyRepresentation.GroupDefinition> getGroupsDefinition(Map<String, String> config) throws IOException {
|
private Set<GroupPolicyRepresentation.GroupDefinition> getGroupsDefinition(Map<String, String> config) throws IOException {
|
||||||
return new HashSet<>(Arrays.asList(JsonSerialization.readValue(config.get("groups"), GroupPolicyRepresentation.GroupDefinition[].class)));
|
String groups = config.get("groups");
|
||||||
|
|
||||||
|
if (groups == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HashSet<>(Arrays.asList(JsonSerialization.readValue(groups, GroupPolicyRepresentation.GroupDefinition[].class)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.util.JsonSerialization;
|
||||||
import java.io.IOException;
|
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.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -78,7 +79,14 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
|
||||||
RolePolicyRepresentation representation = new RolePolicyRepresentation();
|
RolePolicyRepresentation representation = new RolePolicyRepresentation();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
representation.setRoles(new HashSet<>(Arrays.asList(JsonSerialization.readValue(policy.getConfig().get("roles"), RolePolicyRepresentation.RoleDefinition[].class))));
|
String roles = policy.getConfig().get("roles");
|
||||||
|
|
||||||
|
if (roles == null) {
|
||||||
|
representation.setRoles(Collections.emptySet());
|
||||||
|
} else {
|
||||||
|
representation.setRoles(new HashSet<>(
|
||||||
|
Arrays.asList(JsonSerialization.readValue(roles, RolePolicyRepresentation.RoleDefinition[].class))));
|
||||||
|
}
|
||||||
} catch (IOException cause) {
|
} catch (IOException cause) {
|
||||||
throw new RuntimeException("Failed to deserialize roles", cause);
|
throw new RuntimeException("Failed to deserialize roles", cause);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.keycloak.authorization.policy.provider.user;
|
package org.keycloak.authorization.policy.provider.user;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -71,7 +72,13 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
|
||||||
UserPolicyRepresentation representation = new UserPolicyRepresentation();
|
UserPolicyRepresentation representation = new UserPolicyRepresentation();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
representation.setUsers(JsonSerialization.readValue(policy.getConfig().get("users"), Set.class));
|
String users = policy.getConfig().get("users");
|
||||||
|
|
||||||
|
if (users == null) {
|
||||||
|
representation.setUsers(Collections.emptySet());
|
||||||
|
} else {
|
||||||
|
representation.setUsers(JsonSerialization.readValue(users, Set.class));
|
||||||
|
}
|
||||||
} catch (IOException cause) {
|
} catch (IOException cause) {
|
||||||
throw new RuntimeException("Failed to deserialize roles", cause);
|
throw new RuntimeException("Failed to deserialize roles", cause);
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,10 +206,14 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
||||||
}
|
}
|
||||||
if (associatedPolicies != null) return associatedPolicies;
|
if (associatedPolicies != null) return associatedPolicies;
|
||||||
associatedPolicies = new HashSet<>();
|
associatedPolicies = new HashSet<>();
|
||||||
PolicyStore policyStore = cacheSession.getPolicyStore();
|
PolicyStore policyStore = cacheSession.getPolicyStoreDelegate();
|
||||||
String resourceServerId = cached.getResourceServerId();
|
String resourceServerId = cached.getResourceServerId();
|
||||||
for (String id : cached.getAssociatedPoliciesIds(modelSupplier)) {
|
for (String id : cached.getAssociatedPoliciesIds(modelSupplier)) {
|
||||||
Policy policy = policyStore.findById(InfinispanCacheStoreFactoryProviderFactory.NULL_REALM, cacheSession.getResourceServerStore().findById(InfinispanCacheStoreFactoryProviderFactory.NULL_REALM, resourceServerId), id);
|
Policy policy = policyStore.findById(InfinispanCacheStoreFactoryProviderFactory.NULL_REALM, cacheSession.getResourceServerStore().findById(InfinispanCacheStoreFactoryProviderFactory.NULL_REALM, resourceServerId), id);
|
||||||
|
if (policy == null) {
|
||||||
|
// probably because the policy was removed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
cacheSession.cachePolicy(policy);
|
cacheSession.cachePolicy(policy);
|
||||||
associatedPolicies.add(policy);
|
associatedPolicies.add(policy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,8 +199,9 @@ public class JPAPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
List<String> result = paginateQuery(query, firstResult, maxResults).getResultList();
|
List<String> result = paginateQuery(query, firstResult, maxResults).getResultList();
|
||||||
List<Policy> list = new LinkedList<>();
|
List<Policy> list = new LinkedList<>();
|
||||||
|
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
||||||
for (String id : result) {
|
for (String id : result) {
|
||||||
Policy policy = provider.getStoreFactory().getPolicyStore().findById(JPAAuthorizationStoreFactory.NULL_REALM, resourceServer, id);
|
Policy policy = policyStore.findById(JPAAuthorizationStoreFactory.NULL_REALM, resourceServer, id);
|
||||||
if (Objects.nonNull(policy)) {
|
if (Objects.nonNull(policy)) {
|
||||||
list.add(policy);
|
list.add(policy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,10 +162,8 @@ public class MapPolicyStore implements PolicyStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
|
return tx.read(withCriteria(mcb).pagination(firstResult, maxResults, SearchableFields.NAME))
|
||||||
.map(MapPolicyEntity::getId)
|
.map(entityToAdapterFunc(realm, resourceServer))
|
||||||
// We need to go through cache
|
.collect(Collectors.toList());
|
||||||
.map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(realm, resourceServer, id))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultModelCriteria<Policy> filterEntryToDefaultModelCriteria(Map.Entry<Policy.FilterOption, String[]> entry) {
|
private DefaultModelCriteria<Policy> filterEntryToDefaultModelCriteria(Map.Entry<Policy.FilterOption, String[]> entry) {
|
||||||
|
|
|
@ -22,6 +22,9 @@ import org.junit.Assert;
|
||||||
import org.keycloak.Config.Scope;
|
import org.keycloak.Config.Scope;
|
||||||
import org.keycloak.authorization.AuthorizationSpi;
|
import org.keycloak.authorization.AuthorizationSpi;
|
||||||
import org.keycloak.authorization.DefaultAuthorizationProviderFactory;
|
import org.keycloak.authorization.DefaultAuthorizationProviderFactory;
|
||||||
|
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||||
|
import org.keycloak.authorization.policy.provider.PolicySpi;
|
||||||
|
import org.keycloak.authorization.policy.provider.permission.UMAPolicyProviderFactory;
|
||||||
import org.keycloak.authorization.store.StoreFactorySpi;
|
import org.keycloak.authorization.store.StoreFactorySpi;
|
||||||
import org.keycloak.cluster.ClusterSpi;
|
import org.keycloak.cluster.ClusterSpi;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
@ -213,6 +216,7 @@ public abstract class KeycloakModelTest {
|
||||||
|
|
||||||
private static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
private static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
||||||
.add(AuthorizationSpi.class)
|
.add(AuthorizationSpi.class)
|
||||||
|
.add(PolicySpi.class)
|
||||||
.add(ClientScopeSpi.class)
|
.add(ClientScopeSpi.class)
|
||||||
.add(ClientSpi.class)
|
.add(ClientSpi.class)
|
||||||
.add(ComponentFactorySpi.class)
|
.add(ComponentFactorySpi.class)
|
||||||
|
@ -235,6 +239,7 @@ public abstract class KeycloakModelTest {
|
||||||
private static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
private static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
||||||
.add(ComponentFactoryProviderFactory.class)
|
.add(ComponentFactoryProviderFactory.class)
|
||||||
.add(DefaultAuthorizationProviderFactory.class)
|
.add(DefaultAuthorizationProviderFactory.class)
|
||||||
|
.add(PolicyProviderFactory.class)
|
||||||
.add(DefaultExecutorsProviderFactory.class)
|
.add(DefaultExecutorsProviderFactory.class)
|
||||||
.add(DeploymentStateProviderFactory.class)
|
.add(DeploymentStateProviderFactory.class)
|
||||||
.add(DatastoreProviderFactory.class)
|
.add(DatastoreProviderFactory.class)
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.testsuite.model.authz;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.model.Policy;
|
||||||
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientProvider;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RealmProvider;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||||
|
import org.keycloak.testsuite.model.KeycloakModelTest;
|
||||||
|
import org.keycloak.testsuite.model.RequireProvider;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
|
||||||
|
@RequireProvider(AuthorizationProvider.class)
|
||||||
|
@RequireProvider(RealmProvider.class)
|
||||||
|
@RequireProvider(ClientProvider.class)
|
||||||
|
public class ConcurrentAuthzTest extends KeycloakModelTest {
|
||||||
|
|
||||||
|
private String realmId;
|
||||||
|
private String resourceServerId;
|
||||||
|
private String resourceId;
|
||||||
|
private String adminId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createEnvironment(KeycloakSession s) {
|
||||||
|
RealmModel realm = s.realms().createRealm("test");
|
||||||
|
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
|
||||||
|
|
||||||
|
realmId = realm.getId();
|
||||||
|
|
||||||
|
ClientModel client = s.clients().addClient(realm, "my-server");
|
||||||
|
|
||||||
|
AuthorizationProvider authorization = s.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().create(client);
|
||||||
|
resourceServerId = rs.getId();
|
||||||
|
resourceId = aStore.getResourceStore().create(rs, "myResource", client.getClientId()).getId();
|
||||||
|
aStore.getScopeStore().create(rs, "read");
|
||||||
|
|
||||||
|
adminId = s.users().addUser(realm, "admin").getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cleanEnvironment(KeycloakSession s) {
|
||||||
|
s.realms().removeRealm(realmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isUseSameKeycloakSessionFactoryForAllThreads() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPermissionRemoved() {
|
||||||
|
IntStream.range(0, 500).parallel().forEach(index -> {
|
||||||
|
String permissionId = withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().findById(realm, resourceServerId);
|
||||||
|
|
||||||
|
UserModel u = session.users().addUser(realm, "user" + index);
|
||||||
|
|
||||||
|
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||||
|
permission.setName(KeycloakModelUtils.generateId());
|
||||||
|
permission.addUser(u.getUsername());
|
||||||
|
permission.addScope("read");
|
||||||
|
|
||||||
|
permission.addResource(resourceId);
|
||||||
|
permission.setOwner(adminId);
|
||||||
|
return aStore.getPolicyStore().create(rs, permission).getId();
|
||||||
|
});
|
||||||
|
|
||||||
|
withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
|
||||||
|
aStore.getPolicyStore().delete(realm, permissionId);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().findById(realm, resourceServerId);
|
||||||
|
|
||||||
|
Map<Policy.FilterOption, String[]> searchMap = new HashMap<>();
|
||||||
|
searchMap.put(Policy.FilterOption.TYPE, new String[]{"uma"});
|
||||||
|
searchMap.put(Policy.FilterOption.OWNER, new String[]{adminId});
|
||||||
|
searchMap.put(Policy.FilterOption.PERMISSION, new String[] {"true"});
|
||||||
|
Set<String> s = aStore.getPolicyStore().find(realm, rs, searchMap, 0, 500).stream().map(Policy::getId).collect(Collectors.toSet());
|
||||||
|
assertThat(s, not(contains(permissionId)));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStaleCacheConcurrent() {
|
||||||
|
String permissionId = withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
UserModel u = session.users().getUserById(realm, adminId);
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().findById(realm, resourceServerId);
|
||||||
|
|
||||||
|
|
||||||
|
UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
|
||||||
|
permission.setName(KeycloakModelUtils.generateId());
|
||||||
|
permission.addUser(u.getUsername());
|
||||||
|
permission.addScope("read");
|
||||||
|
|
||||||
|
permission.addResource(resourceId);
|
||||||
|
permission.setOwner(adminId);
|
||||||
|
return aStore.getPolicyStore().create(rs, permission).getId();
|
||||||
|
});
|
||||||
|
|
||||||
|
IntStream.range(0, 500).parallel().forEach(index -> {
|
||||||
|
String createdPolicyId = withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().findById(realm, resourceServerId);
|
||||||
|
Policy permission = aStore.getPolicyStore().findById(realm, rs, permissionId);
|
||||||
|
|
||||||
|
UserPolicyRepresentation userRep = new UserPolicyRepresentation();
|
||||||
|
userRep.setName("isAdminUser" + index);
|
||||||
|
userRep.addUser("admin");
|
||||||
|
Policy associatedPolicy = aStore.getPolicyStore().create(rs, userRep);
|
||||||
|
permission.addAssociatedPolicy(associatedPolicy);
|
||||||
|
return associatedPolicy.getId();
|
||||||
|
});
|
||||||
|
|
||||||
|
withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
ResourceServer rs = aStore.getResourceServerStore().findById(realm, resourceServerId);
|
||||||
|
Policy permission = aStore.getPolicyStore().findById(realm, rs, permissionId);
|
||||||
|
|
||||||
|
assertThat(permission.getAssociatedPolicies(), not(contains(nullValue())));
|
||||||
|
ModelToRepresentation.toRepresentation(permission, authorization);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
withRealm(realmId, (session, realm) -> {
|
||||||
|
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory aStore = authorization.getStoreFactory();
|
||||||
|
aStore.getPolicyStore().delete(realm, createdPolicyId);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,8 @@ import org.keycloak.keys.infinispan.InfinispanCachePublicKeyProviderFactory;
|
||||||
import org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProviderFactory;
|
import org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProviderFactory;
|
||||||
import org.keycloak.models.ActionTokenStoreSpi;
|
import org.keycloak.models.ActionTokenStoreSpi;
|
||||||
import org.keycloak.models.SingleUseObjectSpi;
|
import org.keycloak.models.SingleUseObjectSpi;
|
||||||
|
import org.keycloak.models.cache.authorization.CachedStoreFactorySpi;
|
||||||
|
import org.keycloak.models.cache.infinispan.authorization.InfinispanCacheStoreFactoryProviderFactory;
|
||||||
import org.keycloak.models.cache.CachePublicKeyProviderSpi;
|
import org.keycloak.models.cache.CachePublicKeyProviderSpi;
|
||||||
import org.keycloak.models.session.UserSessionPersisterSpi;
|
import org.keycloak.models.session.UserSessionPersisterSpi;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory;
|
import org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory;
|
||||||
|
@ -61,6 +63,7 @@ public class Infinispan extends KeycloakModelParameters {
|
||||||
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
||||||
.add(AuthenticationSessionSpi.class)
|
.add(AuthenticationSessionSpi.class)
|
||||||
.add(CacheRealmProviderSpi.class)
|
.add(CacheRealmProviderSpi.class)
|
||||||
|
.add(CachedStoreFactorySpi.class)
|
||||||
.add(CacheUserProviderSpi.class)
|
.add(CacheUserProviderSpi.class)
|
||||||
.add(InfinispanConnectionSpi.class)
|
.add(InfinispanConnectionSpi.class)
|
||||||
.add(StickySessionEncoderSpi.class)
|
.add(StickySessionEncoderSpi.class)
|
||||||
|
@ -77,6 +80,7 @@ public class Infinispan extends KeycloakModelParameters {
|
||||||
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
||||||
.add(InfinispanAuthenticationSessionProviderFactory.class)
|
.add(InfinispanAuthenticationSessionProviderFactory.class)
|
||||||
.add(InfinispanCacheRealmProviderFactory.class)
|
.add(InfinispanCacheRealmProviderFactory.class)
|
||||||
|
.add(InfinispanCacheStoreFactoryProviderFactory.class)
|
||||||
.add(InfinispanClusterProviderFactory.class)
|
.add(InfinispanClusterProviderFactory.class)
|
||||||
.add(InfinispanConnectionProviderFactory.class)
|
.add(InfinispanConnectionProviderFactory.class)
|
||||||
.add(InfinispanUserCacheProviderFactory.class)
|
.add(InfinispanUserCacheProviderFactory.class)
|
||||||
|
|
Loading…
Reference in a new issue