mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
[KEYCLOAK-10443] - Define a global decision strategy for resource servers
This commit is contained in:
parent
aca8c89d3e
commit
0cdd23763c
@ -34,6 +34,7 @@ public class ResourceServerRepresentation {
|
||||
private List<ResourceRepresentation> resources = emptyList();
|
||||
private List<PolicyRepresentation> policies = emptyList();
|
||||
private List<ScopeRepresentation> scopes = emptyList();
|
||||
private DecisionStrategy decisionStrategy;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
@ -98,4 +99,12 @@ public class ResourceServerRepresentation {
|
||||
public List<ScopeRepresentation> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan.authorization;
|
||||
import org.keycloak.authorization.model.CachedModel;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
/**
|
||||
@ -103,6 +104,18 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
if (isUpdated()) return updated.getDecisionStrategy();
|
||||
return cached.getDecisionStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
getDelegateForUpdate();
|
||||
updated.setDecisionStrategy(decisionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@ -20,6 +20,7 @@ package org.keycloak.models.cache.infinispan.authorization.entities;
|
||||
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
/**
|
||||
@ -29,11 +30,13 @@ public class CachedResourceServer extends AbstractRevisioned {
|
||||
|
||||
private final boolean allowRemoteResourceManagement;
|
||||
private final PolicyEnforcementMode policyEnforcementMode;
|
||||
private final DecisionStrategy decisionStrategy;
|
||||
|
||||
public CachedResourceServer(Long revision, ResourceServer resourceServer) {
|
||||
super(revision, resourceServer.getId());
|
||||
this.allowRemoteResourceManagement = resourceServer.isAllowRemoteResourceManagement();
|
||||
this.policyEnforcementMode = resourceServer.getPolicyEnforcementMode();
|
||||
this.decisionStrategy = resourceServer.getDecisionStrategy();
|
||||
}
|
||||
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
@ -43,4 +46,8 @@ public class CachedResourceServer extends AbstractRevisioned {
|
||||
public PolicyEnforcementMode getPolicyEnforcementMode() {
|
||||
return this.policyEnforcementMode;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
package org.keycloak.authorization.jpa.entities;
|
||||
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import javax.persistence.Column;
|
||||
@ -42,6 +43,9 @@ public class ResourceServerEntity {
|
||||
@Column(name = "POLICY_ENFORCE_MODE")
|
||||
private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
|
||||
|
||||
@Column(name = "DECISION_STRATEGY")
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
@ -66,6 +70,17 @@ public class ResourceServerEntity {
|
||||
this.policyEnforcementMode = policyEnforcementMode;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
if (DecisionStrategy.CONSENSUS.equals(decisionStrategy)) {
|
||||
throw new IllegalArgumentException("Strategy " + decisionStrategy + " not supported");
|
||||
}
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@ -21,6 +21,7 @@ import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.jpa.JpaModel;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
@ -75,6 +76,17 @@ public class ResourceServerAdapter extends AbstractAuthorizationModel implements
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return entity.getDecisionStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDecisionStrategy(decisionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--
|
||||
~ Copyright 2019 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.2.xsd">
|
||||
<changeSet author="psilva@redhat.com" id="authz-7.0.0-KEYCLOAK-10443">
|
||||
<addColumn tableName="RESOURCE_SERVER">
|
||||
<column name="DECISION_STRATEGY" type="TINYINT" defaultValue="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@ -62,4 +62,5 @@
|
||||
<include file="META-INF/jpa-changelog-4.6.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-4.7.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-4.8.0.xml"/>
|
||||
<include file="META-INF/jpa-changelog-authz-7.0.0.xml"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
package org.keycloak.authorization.model;
|
||||
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
/**
|
||||
@ -62,4 +63,19 @@ public interface ResourceServer {
|
||||
* @param enforcementMode one of the available options in {@code PolicyEnforcementMode}
|
||||
*/
|
||||
void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode);
|
||||
|
||||
/**
|
||||
* Defines a {@link DecisionStrategy} for this instance, indicating how permissions should be granted depending on the given
|
||||
* {@code decisionStrategy}.
|
||||
*
|
||||
* @param decisionStrategy the decision strategy
|
||||
*/
|
||||
void setDecisionStrategy(DecisionStrategy decisionStrategy);
|
||||
|
||||
/**
|
||||
* Returns the {@link DecisionStrategy} configured for this instance.
|
||||
*
|
||||
* @return the decision strategy
|
||||
*/
|
||||
DecisionStrategy getDecisionStrategy();
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.permission.ResourcePermission;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationRequest;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -70,6 +71,8 @@ public class DecisionPermissionCollector extends AbstractDecisionCollector {
|
||||
for (Result.PolicyResult policyResult : result.getResults()) {
|
||||
Policy policy = policyResult.getPolicy();
|
||||
Set<Scope> policyScopes = policy.getScopes();
|
||||
Set<Resource> policyResources = policy.getResources();
|
||||
boolean containsResource = policyResources.contains(resource);
|
||||
|
||||
if (isGranted(policyResult)) {
|
||||
if (isScopePermission(policy)) {
|
||||
@ -89,15 +92,21 @@ public class DecisionPermissionCollector extends AbstractDecisionCollector {
|
||||
userManagedPermissions.add(policyResult);
|
||||
}
|
||||
if (!resourceGranted) {
|
||||
resourceGranted = policy.getResources().contains(resource);
|
||||
resourceGranted = containsResource;
|
||||
}
|
||||
} else {
|
||||
if (isResourcePermission(policy)) {
|
||||
if (!resourceGranted) {
|
||||
// deny all requested scopes if the resource-based permission is associated with the resource or if the
|
||||
// resource was not granted by any other permission
|
||||
if (containsResource || !resourceGranted) {
|
||||
deniedScopes.addAll(requestedScopes);
|
||||
}
|
||||
} else {
|
||||
deniedScopes.addAll(policyScopes);
|
||||
// deny all scopes associated with the scope-based permission if the permission is associated with the
|
||||
// resource or if the permission applies to any resource associated with the scopes
|
||||
if (containsResource || policyResources.isEmpty()) {
|
||||
deniedScopes.addAll(policyScopes);
|
||||
}
|
||||
}
|
||||
if (!anyDeny) {
|
||||
anyDeny = true;
|
||||
@ -105,7 +114,11 @@ public class DecisionPermissionCollector extends AbstractDecisionCollector {
|
||||
}
|
||||
}
|
||||
|
||||
// remove any scope denied from the list of granted scopes
|
||||
if (DecisionStrategy.AFFIRMATIVE.equals(resourceServer.getDecisionStrategy())) {
|
||||
// remove any scope that was granted from the list of denied scopes if the decision strategy is affirmative
|
||||
deniedScopes.removeAll(grantedScopes);
|
||||
}
|
||||
|
||||
grantedScopes.removeAll(deniedScopes);
|
||||
|
||||
if (userManagedPermissions.isEmpty()) {
|
||||
|
||||
@ -788,6 +788,7 @@ public class ModelToRepresentation {
|
||||
server.setName(client.getClientId());
|
||||
server.setAllowRemoteResourceManagement(model.isAllowRemoteResourceManagement());
|
||||
server.setPolicyEnforcementMode(model.getPolicyEnforcementMode());
|
||||
server.setDecisionStrategy(model.getDecisionStrategy());
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
@ -116,6 +116,7 @@ import org.keycloak.representations.idm.UserFederationMapperRepresentation;
|
||||
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
@ -2068,6 +2069,14 @@ public class RepresentationToModel {
|
||||
resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
|
||||
resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
|
||||
|
||||
DecisionStrategy decisionStrategy = rep.getDecisionStrategy();
|
||||
|
||||
if (decisionStrategy == null) {
|
||||
decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
}
|
||||
|
||||
resourceServer.setDecisionStrategy(decisionStrategy);
|
||||
|
||||
for (ScopeRepresentation scope : rep.getScopes()) {
|
||||
toModel(scope, resourceServer, authorization);
|
||||
}
|
||||
|
||||
@ -100,6 +100,7 @@ public class ResourceServerService {
|
||||
this.auth.realm().requireManageAuthorization();
|
||||
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
|
||||
this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
|
||||
this.resourceServer.setDecisionStrategy(server.getDecisionStrategy());
|
||||
audit(OperationType.UPDATE, session.getContext().getUri(), false);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
@ -51,6 +52,7 @@ public class ResourceServerManagementTest extends AbstractAuthorizationTest {
|
||||
AuthorizationResource settings = clientsResource.get(clientId).authorization();
|
||||
|
||||
assertEquals(PolicyEnforcementMode.PERMISSIVE, settings.exportSettings().getPolicyEnforcementMode());
|
||||
assertEquals(DecisionStrategy.UNANIMOUS, settings.exportSettings().getDecisionStrategy());
|
||||
|
||||
assertFalse(settings.resources().findByName("Resource 1").isEmpty());
|
||||
assertFalse(settings.resources().findByName("Resource 15").isEmpty());
|
||||
|
||||
@ -45,6 +45,7 @@ import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AuthorizationResponse;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
@ -102,6 +103,7 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
|
||||
ResourceServerRepresentation settings = authorization.getSettings();
|
||||
|
||||
settings.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING);
|
||||
settings.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
|
||||
authorization.update(settings);
|
||||
|
||||
@ -129,6 +131,46 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
|
||||
assertTrue(permissions.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Scope Read on Resource A has two conflicting permissions. One is granting access for Marta and the other for Kolo.
|
||||
*
|
||||
* <p>Scope Read should not be granted for Marta.
|
||||
*/
|
||||
@Test
|
||||
public void testMartaCanAccessResourceA() throws Exception {
|
||||
ClientResource client = getClient(getRealm());
|
||||
AuthorizationResource authorization = client.authorization();
|
||||
ResourceServerRepresentation settings = authorization.getSettings();
|
||||
|
||||
settings.setPolicyEnforcementMode(PolicyEnforcementMode.ENFORCING);
|
||||
settings.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
|
||||
authorization.update(settings);
|
||||
|
||||
Collection<Permission> permissions = getEntitlements("marta", "password");
|
||||
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission permission : new ArrayList<>(permissions)) {
|
||||
String resourceSetName = permission.getResourceName();
|
||||
|
||||
switch (resourceSetName) {
|
||||
case "Resource A":
|
||||
assertThat(permission.getScopes(), containsInAnyOrder("execute", "write", "read"));
|
||||
permissions.remove(permission);
|
||||
break;
|
||||
case "Resource C":
|
||||
assertThat(permission.getScopes(), containsInAnyOrder("execute", "write", "read"));
|
||||
permissions.remove(permission);
|
||||
break;
|
||||
default:
|
||||
fail("Unexpected permission for resource [" + resourceSetName + "]");
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(permissions.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithPermissiveMode() throws Exception {
|
||||
ClientResource client = getClient(getRealm());
|
||||
@ -136,6 +178,7 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
|
||||
ResourceServerRepresentation settings = authorization.getSettings();
|
||||
|
||||
settings.setPolicyEnforcementMode(PolicyEnforcementMode.PERMISSIVE);
|
||||
settings.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
|
||||
authorization.update(settings);
|
||||
|
||||
@ -174,6 +217,7 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
|
||||
ResourceServerRepresentation settings = authorization.getSettings();
|
||||
|
||||
settings.setPolicyEnforcementMode(PolicyEnforcementMode.DISABLED);
|
||||
settings.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
|
||||
authorization.update(settings);
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.ResourceResource;
|
||||
import org.keycloak.admin.client.resource.ScopePermissionsResource;
|
||||
import org.keycloak.authorization.client.AuthorizationDeniedException;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.authorization.client.Configuration;
|
||||
@ -79,6 +80,7 @@ import org.keycloak.representations.idm.authorization.PermissionResponse;
|
||||
import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||
@ -842,6 +844,151 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerDecisionStrategy() throws Exception {
|
||||
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||
AuthorizationResource authorization = client.authorization();
|
||||
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName(KeycloakModelUtils.generateId());
|
||||
resource.addScope("read", "write", "delete");
|
||||
|
||||
try (Response response = authorization.resources().create(resource)) {
|
||||
resource = response.readEntity(ResourceRepresentation.class);
|
||||
}
|
||||
|
||||
JSPolicyRepresentation grantPolicy = new JSPolicyRepresentation();
|
||||
|
||||
grantPolicy.setName(KeycloakModelUtils.generateId());
|
||||
grantPolicy.setCode("$evaluation.grant();");
|
||||
|
||||
authorization.policies().js().create(grantPolicy).close();
|
||||
|
||||
JSPolicyRepresentation denyPolicy = new JSPolicyRepresentation();
|
||||
|
||||
denyPolicy.setName(KeycloakModelUtils.generateId());
|
||||
denyPolicy.setCode("$evaluation.deny();");
|
||||
|
||||
authorization.policies().js().create(denyPolicy).close();
|
||||
|
||||
ResourcePermissionRepresentation resourcePermission = new ResourcePermissionRepresentation();
|
||||
|
||||
resourcePermission.setName(KeycloakModelUtils.generateId());
|
||||
resourcePermission.addResource(resource.getId());
|
||||
resourcePermission.addPolicy(denyPolicy.getName());
|
||||
|
||||
authorization.permissions().resource().create(resourcePermission).close();
|
||||
|
||||
ScopePermissionRepresentation scopePermission1 = new ScopePermissionRepresentation();
|
||||
|
||||
scopePermission1.setName(KeycloakModelUtils.generateId());
|
||||
scopePermission1.addScope("read");
|
||||
scopePermission1.addPolicy(grantPolicy.getName());
|
||||
|
||||
ScopePermissionsResource scopePermissions = authorization.permissions().scope();
|
||||
scopePermissions.create(scopePermission1).close();
|
||||
|
||||
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken();
|
||||
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission(resource.getName());
|
||||
|
||||
try {
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access the resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
|
||||
ResourceServerRepresentation settings = authorization.getSettings();
|
||||
|
||||
settings.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
|
||||
authorization.update(settings);
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read");
|
||||
|
||||
scopePermission1 = scopePermissions.findByName(scopePermission1.getName());
|
||||
|
||||
scopePermission1.addScope("read", "delete");
|
||||
|
||||
scopePermissions.findById(scopePermission1.getId()).update(scopePermission1);
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete");
|
||||
|
||||
ScopePermissionRepresentation scopePermission2 = new ScopePermissionRepresentation();
|
||||
|
||||
scopePermission2.setName(KeycloakModelUtils.generateId());
|
||||
scopePermission2.addScope("write");
|
||||
scopePermission2.addPolicy(grantPolicy.getName());
|
||||
|
||||
scopePermissions.create(scopePermission2).close();
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete", "write");
|
||||
|
||||
ScopePermissionRepresentation scopePermission3 = new ScopePermissionRepresentation();
|
||||
|
||||
scopePermission3.setName(KeycloakModelUtils.generateId());
|
||||
scopePermission3.addResource(resource.getId());
|
||||
scopePermission3.addScope("write", "read", "delete");
|
||||
scopePermission3.addPolicy(grantPolicy.getName());
|
||||
|
||||
scopePermissions.create(scopePermission3).close();
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete", "write");
|
||||
|
||||
scopePermission2 = scopePermissions.findByName(scopePermission2.getName());
|
||||
scopePermissions.findById(scopePermission2.getId()).remove();
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete", "write");
|
||||
|
||||
scopePermission1 = scopePermissions.findByName(scopePermission1.getName());
|
||||
scopePermissions.findById(scopePermission1.getId()).remove();
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete", "write");
|
||||
|
||||
scopePermission3 = scopePermissions.findByName(scopePermission3.getName());
|
||||
|
||||
scopePermission3.addScope("write", "delete");
|
||||
scopePermissions.findById(scopePermission3.getId()).update(scopePermission3);
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "delete", "write");
|
||||
|
||||
scopePermissions.findById(scopePermission3.getId()).remove();
|
||||
|
||||
try {
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access the resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
|
||||
ResourcePermissionRepresentation grantResourcePermission = new ResourcePermissionRepresentation();
|
||||
|
||||
grantResourcePermission.setName(KeycloakModelUtils.generateId());
|
||||
grantResourcePermission.addResource(resource.getId());
|
||||
grantResourcePermission.addPolicy(grantPolicy.getName());
|
||||
|
||||
authorization.permissions().resource().create(grantResourcePermission).close();
|
||||
|
||||
assertPermissions(authzClient, accessToken, request, resource, "read", "delete", "write");
|
||||
|
||||
settings.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
|
||||
authorization.update(settings);
|
||||
|
||||
try {
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access the resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObtainAllEntitlementsForResourceType() throws Exception {
|
||||
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||
@ -1226,6 +1373,188 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideParentScopePermission() throws Exception {
|
||||
ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST);
|
||||
AuthorizationResource authorization = client.authorization();
|
||||
JSPolicyRepresentation onlyOwnerPolicy = createOnlyOwnerPolicy();
|
||||
|
||||
authorization.policies().js().create(onlyOwnerPolicy).close();
|
||||
|
||||
ResourceRepresentation typedResource = new ResourceRepresentation();
|
||||
|
||||
typedResource.setType("resource");
|
||||
typedResource.setName(KeycloakModelUtils.generateId());
|
||||
typedResource.addScope("read", "update");
|
||||
|
||||
try (Response response = authorization.resources().create(typedResource)) {
|
||||
typedResource = response.readEntity(ResourceRepresentation.class);
|
||||
}
|
||||
|
||||
ScopePermissionRepresentation typedResourcePermission = new ScopePermissionRepresentation();
|
||||
|
||||
typedResourcePermission.setName(KeycloakModelUtils.generateId());
|
||||
typedResourcePermission.addResource(typedResource.getName());
|
||||
typedResourcePermission.addPolicy(onlyOwnerPolicy.getName());
|
||||
typedResourcePermission.addScope("read", "update");
|
||||
|
||||
authorization.permissions().scope().create(typedResourcePermission).close();
|
||||
|
||||
ResourceRepresentation martaResource = new ResourceRepresentation();
|
||||
|
||||
martaResource.setType("resource");
|
||||
martaResource.setName(KeycloakModelUtils.generateId());
|
||||
martaResource.addScope("read");
|
||||
martaResource.setOwner("marta");
|
||||
|
||||
try (Response response = authorization.resources().create(martaResource)) {
|
||||
martaResource = response.readEntity(ResourceRepresentation.class);
|
||||
}
|
||||
|
||||
String accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
|
||||
AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
|
||||
AuthorizationRequest request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission(martaResource.getName());
|
||||
|
||||
// marta can access her resource
|
||||
AuthorizationResponse response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
Collection<Permission> permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(martaResource.getName(), grantedPermission.getResourceName());
|
||||
Set<String> scopes = grantedPermission.getScopes();
|
||||
assertEquals(2, scopes.size());
|
||||
assertThat(scopes, Matchers.containsInAnyOrder("read", "update"));
|
||||
}
|
||||
|
||||
accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken();
|
||||
authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG);
|
||||
|
||||
request = new AuthorizationRequest();
|
||||
|
||||
request.addPermission(martaResource.getId());
|
||||
|
||||
try {
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access marta resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
|
||||
UserPolicyRepresentation onlyKoloPolicy = new UserPolicyRepresentation();
|
||||
|
||||
onlyKoloPolicy.setName(KeycloakModelUtils.generateId());
|
||||
onlyKoloPolicy.addUser("kolo");
|
||||
|
||||
authorization.policies().user().create(onlyKoloPolicy).close();
|
||||
|
||||
ResourcePermissionRepresentation martaResourcePermission = new ResourcePermissionRepresentation();
|
||||
|
||||
martaResourcePermission.setName(KeycloakModelUtils.generateId());
|
||||
martaResourcePermission.addResource(martaResource.getId());
|
||||
martaResourcePermission.addPolicy(onlyKoloPolicy.getName());
|
||||
|
||||
try (Response response1 = authorization.permissions().resource().create(martaResourcePermission)) {
|
||||
martaResourcePermission = response1.readEntity(ResourcePermissionRepresentation.class);
|
||||
}
|
||||
|
||||
response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(martaResource.getName(), grantedPermission.getResourceName());
|
||||
Set<String> scopes = grantedPermission.getScopes();
|
||||
assertEquals(2, scopes.size());
|
||||
assertThat(scopes, Matchers.containsInAnyOrder("read", "update"));
|
||||
}
|
||||
|
||||
ScopePermissionRepresentation martaResourceUpdatePermission = new ScopePermissionRepresentation();
|
||||
|
||||
martaResourceUpdatePermission.setName(KeycloakModelUtils.generateId());
|
||||
martaResourceUpdatePermission.addResource(martaResource.getId());
|
||||
martaResourceUpdatePermission.addScope("update");
|
||||
martaResourceUpdatePermission.addPolicy(onlyOwnerPolicy.getName());
|
||||
|
||||
try (Response response1 = authorization.permissions().scope().create(martaResourceUpdatePermission)) {
|
||||
martaResourceUpdatePermission = response1.readEntity(ScopePermissionRepresentation.class);
|
||||
}
|
||||
|
||||
// now kolo can only read, but not update
|
||||
response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(martaResource.getName(), grantedPermission.getResourceName());
|
||||
Set<String> scopes = grantedPermission.getScopes();
|
||||
assertEquals(1, scopes.size());
|
||||
assertThat(scopes, Matchers.containsInAnyOrder("read"));
|
||||
}
|
||||
|
||||
authorization.permissions().resource().findById(martaResourcePermission.getId()).remove();
|
||||
|
||||
try {
|
||||
// after removing permission to marta resource, kolo can not access any scope in the resource
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access marta resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
|
||||
martaResourceUpdatePermission.addPolicy(onlyKoloPolicy.getName());
|
||||
martaResourceUpdatePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
|
||||
authorization.permissions().scope().findById(martaResourceUpdatePermission.getId()).update(martaResourceUpdatePermission);
|
||||
|
||||
// now kolo can access because update permission changed to allow him to access the resource using an affirmative strategy
|
||||
response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(martaResource.getName(), grantedPermission.getResourceName());
|
||||
Set<String> scopes = grantedPermission.getScopes();
|
||||
assertEquals(1, scopes.size());
|
||||
assertThat(scopes, Matchers.containsInAnyOrder("update"));
|
||||
}
|
||||
|
||||
accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
|
||||
|
||||
// marta can still access her resource
|
||||
response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(martaResource.getName(), grantedPermission.getResourceName());
|
||||
Set<String> scopes = grantedPermission.getScopes();
|
||||
assertEquals(2, scopes.size());
|
||||
assertThat(scopes, Matchers.containsInAnyOrder("update", "read"));
|
||||
}
|
||||
|
||||
authorization.permissions().scope().findById(martaResourceUpdatePermission.getId()).remove();
|
||||
accessToken = new OAuthClient().realm("authz-test").clientId(RESOURCE_SERVER_TEST).doGrantAccessTokenRequest("secret", "kolo", "password").getAccessToken();
|
||||
|
||||
try {
|
||||
// back to original setup, permissions not granted by the type resource
|
||||
authzClient.authorization(accessToken).authorize(request);
|
||||
fail("kolo can not access marta resource");
|
||||
} catch (RuntimeException expected) {
|
||||
assertEquals(403, HttpResponseException.class.cast(expected.getCause()).getStatusCode());
|
||||
assertTrue(HttpResponseException.class.cast(expected.getCause()).toString().contains("access_denied"));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JSPolicyRepresentation createOnlyOwnerPolicy() {
|
||||
JSPolicyRepresentation onlyOwnerPolicy = new JSPolicyRepresentation();
|
||||
@ -1681,4 +2010,17 @@ public class EntitlementAPITest extends AbstractAuthzTest {
|
||||
|
||||
client.update(representation);
|
||||
}
|
||||
|
||||
private void assertPermissions(AuthzClient authzClient, String accessToken, AuthorizationRequest request, ResourceRepresentation resource, String... expectedScopes) {
|
||||
AuthorizationResponse response = authzClient.authorization(accessToken).authorize(request);
|
||||
assertNotNull(response.getToken());
|
||||
Collection<Permission> permissions = toAccessToken(response.getToken()).getAuthorization().getPermissions();
|
||||
assertEquals(1, permissions.size());
|
||||
|
||||
for (Permission grantedPermission : permissions) {
|
||||
assertEquals(resource.getId(), grantedPermission.getResourceId());
|
||||
assertEquals(expectedScopes.length, grantedPermission.getScopes().size());
|
||||
assertTrue(grantedPermission.getScopes().containsAll(Arrays.asList(expectedScopes)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,8 +42,9 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
@ -239,6 +240,13 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||
testMicroprofileJWTScopeAddedToClient();
|
||||
}
|
||||
|
||||
private void testDecisionStrategySetOnResourceServer() {
|
||||
ClientsResource clients = migrationRealm.clients();
|
||||
ClientRepresentation clientRepresentation = clients.findByClientId("authz-servlet").get(0);
|
||||
ResourceServerRepresentation settings = clients.get(clientRepresentation.getId()).authorization().getSettings();
|
||||
assertEquals(DecisionStrategy.UNANIMOUS, settings.getDecisionStrategy());
|
||||
}
|
||||
|
||||
private void testGroupPolicyTypeFineGrainedAdminPermission() {
|
||||
ClientsResource clients = migrationRealm.clients();
|
||||
ClientRepresentation clientRepresentation = clients.findByClientId("realm-management").get(0);
|
||||
@ -600,4 +608,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||
protected void testMigrationTo6_x() {
|
||||
testMigrationTo6_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
||||
if (supportedAuthzServices) {
|
||||
testDecisionStrategySetOnResourceServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +75,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo4_x(false, false);
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -68,6 +68,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo4_x(true, false);
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -67,6 +67,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
||||
testMigrationTo4_x(true, false);
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -61,6 +61,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
|
||||
checkRealmsImported();
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||
testMigratedData();
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -80,6 +81,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||
testMigrationTo4_x();
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -90,6 +92,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||
testMigrationTo4_x();
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -101,6 +104,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||
testMigrationTo4_x(false, false);
|
||||
testMigrationTo5_x();
|
||||
testMigrationTo6_x();
|
||||
testMigrationTo7_x(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -400,7 +400,8 @@
|
||||
"fullScopeAllowed" : true,
|
||||
"nodeReRegistrationTimeout" : -1,
|
||||
"defaultClientScopes" : [ "web-origins", "role_list", "profile", "roles", "email" ],
|
||||
"optionalClientScopes" : [ "address", "phone", "offline_access" ]
|
||||
"optionalClientScopes" : [ "address", "phone", "offline_access" ],
|
||||
"authorizationServicesEnabled": true
|
||||
}, {
|
||||
"id" : "08b72946-628a-48a2-994f-6ccbbf2f5f8e",
|
||||
"clientId" : "broker",
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package org.keycloak.testsuite.console.page.clients.authorization;
|
||||
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
|
||||
import org.keycloak.testsuite.page.Form;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
@ -33,6 +34,9 @@ public class AuthorizationSettingsForm extends Form {
|
||||
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='server.allowRemoteResourceManagement']]")
|
||||
private OnOffSwitch allowRemoteResourceManagement;
|
||||
|
||||
@FindBy(id = "server.decisionStrategy")
|
||||
private Select decisionStrategy;
|
||||
|
||||
public void setEnforcementMode(PolicyEnforcerConfig.EnforcementMode mode) {
|
||||
enforcementMode.selectByValue(mode.name());
|
||||
}
|
||||
@ -48,4 +52,12 @@ public class AuthorizationSettingsForm extends Form {
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return allowRemoteResourceManagement.isOn();
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return DecisionStrategy.valueOf(decisionStrategy.getFirstSelectedOption().getAttribute("value"));
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
enforcementMode.selectByValue(decisionStrategy.name());
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,7 @@ import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.testsuite.console.page.clients.authorization.AuthorizationSettingsForm;
|
||||
@ -56,6 +57,9 @@ public class DefaultAuthorizationSettingsTest extends AbstractAuthorizationSetti
|
||||
assertEquals(PolicyEnforcerConfig.EnforcementMode.ENFORCING, settings.getEnforcementMode());
|
||||
assertEquals(true, settings.isAllowRemoteResourceManagement());
|
||||
|
||||
assertEquals(DecisionStrategy.UNANIMOUS, settings.getDecisionStrategy());
|
||||
assertEquals(true, settings.isAllowRemoteResourceManagement());
|
||||
|
||||
Resources resources = authorizationPage.authorizationTabs().resources();
|
||||
ResourceRepresentation resource = resources.resources().findByName("Default Resource");
|
||||
|
||||
|
||||
@ -1291,6 +1291,7 @@ authz-remote-resource-management=Remote Resource Management
|
||||
authz-remote-resource-management.tooltip=Should resources be managed remotely by the resource server? If false, resources can be managed only from this admin console.
|
||||
authz-export-settings=Export Settings
|
||||
authz-export-settings.tooltip=Export and download all authorization settings for this resource server.
|
||||
authz-server-decision-strategy.tooltip=The decision strategy dictates how permissions are evaluated and how a final decision is obtained. 'Affirmative' means that at least one permission must evaluate to a positive decision in order grant access to a resource and its scopes. 'Unanimous' means that all permissions must evaluate to a positive decision in order for the final decision to be also positive.
|
||||
# Authz Resource List
|
||||
authz-no-resources-available=No resources available.
|
||||
authz-no-scopes-assigned=No scopes assigned.
|
||||
|
||||
@ -43,6 +43,20 @@
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-policy-enforcement-mode.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="server.decisionStrategy">{{:: 'authz-policy-decision-strategy' | translate}}</label>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<select class="form-control" id="server.decisionStrategy"
|
||||
data-ng-model="server.decisionStrategy"
|
||||
ng-change="selectDecisionStrategy()">
|
||||
<option value="UNANIMOUS">{{:: 'authz-policy-decision-strategy-unanimous' | translate}}</option>
|
||||
<option value="AFFIRMATIVE">{{:: 'authz-policy-decision-strategy-affirmative' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-server-decision-strategy.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="server.allowRemoteResourceManagement">{{:: 'authz-remote-resource-management' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user