Do not export ids when exporting authorization settings

Closes #25975

Co-authored-by: 박시준 <sjpark@logblack.com>
Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2024-04-03 17:23:29 -03:00 committed by Alexander Schwartz
parent ebeb001fe9
commit 8fb6d43e07
4 changed files with 100 additions and 99 deletions

View file

@ -34,3 +34,8 @@ For users of the `keycloak-authz-client` library, calling `AuthorizationResource
Previously, it would return a `List<Map>` at runtime, even though the method declaration advertised `List<Permission>`. Previously, it would return a `List<Map>` at runtime, even though the method declaration advertised `List<Permission>`.
This fix will break code that relied on casting the List or its contents to `List<Map>`. If you have used this method in any capacity, you are likely to have done this and be affected. This fix will break code that relied on casting the List or its contents to `List<Map>`. If you have used this method in any capacity, you are likely to have done this and be affected.
= IDs are no longer set when exporting authorization settings for a client
When exporting the authorization settings for a client, the IDs for resources, scopes, and policies are no longer set. As a
result, you can now import the settings from a client to another client.

View file

@ -1119,6 +1119,8 @@ public class ModelToRepresentation {
.stream().map(resource -> { .stream().map(resource -> {
ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization); ResourceRepresentation rep = toRepresentation(resource, settingsModel, authorization);
rep.setId(null);
if (rep.getOwner().getId().equals(settingsModel.getClientId())) { if (rep.getOwner().getId().equals(settingsModel.getClientId())) {
rep.setOwner((ResourceOwnerRepresentation) null); rep.setOwner((ResourceOwnerRepresentation) null);
} else { } else {
@ -1139,16 +1141,25 @@ public class ModelToRepresentation {
policies.addAll(policyStore.findByResourceServer(settingsModel) policies.addAll(policyStore.findByResourceServer(settingsModel)
.stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null) .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null)
.map(policy -> toRepresentation(authorization, policy)).collect(Collectors.toList())); .map(policy -> {
PolicyRepresentation rep = toRepresentation(authorization, policy);
rep.setId(null);
return rep;
}).collect(Collectors.toList()));
policies.addAll(policyStore.findByResourceServer(settingsModel) policies.addAll(policyStore.findByResourceServer(settingsModel)
.stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null)) .stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null))
.map(policy -> toRepresentation(authorization, policy)).collect(Collectors.toList())); .map(policy -> {
PolicyRepresentation rep = toRepresentation(authorization, policy);
rep.setId(null);
return rep;
}).collect(Collectors.toList()));
representation.setPolicies(policies); representation.setPolicies(policies);
List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel).stream().map(scope -> { List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel).stream().map(scope -> {
ScopeRepresentation rep = toRepresentation(scope); ScopeRepresentation rep = toRepresentation(scope);
rep.setId(null);
rep.setPolicies(null); rep.setPolicies(null);
rep.setResources(null); rep.setResources(null);

View file

@ -20,12 +20,19 @@ package org.keycloak.testsuite.authz.admin;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.AuthorizationResource; import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode; import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import java.util.List; import java.util.List;
import java.util.Objects;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -96,4 +103,57 @@ public class ResourceServerManagementTest extends AbstractAuthorizationTest {
// expected // expected
} }
} }
@Test
public void testImportSettingsToDifferentClient() throws Exception {
ClientsResource clientsResource = testRealmResource().clients();
ClientRepresentation clientRep = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"), ClientRepresentation.class);
clientRep.setClientId(KeycloakModelUtils.generateId());
clientsResource.create(clientRep).close();
List<ClientRepresentation> clients = clientsResource.findByClientId(clientRep.getClientId());
assertFalse(clients.isEmpty());
String clientId = clients.get(0).getId();
AuthorizationResource authorization = clientsResource.get(clientId).authorization();
ResourceServerRepresentation settings = authorization.exportSettings();
assertEquals(PolicyEnforcementMode.PERMISSIVE, settings.getPolicyEnforcementMode());
assertEquals(DecisionStrategy.UNANIMOUS, settings.getDecisionStrategy());
assertFalse(authorization.resources().findByName("Resource 1").isEmpty());
assertFalse(authorization.resources().findByName("Resource 15").isEmpty());
assertFalse(authorization.resources().findByName("Resource 20").isEmpty());
assertNotNull(authorization.permissions().resource().findByName("Resource 15 Permission"));
assertNotNull(authorization.policies().role().findByName("Resource 1 Policy"));
settings.getPolicies().removeIf(p -> "js".equals(p.getType()));
ClientRepresentation anotherClientRep = ClientBuilder.create().clientId(KeycloakModelUtils.generateId()).secret("secret").authorizationServicesEnabled(true).serviceAccount().enabled(true).build();
clientsResource.create(anotherClientRep).close();
clients = clientsResource.findByClientId(anotherClientRep.getClientId());
assertFalse(clients.isEmpty());
ClientRepresentation anotherClient = clients.get(0);
authorization = clientsResource.get(anotherClient.getId()).authorization();
authorization.importSettings(settings);
ResourceServerRepresentation anotherSettings = authorization.exportSettings();
assertEquals(PolicyEnforcementMode.PERMISSIVE, anotherSettings.getPolicyEnforcementMode());
assertEquals(DecisionStrategy.UNANIMOUS, anotherSettings.getDecisionStrategy());
assertFalse(authorization.resources().findByName("Resource 1").isEmpty());
assertFalse(authorization.resources().findByName("Resource 15").isEmpty());
assertFalse(authorization.resources().findByName("Resource 20").isEmpty());
assertNotNull(authorization.permissions().resource().findByName("Resource 15 Permission"));
assertNotNull(authorization.policies().role().findByName("Resource 1 Policy"));
}
@Test
public void testExportSettings() throws Exception {
ClientsResource clientsResource = testRealmResource().clients();
ClientRepresentation clientRep = JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/client-with-authz-settings.json"), ClientRepresentation.class);
clientRep.setClientId(KeycloakModelUtils.generateId());
clientsResource.create(clientRep).close();
List<ClientRepresentation> clients = clientsResource.findByClientId(clientRep.getClientId());
assertFalse(clients.isEmpty());
String clientId = clients.get(0).getId();
AuthorizationResource authorization = clientsResource.get(clientId).authorization();
ResourceServerRepresentation settings = authorization.exportSettings();
assertFalse(settings.getResources().stream().map(ResourceRepresentation::getId).anyMatch(Objects::nonNull));
assertFalse(settings.getScopes().stream().map(ScopeRepresentation::getId).anyMatch(Objects::nonNull));
assertFalse(settings.getPolicies().stream().map(PolicyRepresentation::getId).anyMatch(Objects::nonNull));
}
} }

View file

@ -447,202 +447,127 @@
"name": "Resource 1 Policy", "name": "Resource 1 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 2 Policy", "name": "Resource 2 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 3 Policy", "name": "Resource 3 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 4 Policy", "name": "Resource 4 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 5 Policy", "name": "Resource 5 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 6 Policy", "name": "Resource 6 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 7 Policy", "name": "Resource 7 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 8 Policy", "name": "Resource 8 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 9 Policy", "name": "Resource 9 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 10 Policy", "name": "Resource 10 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 11 Policy", "name": "Resource 11 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 12 Policy", "name": "Resource 12 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 13 Policy", "name": "Resource 13 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 14 Policy", "name": "Resource 14 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 15 Policy", "name": "Resource 15 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 16 Policy", "name": "Resource 16 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 17 Policy", "name": "Resource 17 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 18 Policy", "name": "Resource 18 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 19 Policy", "name": "Resource 19 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
}, },
{ {
"name": "Resource 20 Policy", "name": "Resource 20 Policy",
"type": "role", "type": "role",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"roles": "[{\"id\":\"authz-client/uma_protection\",\"required\":false}]"
}
},
{
"name": "Default Permission",
"description": "A permission that applies to the default resource type",
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"defaultResourceType": "urn:authz-client:resources:default",
"applyPolicies": "[\"Default Policy\"]"
}
}, },
{ {
"name": "Resource 1 Permission", "name": "Resource 1 Permission",
"type": "resource", "type": "resource",
"logic": "POSITIVE", "logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS", "decisionStrategy": "UNANIMOUS"
"config": {
"resources": "[\"Resource 1\"]",
"applyPolicies": "[\"Resource 1 Policy\"]"
}
}, },
{ {
"name": "Resource 2 Permission", "name": "Resource 2 Permission",