Merge pull request #4052 from pedroigor/KEYCLOAK-4754
[KEYCLOAK-4754] - Unable to delete realm when using aggregated policies
This commit is contained in:
commit
df163d86e8
8 changed files with 234 additions and 25 deletions
|
@ -78,9 +78,6 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (clients.isEmpty()) {
|
if (clients.isEmpty()) {
|
||||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
|
||||||
dependentPolicy.removeAssociatedPolicy(policy);
|
|
||||||
});
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
} else {
|
} else {
|
||||||
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
|
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
|
||||||
|
|
|
@ -230,12 +230,6 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (roles.isEmpty()) {
|
if (roles.isEmpty()) {
|
||||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
|
||||||
dependentPolicy.removeAssociatedPolicy(policy);
|
|
||||||
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
|
|
||||||
policyStore.delete(dependentPolicy.getId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
} else {
|
} else {
|
||||||
Map<String, String> config = policy.getConfig();
|
Map<String, String> config = policy.getConfig();
|
||||||
|
|
|
@ -180,12 +180,6 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (users.isEmpty()) {
|
if (users.isEmpty()) {
|
||||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
|
||||||
dependentPolicy.removeAssociatedPolicy(policy);
|
|
||||||
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
|
|
||||||
policyStore.delete(dependentPolicy.getId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
} else {
|
} else {
|
||||||
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
|
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
|
||||||
|
|
|
@ -102,6 +102,10 @@ public class CachedPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy findById(String id, String resourceServerId) {
|
public Policy findById(String id, String resourceServerId) {
|
||||||
|
if (resourceServerId == null) {
|
||||||
|
return getDelegate().findById(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
|
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
|
||||||
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
|
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
|
||||||
|
|
||||||
|
|
|
@ -128,8 +128,21 @@ public final class AuthorizationProvider implements Provider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String id) {
|
public void delete(String id) {
|
||||||
|
Policy policy = findById(id, null);
|
||||||
|
|
||||||
|
if (policy != null) {
|
||||||
|
ResourceServer resourceServer = policy.getResourceServer();
|
||||||
|
|
||||||
|
findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
||||||
|
dependentPolicy.removeAssociatedPolicy(policy);
|
||||||
|
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
|
||||||
|
delete(dependentPolicy.getId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
policyStore.delete(id);
|
policyStore.delete(id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Policy findById(String id, String resourceServerId) {
|
public Policy findById(String id, String resourceServerId) {
|
||||||
|
|
|
@ -96,14 +96,6 @@ public class PolicyResourceService {
|
||||||
|
|
||||||
resource.onRemove(policy, authorization);
|
resource.onRemove(policy, authorization);
|
||||||
|
|
||||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
|
||||||
if (dependentPolicy.getAssociatedPolicies().size() == 1) {
|
|
||||||
policyStore.delete(dependentPolicy.getId());
|
|
||||||
} else {
|
|
||||||
dependentPolicy.removeAssociatedPolicy(policy);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
|
|
|
@ -23,20 +23,24 @@ import java.util.List;
|
||||||
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.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.ClientsResource;
|
||||||
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.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
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 org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -56,7 +60,17 @@ public class AuthzCleanupTest extends AbstractKeycloakTest {
|
||||||
.secret("secret")
|
.secret("secret")
|
||||||
.authorizationServicesEnabled(true)
|
.authorizationServicesEnabled(true)
|
||||||
.redirectUris("http://localhost/myclient")
|
.redirectUris("http://localhost/myclient")
|
||||||
.defaultRoles("client-role-1", "client-role-2").build()).build());
|
.defaultRoles(
|
||||||
|
"client-role-1",
|
||||||
|
"client-role-2",
|
||||||
|
"Acme administrator",
|
||||||
|
"Acme viewer",
|
||||||
|
"tenant administrator",
|
||||||
|
"tenant viewer",
|
||||||
|
"tenant user"
|
||||||
|
)
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setup(KeycloakSession session) {
|
public static void setup(KeycloakSession session) {
|
||||||
|
@ -84,6 +98,12 @@ public class AuthzCleanupTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreate() throws Exception {
|
public void testCreate() throws Exception {
|
||||||
|
ClientsResource clients = getAdminClient().realms().realm(TEST).clients();
|
||||||
|
ClientRepresentation client = clients.findByClientId("myclient").get(0);
|
||||||
|
ResourceServerRepresentation settings = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/acme-resource-server-cleanup-test.json"), ResourceServerRepresentation.class);
|
||||||
|
|
||||||
|
clients.get(client.getId()).authorization().importSettings(settings);
|
||||||
|
|
||||||
testingClient.server().run(AuthzCleanupTest::setup);
|
testingClient.server().run(AuthzCleanupTest::setup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
{
|
||||||
|
"allowRemoteResourceManagement": false,
|
||||||
|
"policyEnforcementMode": "ENFORCING",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"name": "Administration resource",
|
||||||
|
"uri": "/admin/*",
|
||||||
|
"type": "http://acme.com/admin",
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:admin:manage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:admin:view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typedScopes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Role resource",
|
||||||
|
"uri": "/{REALM}/roles",
|
||||||
|
"type": "http://acme.com/roles",
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:role:view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typedScopes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "User profile resource",
|
||||||
|
"uri": "/{REALM}/userprofiles/*",
|
||||||
|
"type": "http://acme.com/userprofiles",
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:userprofile:manage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:userprofile:view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typedScopes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Account resource",
|
||||||
|
"uri": "/{REALM}/account/*",
|
||||||
|
"type": "http://acme.com/account",
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:account:manage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typedScopes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"name": "Acme admin policy",
|
||||||
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"roles": "[{\"id\":\"Acme administrator\",\"required\":true}]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Acme viewer policy",
|
||||||
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"roles": "[{\"id\":\"Acme viewer\",\"required\":true}]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tenant user policy",
|
||||||
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"roles": "[{\"id\":\"tenant user\",\"required\":true}]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tenant administrator policy",
|
||||||
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"roles": "[{\"id\":\"tenant administrator\",\"required\":true}]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tenant viewer policy",
|
||||||
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"roles": "[{\"id\":\"tenant viewer\",\"required\":true}]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Any user policy",
|
||||||
|
"description": "Defines that only users from well known clients are allowed to access",
|
||||||
|
"type": "aggregate",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"applyPolicies": "[\"Tenant user policy\",\"Acme admin policy\",\"Acme viewer policy\",\"Tenant viewer policy\",\"Tenant administrator policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Super tenant admin permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
|
"config": {
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:admin:manage\"]",
|
||||||
|
"applyPolicies": "[\"Acme admin policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Super tenant admin read permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:admin:view\"]",
|
||||||
|
"applyPolicies": "[\"Acme admin policy\",\"Acme viewer policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "View tenant role permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"resources": "[\"Role resource\"]",
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:role:view\"]",
|
||||||
|
"applyPolicies": "[\"Any user policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage account permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"resources": "[\"Account resource\"]",
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:account:manage\"]",
|
||||||
|
"applyPolicies": "[\"Any user policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage user profile permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:userprofile:manage\"]",
|
||||||
|
"applyPolicies": "[\"Acme admin policy\",\"Tenant administrator policy\"]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "View user profile permission",
|
||||||
|
"type": "scope",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
|
"config": {
|
||||||
|
"scopes": "[\"urn:acme.com:scopes:userprofile:view\"]",
|
||||||
|
"applyPolicies": "[\"Acme admin policy\",\"Acme viewer policy\",\"Tenant viewer policy\",\"Tenant administrator policy\"]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopes": [
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:admin:manage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:admin:view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:role:view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:account:manage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:userprofile:view"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "urn:acme.com:scopes:userprofile:manage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue