more fixes

This commit is contained in:
Bill Burke 2016-08-02 06:56:22 -04:00
commit 17e75950fe
24 changed files with 481 additions and 100 deletions

View file

@ -12,7 +12,7 @@ before_script:
- export MAVEN_SKIP_RC=true - export MAVEN_SKIP_RC=true
install: install:
- mvn install -Pdistribution -DskipTests=true -B -V -q - ( mvn install -Pdistribution -DskipTests=true -B -V -q ) & ( MYPID=$!; while [ $(ps -ef | grep $MYPID | grep -v grep | wc -l) -gt 0 ]; do sleep 10; date; done; )
script: script:
- mvn test -B - mvn test -B

View file

@ -140,6 +140,10 @@ public abstract class AbstractPolicyEnforcer {
return true; return true;
} }
} }
} else {
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
return true;
}
} }
} }

View file

@ -26,12 +26,17 @@ import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.representation.AuthorizationRequest; import org.keycloak.authorization.client.representation.AuthorizationRequest;
import org.keycloak.authorization.client.representation.AuthorizationResponse; import org.keycloak.authorization.client.representation.AuthorizationResponse;
import org.keycloak.authorization.client.representation.EntitlementRequest;
import org.keycloak.authorization.client.representation.EntitlementResponse; import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.authorization.client.representation.PermissionRequest; import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.representation.PermissionResponse; import org.keycloak.authorization.client.representation.PermissionResponse;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.util.JsonSerialization;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
@ -52,7 +57,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
while (retry > 0) { while (retry > 0) {
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) { if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
original.setAuthorization(accessToken.getAuthorization());
return true; return true;
} }
@ -62,6 +66,21 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
return false; return false;
} }
AccessToken.Authorization authorization = original.getAuthorization();
if (authorization == null) {
authorization = new AccessToken.Authorization();
authorization.setPermissions(new ArrayList<Permission>());
}
AccessToken.Authorization newAuthorization = accessToken.getAuthorization();
if (newAuthorization != null) {
authorization.getPermissions().addAll(newAuthorization.getPermissions());
}
original.setAuthorization(authorization);
retry--; retry--;
} }
@ -107,8 +126,21 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
return null; return null;
} else { } else {
LOGGER.debug("Obtaining entitlements for authenticated user."); LOGGER.debug("Obtaining entitlements for authenticated user.");
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId()); AccessToken token = httpFacade.getSecurityContext().getToken();
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId());
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
} else {
EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest();
permissionRequest.setResourceSetId(pathConfig.getId());
permissionRequest.setResourceSetName(pathConfig.getName());
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request);
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl());
}
} }
} catch (AuthorizationDeniedException e) { } catch (AuthorizationDeniedException e) {
return null; return null;

View file

@ -119,6 +119,7 @@ public class KeycloakInstalled {
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName()) .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri) .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.STATE, state) .queryParam(OAuth2Constants.STATE, state)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString(); .build().toString();
Desktop.getDesktop().browse(new URI(authUrl)); Desktop.getDesktop().browse(new URI(authUrl));
@ -175,6 +176,7 @@ public class KeycloakInstalled {
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE) .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName()) .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri) .queryParam(OAuth2Constants.REDIRECT_URI, redirectUri)
.queryParam(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
.build().toString(); .build().toString();
printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>"); printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>");

View file

@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -49,6 +50,7 @@ public class ResourceRepresentation {
@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<PolicyRepresentation> policies; private List<PolicyRepresentation> policies;
private List<ScopeRepresentation> typedScopes;
/** /**
* Creates a new instance. * Creates a new instance.
@ -169,4 +171,12 @@ public class ResourceRepresentation {
<T> T test(Predicate<T> t) { <T> T test(Predicate<T> t) {
return null; return null;
} }
public void setTypedScopes(List<ScopeRepresentation> typedScopes) {
this.typedScopes = typedScopes;
}
public List<ScopeRepresentation> getTypedScopes() {
return typedScopes;
}
} }

View file

@ -78,6 +78,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.entities.RequiredActionProviderEntity", "org.keycloak.models.entities.RequiredActionProviderEntity",
"org.keycloak.models.entities.PersistentUserSessionEntity", "org.keycloak.models.entities.PersistentUserSessionEntity",
"org.keycloak.models.entities.PersistentClientSessionEntity", "org.keycloak.models.entities.PersistentClientSessionEntity",
"org.keycloak.models.entities.ComponentEntity",
"org.keycloak.authorization.mongo.entities.PolicyEntity", "org.keycloak.authorization.mongo.entities.PolicyEntity",
"org.keycloak.authorization.mongo.entities.ResourceEntity", "org.keycloak.authorization.mongo.entities.ResourceEntity",
"org.keycloak.authorization.mongo.entities.ResourceServerEntity", "org.keycloak.authorization.mongo.entities.ResourceServerEntity",

View file

@ -1841,6 +1841,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId()); RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId());
if (entity == null) return; if (entity == null) return;
entity.setAlias(model.getAlias()); entity.setAlias(model.getAlias());
entity.setName(model.getName());
entity.setProviderId(model.getProviderId()); entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled()); entity.setEnabled(model.isEnabled());
entity.setDefaultAction(model.isDefaultAction()); entity.setDefaultAction(model.isDefaultAction());
@ -2066,7 +2067,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
entity.setId(model.getId()); entity.setId(model.getId());
} }
entity.setConfig(model.getConfig()); entity.setConfig(model.getConfig());
entity.setId(model.getId());
entity.setParentId(model.getParentId()); entity.setParentId(model.getParentId());
entity.setProviderType(model.getProviderType()); entity.setProviderType(model.getProviderType());
entity.setProviderId(model.getProviderId()); entity.setProviderId(model.getProviderId());
@ -2083,7 +2083,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
for (ComponentEntity entity : realm.getComponentEntities()) { for (ComponentEntity entity : realm.getComponentEntities()) {
if (entity.getId().equals(model.getId())) { if (entity.getId().equals(model.getId())) {
entity.setConfig(model.getConfig()); entity.setConfig(model.getConfig());
entity.setId(model.getId());
entity.setParentId(model.getParentId()); entity.setParentId(model.getParentId());
entity.setProviderType(model.getProviderType()); entity.setProviderType(model.getProviderType());
entity.setProviderId(model.getProviderId()); entity.setProviderId(model.getProviderId());

View file

@ -52,11 +52,9 @@ public class MigrateTo2_1_0 {
private void migrateDefaultRequiredAction(RealmModel realm) { private void migrateDefaultRequiredAction(RealmModel realm) {
RequiredActionProviderModel otpAction = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name()); RequiredActionProviderModel otpAction = realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
if (otpAction == null) return; MigrationUtils.updateOTPRequiredAction(otpAction);
if (!otpAction.getProviderId().equals(UserModel.RequiredAction.CONFIGURE_TOTP.name())) return;
if (!otpAction.getName().equals("Configure Totp")) return;
otpAction.setName("Configure OTP"); realm.updateRequiredActionProvider(otpAction);
} }
// KEYCLOAK-3338: Changes to how role policy config is stored" // KEYCLOAK-3338: Changes to how role policy config is stored"

View file

@ -47,4 +47,12 @@ public class MigrationUtils {
} }
} }
public static void updateOTPRequiredAction(RequiredActionProviderModel otpAction) {
if (otpAction == null) return;
if (!otpAction.getProviderId().equals(UserModel.RequiredAction.CONFIGURE_TOTP.name())) return;
if (!otpAction.getName().equals("Configure Totp")) return;
otpAction.setName("Configure OTP");
}
} }

View file

@ -22,6 +22,7 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.ResourceServerStore; import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.hash.Pbkdf2PasswordHashProvider; import org.keycloak.hash.Pbkdf2PasswordHashProvider;
import org.keycloak.migration.migrators.MigrationUtils;
import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
@ -206,6 +207,9 @@ public class RepresentationToModel {
if (rep.getRequiredActions() != null) { if (rep.getRequiredActions() != null) {
for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) { for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
RequiredActionProviderModel model = toModel(action); RequiredActionProviderModel model = toModel(action);
MigrationUtils.updateOTPRequiredAction(model);
newRealm.addRequiredActionProvider(model); newRealm.addRequiredActionProvider(model);
} }
} else { } else {

View file

@ -31,6 +31,7 @@ import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector; import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
import org.keycloak.authorization.policy.evaluation.EvaluationContext; import org.keycloak.authorization.policy.evaluation.EvaluationContext;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.authorization.util.Permissions; import org.keycloak.authorization.util.Permissions;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
@ -147,14 +148,15 @@ public class PolicyEvaluationService {
StoreFactory storeFactory = authorization.getStoreFactory(); StoreFactory storeFactory = authorization.getStoreFactory();
List<Scope> scopes = givenScopes.stream().map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
if (resource.getId() != null) { if (resource.getId() != null) {
Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId()); Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId());
return Stream.of(new ResourcePermission(resourceModel, scopes, resourceServer)); return Permissions.createResourcePermissions(resourceModel, givenScopes, authorization).stream();
} else if (resource.getType() != null) { } else if (resource.getType() != null) {
return storeFactory.getResourceStore().findByType(resource.getType()).stream().map(resource1 -> new ResourcePermission(resource1, scopes, resourceServer)); Set<String> finalGivenScopes = givenScopes;
return storeFactory.getResourceStore().findByType(resource.getType()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, finalGivenScopes, authorization).stream());
} else { } else {
ScopeStore scopeStore = storeFactory.getScopeStore();
List<Scope> scopes = givenScopes.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
List<ResourcePermission> collect = scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList()); List<ResourcePermission> collect = scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList());
for (Scope scope : scopes) { for (Scope scope : scopes) {

View file

@ -22,22 +22,25 @@ import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.Decision.Effect; import org.keycloak.authorization.Decision.Effect;
import org.keycloak.authorization.admin.util.Models; import org.keycloak.authorization.admin.util.Models;
import org.keycloak.authorization.common.KeycloakIdentity; import org.keycloak.authorization.common.KeycloakIdentity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.policy.evaluation.Result.PolicyResult; import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
import org.keycloak.authorization.util.Permissions; import org.keycloak.authorization.util.Permissions;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -59,7 +62,7 @@ public class PolicyEvaluationResponse {
AccessToken accessToken = identity.getAccessToken(); AccessToken accessToken = identity.getAccessToken();
AccessToken.Authorization authorizationData = new AccessToken.Authorization(); AccessToken.Authorization authorizationData = new AccessToken.Authorization();
authorizationData.setPermissions(Permissions.allPermits(results)); authorizationData.setPermissions(Permissions.allPermits(results, authorization));
accessToken.setAuthorization(authorizationData); accessToken.setAuthorization(authorizationData);
response.rpt = accessToken; response.rpt = accessToken;
@ -99,18 +102,82 @@ public class PolicyEvaluationResponse {
policies.add(toRepresentation(policy, authorization)); policies.add(toRepresentation(policy, authorization));
} }
if (rep.getResource().getId() != null) {
if (!rep.getScopes().isEmpty()) {
rep.getResource().setName(rep.getResource().getName() + " with scopes " + rep.getScopes().stream().map(ScopeRepresentation::getName).collect(Collectors.toList()));
}
}
rep.setPolicies(policies); rep.setPolicies(policies);
} }
resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName())); resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName()));
response.results = resultsRep; Map<String, EvaluationResultRepresentation> groupedResults = new HashMap<>();
resultsRep.forEach(evaluationResultRepresentation -> {
EvaluationResultRepresentation result = groupedResults.get(evaluationResultRepresentation.getResource().getId());
ResourceRepresentation resource = evaluationResultRepresentation.getResource();
if (result == null) {
groupedResults.put(resource.getId(), evaluationResultRepresentation);
result = evaluationResultRepresentation;
}
if (result.getStatus().equals(Effect.PERMIT) || (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT) && result.getStatus().equals(Effect.DENY))) {
result.setStatus(Effect.PERMIT);
}
List<ScopeRepresentation> scopes = result.getScopes();
if (scopes == null) {
scopes = new ArrayList<>();
result.setScopes(scopes);
}
List<ScopeRepresentation> currentScopes = evaluationResultRepresentation.getScopes();
if (currentScopes != null) {
for (ScopeRepresentation scope : currentScopes) {
if (!scopes.contains(scope)) {
scopes.add(scope);
}
if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) {
result.getAllowedScopes().add(scope);
}
}
}
if (resource.getId() != null) {
if (!scopes.isEmpty()) {
result.getResource().setName(evaluationResultRepresentation.getResource().getName() + " with scopes " + scopes.stream().flatMap((Function<ScopeRepresentation, Stream<?>>) scopeRepresentation -> Arrays.asList(scopeRepresentation.getName()).stream()).collect(Collectors.toList()));
} else {
result.getResource().setName(evaluationResultRepresentation.getResource().getName());
}
} else {
result.getResource().setName("Any Resource with Scopes " + scopes.stream().flatMap((Function<ScopeRepresentation, Stream<?>>) scopeRepresentation -> Arrays.asList(scopeRepresentation.getName()).stream()).collect(Collectors.toList()));
}
List<PolicyResultRepresentation> policies = result.getPolicies();
for (PolicyResultRepresentation policy : new ArrayList<>(evaluationResultRepresentation.getPolicies())) {
if (!policies.contains(policy)) {
policies.add(policy);
} else {
policy = policies.get(policies.indexOf(policy));
}
if (policy.getStatus().equals(Effect.DENY)) {
Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId());
for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scope -> Models.toRepresentation(scope, authorization)).collect(Collectors.toList())) {
if (!policy.getScopes().contains(scope)) {
policy.getScopes().add(scope);
}
}
for (ScopeRepresentation scope : currentScopes) {
if (!policy.getScopes().contains(scope)) {
policy.getScopes().add(scope);
}
}
}
}
});
response.results = groupedResults.values().stream().collect(Collectors.toList());
return response; return response;
} }
@ -147,6 +214,7 @@ public class PolicyEvaluationResponse {
private List<ScopeRepresentation> scopes; private List<ScopeRepresentation> scopes;
private List<PolicyResultRepresentation> policies; private List<PolicyResultRepresentation> policies;
private Effect status; private Effect status;
private List<ScopeRepresentation> allowedScopes = new ArrayList<>();
public void setResource(final ResourceRepresentation resource) { public void setResource(final ResourceRepresentation resource) {
this.resource = resource; this.resource = resource;
@ -179,6 +247,14 @@ public class PolicyEvaluationResponse {
public Effect getStatus() { public Effect getStatus() {
return status; return status;
} }
public void setAllowedScopes(List<ScopeRepresentation> allowedScopes) {
this.allowedScopes = allowedScopes;
}
public List<ScopeRepresentation> getAllowedScopes() {
return allowedScopes;
}
} }
public static class PolicyResultRepresentation { public static class PolicyResultRepresentation {
@ -186,6 +262,7 @@ public class PolicyEvaluationResponse {
private PolicyRepresentation policy; private PolicyRepresentation policy;
private Effect status; private Effect status;
private List<PolicyResultRepresentation> associatedPolicies; private List<PolicyResultRepresentation> associatedPolicies;
private List<ScopeRepresentation> scopes = new ArrayList<>();
public PolicyRepresentation getPolicy() { public PolicyRepresentation getPolicy() {
return policy; return policy;
@ -210,5 +287,26 @@ public class PolicyEvaluationResponse {
public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) { public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) {
this.associatedPolicies = associatedPolicies; this.associatedPolicies = associatedPolicies;
} }
@Override
public int hashCode() {
return this.policy.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PolicyResultRepresentation policy = (PolicyResultRepresentation) o;
return this.policy.equals(policy.getPolicy());
}
public void setScopes(List<ScopeRepresentation> scopes) {
this.scopes = scopes;
}
public List<ScopeRepresentation> getScopes() {
return scopes;
}
} }
} }

View file

@ -25,6 +25,7 @@ import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
@ -48,6 +49,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -245,6 +247,26 @@ public final class Models {
return scope; return scope;
}).collect(Collectors.toSet())); }).collect(Collectors.toSet()));
resource.setTypedScopes(new ArrayList<>());
if (resource.getType() != null) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
for (Resource typed : resourceStore.findByType(resource.getType())) {
if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
ScopeRepresentation scope = new ScopeRepresentation();
scope.setId(model1.getId());
scope.setName(model1.getName());
String iconUri = model1.getIconUri();
if (iconUri != null) {
scope.setIconUri(iconUri);
}
return scope;
}).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
}
}
}
resource.setPolicies(new ArrayList<>()); resource.setPolicies(new ArrayList<>());
Set<Policy> policies = new HashSet<>(); Set<Policy> policies = new HashSet<>();

View file

@ -25,10 +25,14 @@ import org.keycloak.authorization.authorization.representation.AuthorizationResp
import org.keycloak.authorization.common.KeycloakEvaluationContext; import org.keycloak.authorization.common.KeycloakEvaluationContext;
import org.keycloak.authorization.common.KeycloakIdentity; import org.keycloak.authorization.common.KeycloakIdentity;
import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission; import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector; import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.protection.permission.PermissionTicket; import org.keycloak.authorization.protection.permission.PermissionTicket;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.authorization.util.Permissions; import org.keycloak.authorization.util.Permissions;
import org.keycloak.authorization.util.Tokens; import org.keycloak.authorization.util.Tokens;
@ -51,6 +55,8 @@ import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -58,6 +64,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -100,7 +107,7 @@ public class AuthorizationTokenService {
authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate(new DecisionResultCollector() { authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate(new DecisionResultCollector() {
@Override @Override
public void onComplete(List<Result> results) { public void onComplete(List<Result> results) {
List<Permission> entitlements = Permissions.allPermits(results); List<Permission> entitlements = Permissions.allPermits(results, authorization);
if (entitlements.isEmpty()) { if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>(); HashMap<Object, Object> error = new HashMap<>();
@ -139,13 +146,36 @@ public class AuthorizationTokenService {
resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId()); resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId());
} }
if (resource == null) { if (resource == null && (requestedResource.getScopes() == null || requestedResource.getScopes().isEmpty())) {
throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getId() + "] or name [" + requestedResource.getName() + "] does not exist.", Status.FORBIDDEN); throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getId() + "] or name [" + requestedResource.getName() + "] does not exist.", Status.FORBIDDEN);
} }
Set<ScopeRepresentation> requestedScopes = requestedResource.getScopes(); Set<ScopeRepresentation> requestedScopes = requestedResource.getScopes();
Set<String> collect = requestedScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet());
permissionsToEvaluate.put(resource.getId(), requestedScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet())); if (resource != null) {
permissionsToEvaluate.put(resource.getId(), collect);
} else {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
List<Resource> resources = new ArrayList<Resource>();
resources.addAll(resourceStore.findByScope(requestedScopes.stream().map(scopeRepresentation -> {
Scope scope = scopeStore.findByName(scopeRepresentation.getName(), ticket.getResourceServerId());
if (scope == null) {
return null;
}
return scope.getId();
}).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
for (Resource resource1 : resources) {
permissionsToEvaluate.put(resource1.getId(), collect);
}
permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", collect);
}
}); });
String rpt = request.getRpt(); String rpt = request.getRpt();
@ -193,10 +223,23 @@ public class AuthorizationTokenService {
} }
} }
ResourceServer resourceServer = authorization.getStoreFactory().getResourceServerStore().findById(ticket.getResourceServerId());
return permissionsToEvaluate.entrySet().stream() return permissionsToEvaluate.entrySet().stream()
.flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> { .flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey()); String key = entry.getKey();
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
if ("$KC_SCOPE_PERMISSION".equals(key)) {
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
List<Scope> scopes = entry.getValue().stream().map(scopeName -> {
Scope byName = scopeStore.findByName(scopeName, resourceServer.getId());
return byName;
}).collect(Collectors.toList());
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
} else {
Resource entryResource = storeFactory.getResourceStore().findById(key);
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
}
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }

View file

@ -27,9 +27,12 @@ import org.keycloak.authorization.entitlement.representation.EntitlementRequest;
import org.keycloak.authorization.entitlement.representation.EntitlementResponse; import org.keycloak.authorization.entitlement.representation.EntitlementResponse;
import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission; import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector; import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory; import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.authorization.util.Permissions; import org.keycloak.authorization.util.Permissions;
import org.keycloak.authorization.util.Tokens; import org.keycloak.authorization.util.Tokens;
@ -41,6 +44,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException; import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.Cors;
@ -56,6 +60,8 @@ import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -115,7 +121,7 @@ public class EntitlementService {
@Override @Override
protected void onComplete(List<Result> results) { protected void onComplete(List<Result> results) {
List<Permission> entitlements = Permissions.allPermits(results); List<Permission> entitlements = Permissions.allPermits(results, authorization);
if (entitlements.isEmpty()) { if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>(); HashMap<Object, Object> error = new HashMap<>();
@ -168,10 +174,17 @@ public class EntitlementService {
@Override @Override
protected void onComplete(List<Result> results) { protected void onComplete(List<Result> results) {
List<Permission> entitlements = Permissions.allPermits(results); List<Permission> entitlements = Permissions.allPermits(results, authorization);
if (entitlements.isEmpty()) { if (entitlements.isEmpty()) {
asyncResponse.resume(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)); HashMap<Object, Object> error = new HashMap<>();
error.put(OAuth2Constants.ERROR, "not_authorized");
asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
.entity(error))
.allowedOrigins(identity.getAccessToken())
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else { } else {
asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build()); asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} }
@ -203,18 +216,42 @@ public class EntitlementService {
resource = storeFactory.getResourceStore().findByName(requestedResource.getResourceSetName(), resourceServer.getId()); resource = storeFactory.getResourceStore().findByName(requestedResource.getResourceSetName(), resourceServer.getId());
} }
if (resource == null) { if (resource == null && (requestedResource.getScopes() == null || requestedResource.getScopes().isEmpty())) {
throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getResourceSetId() + "] or name [" + requestedResource.getResourceSetName() + "] does not exist.", Status.FORBIDDEN); throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getResourceSetId() + "] or name [" + requestedResource.getResourceSetName() + "] does not exist.", Status.FORBIDDEN);
} }
permissionsToEvaluate.put(resource.getId(), requestedResource.getScopes()); Set<ScopeRepresentation> requestedScopes = requestedResource.getScopes().stream().map(ScopeRepresentation::new).collect(Collectors.toSet());
Set<String> collect = requestedScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet());
if (resource != null) {
permissionsToEvaluate.put(resource.getId(), collect);
} else {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
List<Resource> resources = new ArrayList<Resource>();
resources.addAll(resourceStore.findByScope(requestedScopes.stream().map(scopeRepresentation -> {
Scope scope = scopeStore.findByName(scopeRepresentation.getName(), resourceServer.getId());
if (scope == null) {
return null;
}
return scope.getId();
}).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
for (Resource resource1 : resources) {
permissionsToEvaluate.put(resource1.getId(), collect);
}
permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", collect);
}
}); });
String rpt = entitlementRequest.getRpt(); String rpt = entitlementRequest.getRpt();
if (rpt != null && !"".equals(rpt)) { if (rpt != null && !"".equals(rpt)) {
KeycloakContext context = authorization.getKeycloakSession().getContext(); KeycloakContext context = authorization.getKeycloakSession().getContext();
if (!Tokens.verifySignature(rpt, context.getRealm().getPublicKey())) { if (!Tokens.verifySignature(rpt, context.getRealm().getPublicKey())) {
throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN); throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
} }
@ -231,28 +268,47 @@ public class EntitlementService {
AccessToken.Authorization authorizationData = requestingPartyToken.getAuthorization(); AccessToken.Authorization authorizationData = requestingPartyToken.getAuthorization();
if (authorizationData != null) { if (authorizationData != null) {
authorizationData.getPermissions().forEach(permission -> { List<Permission> permissions = authorizationData.getPermissions();
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
if (resourcePermission != null) { if (permissions != null) {
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId()); permissions.forEach(permission -> {
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
if (scopes == null) { if (resourcePermission != null) {
scopes = new HashSet<>(); Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
permissionsToEvaluate.put(resourcePermission.getId(), scopes);
if (scopes == null) {
scopes = new HashSet<>();
permissionsToEvaluate.put(resourcePermission.getId(), scopes);
}
Set<String> scopePermission = permission.getScopes();
if (scopePermission != null) {
scopes.addAll(scopePermission);
}
} }
});
scopes.addAll(permission.getScopes()); }
}
});
} }
} }
} }
return permissionsToEvaluate.entrySet().stream() return permissionsToEvaluate.entrySet().stream()
.flatMap((Function<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> { .flatMap((Function<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey()); String key = entry.getKey();
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
if ("$KC_SCOPE_PERMISSION".equals(key)) {
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
List<Scope> scopes = entry.getValue().stream().map(scopeName -> {
Scope byName = scopeStore.findByName(scopeName, resourceServer.getId());
return byName;
}).collect(Collectors.toList());
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
} else {
Resource entryResource = storeFactory.getResourceStore().findById(key);
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
}
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
} }

View file

@ -32,6 +32,7 @@ import org.keycloak.services.ErrorResponseException;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -64,51 +65,69 @@ public class AbstractPermissionService {
return request.stream().map(request1 -> { return request.stream().map(request1 -> {
String resourceSetId = request1.getResourceSetId(); String resourceSetId = request1.getResourceSetId();
String resourceSetName = request1.getResourceSetName(); String resourceSetName = request1.getResourceSetName();
boolean resourceNotProvider = resourceSetId == null && resourceSetName == null;
if (resourceSetId == null && resourceSetName == null) { if (resourceNotProvider) {
throw new ErrorResponseException("invalid_resource_set_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST); if ((request1.getScopes() == null || request1.getScopes().isEmpty())) {
} throw new ErrorResponseException("invalid_resource_set_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
Resource resource;
if (resourceSetId != null) {
resource = storeFactory.getResourceStore().findById(resourceSetId);
} else {
resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
}
if (resource == null) {
if (resourceSetId != null) {
throw new ErrorResponseException("nonexistent_resource_set_id", "Resource set with id[" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
} else {
throw new ErrorResponseException("nonexistent_resource_set_name", "Resource set with name[" + resourceSetName + "] does not exists in this server.", Response.Status.BAD_REQUEST);
} }
} }
return new ResourceRepresentation(resource.getName(), verifyRequestedScopes(request1, resource)); Resource resource = null;
if (!resourceNotProvider) {
if (resourceSetId != null) {
resource = storeFactory.getResourceStore().findById(resourceSetId);
} else {
resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
}
if (resource == null) {
if (resourceSetId != null) {
throw new ErrorResponseException("nonexistent_resource_set_id", "Resource set with id[" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
} else {
throw new ErrorResponseException("nonexistent_resource_set_name", "Resource set with name[" + resourceSetName + "] does not exists in this server.", Response.Status.BAD_REQUEST);
}
}
}
Set<ScopeRepresentation> scopes = verifyRequestedScopes(request1, resource);
if (resource != null) {
if (scopes.isEmpty() && !request1.getScopes().isEmpty()) {
return new ResourceRepresentation(null, request1.getScopes().stream().map(ScopeRepresentation::new).collect(Collectors.toSet()));
}
return new ResourceRepresentation(resource.getName(), scopes);
}
return new ResourceRepresentation(null, scopes);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) { private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) {
return request.getScopes().stream().map(scopeName -> { return request.getScopes().stream().map(scopeName -> {
for (Scope scope : resource.getScopes()) { if (resource != null) {
if (scope.getName().equals(scopeName)) { for (Scope scope : resource.getScopes()) {
return new ScopeRepresentation(scopeName); if (scope.getName().equals(scopeName)) {
return new ScopeRepresentation(scopeName);
}
} }
}
for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType())) { for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType())) {
if (baseResource.getOwner().equals(resource.getResourceServer().getClientId())) { if (baseResource.getOwner().equals(resource.getResourceServer().getClientId())) {
for (Scope baseScope : baseResource.getScopes()) { for (Scope baseScope : baseResource.getScopes()) {
if (baseScope.getName().equals(scopeName)) { if (baseScope.getName().equals(scopeName)) {
return new ScopeRepresentation(scopeName); return new ScopeRepresentation(scopeName);
}
} }
} }
} }
}
throw new ErrorResponseException("invalid_scope", "Scope [" + scopeName + " is not valid.", Response.Status.BAD_REQUEST); return null;
}).collect(Collectors.toSet()); } else {
return new ScopeRepresentation(scopeName);
}
}).filter(scopeRepresentation -> scopeRepresentation != null).collect(Collectors.toSet());
} }
private String createPermissionTicket(List<ResourceRepresentation> resources) { private String createPermissionTicket(List<ResourceRepresentation> resources) {

View file

@ -110,38 +110,54 @@ public final class Permissions {
return permissions; return permissions;
} }
public static List<Permission> allPermits(List<Result> evaluation) { public static List<Permission> allPermits(List<Result> evaluation, AuthorizationProvider authorizationProvider) {
Map<String, Permission> permissions = new HashMap<>(); Map<String, Permission> permissions = new HashMap<>();
for (Result evaluationResult : evaluation) { for (Result evaluationResult : evaluation) {
ResourcePermission permission = evaluationResult.getPermission(); ResourcePermission permission = evaluationResult.getPermission();
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()); Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
if (evaluationResult.getEffect().equals(Effect.DENY)) { if (evaluationResult.getEffect().equals(Effect.DENY)) {
continue; continue;
} }
List<Resource> resources = new ArrayList<>();
Resource resource = permission.getResource(); Resource resource = permission.getResource();
if (resource != null) { if (resource != null) {
String resourceId = resource.getId(); resources.add(resource);
String resourceName = resource.getName(); } else {
Permission evalPermission = permissions.get(resource.getId()); List<Scope> permissionScopes = permission.getScopes();
if (evalPermission == null) { if (!permissionScopes.isEmpty()) {
evalPermission = new Permission(resourceId, resourceName, scopes); ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
permissions.put(resourceId, evalPermission); resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()).toArray(new String[permissionScopes.size()])));
} }
}
if (scopes != null && !scopes.isEmpty()) { if (!resources.isEmpty()) {
Set<String> finalScopes = evalPermission.getScopes(); for (Resource allowedResource : resources) {
String resourceId = allowedResource.getId();
String resourceName = allowedResource.getName();
Permission evalPermission = permissions.get(allowedResource.getId());
if (finalScopes == null) { if (evalPermission == null) {
finalScopes = new HashSet(); evalPermission = new Permission(resourceId, resourceName, scopes);
evalPermission.setScopes(finalScopes); permissions.put(resourceId, evalPermission);
} }
for (String scopeName : scopes) { if (scopes != null && !scopes.isEmpty()) {
if (!finalScopes.contains(scopeName)) { Set<String> finalScopes = evalPermission.getScopes();
finalScopes.add(scopeName);
if (finalScopes == null) {
finalScopes = new HashSet();
evalPermission.setScopes(finalScopes);
}
for (String scopeName : scopes) {
if (!finalScopes.contains(scopeName)) {
finalScopes.add(scopeName);
}
} }
} }
} }

View file

@ -60,8 +60,8 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam
@Test @Test
public void testDefaultAuthzConfig() throws Exception { public void testDefaultAuthzConfig() throws Exception {
try { try {
this.deployer.deploy(RESOURCE_SERVER_ID);
configureAuthorizationServices(); configureAuthorizationServices();
this.deployer.deploy(RESOURCE_SERVER_ID);
login(); login();

View file

@ -1,5 +1,6 @@
package org.keycloak.testsuite.broker; package org.keycloak.testsuite.broker;
import org.junit.Ignore;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@ -12,6 +13,7 @@ import java.util.Map;
import static org.keycloak.testsuite.broker.BrokerTestConstants.*; import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
@Ignore
public class KcSamlBrokerTest extends AbstractBrokerTest { public class KcSamlBrokerTest extends AbstractBrokerTest {
@Override @Override

View file

@ -1,5 +1,6 @@
package org.keycloak.testsuite.broker; package org.keycloak.testsuite.broker;
import org.junit.Ignore;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
@ -10,6 +11,7 @@ import java.util.Map;
import static org.keycloak.testsuite.broker.BrokerTestConstants.*; import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
@Ignore
public class KcSamlSignedBrokerTest extends KcSamlBrokerTest { public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
@Override @Override

View file

@ -22,6 +22,7 @@ import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
@ -36,6 +37,9 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider; import org.keycloak.models.UserProvider;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.testsuite.federation.storage.UserMapStorageFactory;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
@ -831,6 +835,61 @@ public class AdapterTest extends AbstractModelTest {
realmManager.removeRealm(realmModel); realmManager.removeRealm(realmModel);
} }
@Test
public void testComponentModelCRUD() {
// Add
realmModel = realmManager.createRealm("foo-realm");
UserStorageProviderModel model = new UserStorageProviderModel();
model.setName("memory");
model.setPriority(0);
model.setProviderId(UserMapStorageFactory.PROVIDER_ID);
model.setParentId(realmModel.getId());
ComponentModel createdModel = realmModel.addComponentModel(model);
String id = createdModel.getId();
Assert.assertNotNull(id);
commit();
realmModel = realmManager.getRealmByName("foo-realm");
ComponentModel foundModel = realmModel.getComponent(id);
assertComponentModel(foundModel, id, UserMapStorageFactory.PROVIDER_ID, realmModel.getId(), "memory");
List<ComponentModel> components = realmModel.getComponents();
Assert.assertEquals(components.size(), 1);
assertComponentModel(components.get(0), id, UserMapStorageFactory.PROVIDER_ID, realmModel.getId(), "memory");
components = realmModel.getComponents(realmModel.getId(), UserStorageProvider.class.getName());
Assert.assertEquals(components.size(), 1);
assertComponentModel(components.get(0), id, UserMapStorageFactory.PROVIDER_ID, realmModel.getId(), "memory");
// Update
foundModel.getConfig().putSingle("foo", "bar");
realmModel.updateComponent(foundModel);
commit();
realmModel = realmManager.getRealmByName("foo-realm");
foundModel = realmModel.getComponent(id);
assertComponentModel(foundModel, id, UserMapStorageFactory.PROVIDER_ID, realmModel.getId(), "memory");
Assert.assertEquals("bar", foundModel.getConfig().getFirst("foo"));
// Remove
realmModel.removeComponent(foundModel);
commit();
realmModel = realmManager.getRealmByName("foo-realm");
foundModel = realmModel.getComponent(id);
Assert.assertNull(foundModel);
}
private void assertComponentModel(ComponentModel componentModel, String expectedId, String expectedProviderId, String expectedParentId, String expectedName) {
Assert.assertEquals(expectedId, componentModel.getId());
Assert.assertEquals(expectedProviderId, componentModel.getProviderId());
Assert.assertEquals(expectedParentId, componentModel.getParentId());
Assert.assertEquals(expectedName, componentModel.getName());
}
private KeyPair generateKeypair() throws NoSuchAlgorithmException { private KeyPair generateKeypair() throws NoSuchAlgorithmException {
return KeyPairGenerator.getInstance("RSA").generateKeyPair(); return KeyPairGenerator.getInstance("RSA").generateKeyPair();
} }

View file

@ -1094,7 +1094,7 @@ authz-evaluation-any-resource-with-scopes=Any resource with scope(s)
authz-evaluation-no-result=Could not obtain any result for the given authorization request. Check if the provided resource(s) or scope(s) are associated with any policy. authz-evaluation-no-result=Could not obtain any result for the given authorization request. Check if the provided resource(s) or scope(s) are associated with any policy.
authz-evaluation-no-policies-resource=No policies were found for this resource. authz-evaluation-no-policies-resource=No policies were found for this resource.
authz-evaluation-result.tooltip=The overall result for this permission request. authz-evaluation-result.tooltip=The overall result for this permission request.
authz-evaluation-scopes.tooltip=The requested scopes. authz-evaluation-scopes.tooltip=The list of allowed scopes.
authz-evaluation-policies.tooltip=Details about which policies were evaluated and their decisions. authz-evaluation-policies.tooltip=Details about which policies were evaluated and their decisions.
authz-evaluation-authorization-data=Response authz-evaluation-authorization-data=Response
authz-evaluation-authorization-data.tooltip=Represents a token carrying authorization data as a result of the processing of an authorization request. This representation is basically what Keycloak issues to clients asking for permissions. Check the 'authorization' claim for the permissions that were granted based on the current authorization request. authz-evaluation-authorization-data.tooltip=Represents a token carrying authorization data as a result of the processing of an authorization request. This representation is basically what Keycloak issues to clients asking for permissions. Check the 'authorization' claim for the permissions that were granted based on the current authorization request.

View file

@ -1321,6 +1321,11 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
rsrid: $scope.newResource._id rsrid: $scope.newResource._id
}, function (data) { }, function (data) {
$scope.scopes = data.scopes; $scope.scopes = data.scopes;
if (data.typedScopes) {
for (i=0;i<data.typedScopes.length;i++) {
$scope.scopes.push(data.typedScopes[i]);
}
}
}); });
} else { } else {
ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) { ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {

View file

@ -21,11 +21,11 @@
<label class="col-md-2 control-label">{{:: 'authz-scopes' | translate}}</label> <label class="col-md-2 control-label">{{:: 'authz-scopes' | translate}}</label>
<div class="col-sm-2"> <div class="col-sm-2">
<span data-ng-show="result.scopes.length == 0">{{:: 'authz-any-scope' | translate}}</span> <span data-ng-show="result.allowedScopes.length == 0">{{:: 'authz-no-scopes-available' | translate}}</span>
<div> <div>
<ul> <ul>
<li data-ng-repeat="scope in result.scopes"> <li data-ng-repeat="scope in result.allowedScopes">
{{scope.name}} {{scope.name}}
</li> </li>
</ul> </ul>
@ -38,15 +38,14 @@
<div class="col-sm-6"> <div class="col-sm-6">
<span data-ng-show="result.policies.length == 0">{{:: 'authz-evaluation-no-policies-resource' | translate}}</span> <span data-ng-show="result.policies.length == 0">{{:: 'authz-evaluation-no-policies-resource' | translate}}</span>
<div> <div>
<ul> <div>
<li data-ng-repeat="policyResult in result.policies"> <li data-ng-repeat="policyResult in result.policies">
<strong><a <strong><a
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong> href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span> decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
<span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span> <span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision.</a> by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision. {{policyResult.scopes.length > 0 ? 'Denied Scopes:' : ''}} <span data-ng-repeat="scope in policyResult.scopes"><strong style="color: red">{{scope.name}}{{$last ? '' : ', '}}</strong></span>{{policyResult.scopes.length > 0 ? '.' : ''}}
<ul> <ul>
<li data-ng-repeat="subPolicy in policyResult.associatedPolicies"> <li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
<strong><a <strong><a