Skip to content

Commit

Permalink
Introducing a wrapper for policy to run schema validations
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Igor <[email protected]>
  • Loading branch information
pedroigor committed Dec 17, 2024
1 parent ed0b27f commit 32fc366
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ public class ResourcePermissionRepresentation extends AbstractPolicyRepresentati

private String resourceType;

public ResourcePermissionRepresentation() {
this(null);
}

public ResourcePermissionRepresentation(String name) {
setName(name);
}

@Override
public String getType() {
return "resource";
Expand All @@ -35,4 +43,4 @@ public void setResourceType(String resourceType) {
public String getResourceType() {
return resourceType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import jakarta.ws.rs.BadRequestException;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
Expand All @@ -41,7 +43,9 @@
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.Profile.Feature;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelValidationException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.models.utils.RepresentationToModel;
Expand Down Expand Up @@ -354,12 +358,12 @@ public Policy create(ResourceServer resourceServer, AbstractPolicyRepresentation
}).collect(Collectors.toSet()));
}

return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(resourceServer, representation));
return createSchemaAwarePolicy(RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(resourceServer, representation)));
}

@Override
public void delete(String id) {
Policy policy = findById(null, id);
Policy policy = createSchemaAwarePolicy(findById(null, id));

if (policy != null) {
ResourceServer resourceServer = policy.getResourceServer();
Expand Down Expand Up @@ -388,67 +392,71 @@ public void delete(String id) {

@Override
public Policy findById(ResourceServer resourceServer, String id) {
return policyStore.findById(resourceServer, id);
return createSchemaAwarePolicy(policyStore.findById(resourceServer, id));
}

@Override
public Policy findByName(ResourceServer resourceServer, String name) {
return policyStore.findByName(resourceServer, name);
return createSchemaAwarePolicy(policyStore.findByName(resourceServer, name));
}

@Override
public List<Policy> findByResourceServer(ResourceServer resourceServer) {
return policyStore.findByResourceServer(resourceServer);
return policyStore.findByResourceServer(resourceServer).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public List<Policy> find(ResourceServer resourceServer, Map<Policy.FilterOption, String[]> attributes, Integer firstResult, Integer maxResults) {
return policyStore.find(resourceServer, attributes, firstResult, maxResults);
return policyStore.find(resourceServer, attributes, firstResult, maxResults).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public List<Policy> findByResource(ResourceServer resourceServer, Resource resource) {
return policyStore.findByResource(resourceServer, resource);
return policyStore.findByResource(resourceServer, resource).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public void findByResource(ResourceServer resourceServer, Resource resource, Consumer<Policy> consumer) {
policyStore.findByResource(resourceServer, resource, consumer);
policyStore.findByResource(resourceServer, resource, policy -> consumer.accept(createSchemaAwarePolicy(policy)));
}

@Override
public List<Policy> findByResourceType(ResourceServer resourceServer, String resourceType) {
return policyStore.findByResourceType(resourceServer, resourceType);
return policyStore.findByResourceType(resourceServer, resourceType).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public List<Policy> findByScopes(ResourceServer resourceServer, List<Scope> scopes) {
return policyStore.findByScopes(resourceServer, scopes);
return policyStore.findByScopes(resourceServer, scopes).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public List<Policy> findByScopes(ResourceServer resourceServer, Resource resource, List<Scope> scopes) {
return policyStore.findByScopes(resourceServer, resource, scopes);
return policyStore.findByScopes(resourceServer, resource, scopes).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public void findByScopes(ResourceServer resourceServer, Resource resource, List<Scope> scopes, Consumer<Policy> consumer) {
policyStore.findByScopes(resourceServer, resource, scopes, consumer);
policyStore.findByScopes(resourceServer, resource, scopes, policy -> consumer.accept(createSchemaAwarePolicy(policy)));
}

@Override
public List<Policy> findByType(ResourceServer resourceServer, String type) {
return policyStore.findByType(resourceServer, type);
return policyStore.findByType(resourceServer, type).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public List<Policy> findDependentPolicies(ResourceServer resourceServer, String id) {
return policyStore.findDependentPolicies(resourceServer, id);
return policyStore.findDependentPolicies(resourceServer, id).stream().map(this::createSchemaAwarePolicy).collect(Collectors.toList());
}

@Override
public void findByResourceType(ResourceServer resourceServer, String type, Consumer<Policy> policyConsumer) {
policyStore.findByResourceType(resourceServer, type, policyConsumer);
policyStore.findByResourceType(resourceServer, type, policy -> policyConsumer.accept(createSchemaAwarePolicy(policy)));
}

private Policy createSchemaAwarePolicy(Policy byId) {
return Optional.ofNullable(byId).map((Function<Policy, Policy>) p -> new SchemaAwarePolicy(p, keycloakSession)).orElse(null);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.authorization;

import java.util.Map;
import java.util.Set;

import jakarta.ws.rs.BadRequestException;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.common.Profile.Feature;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelValidationException;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;

public class SchemaAwarePolicy implements Policy {

private Policy delegate;
private final KeycloakSession session;

public SchemaAwarePolicy(Policy policy, KeycloakSession session) {
this.delegate = policy;
this.session = session;
checkIfSupportedPolicyType();
}

@Override
public String getId() {
return delegate.getId();
}

@Override
public String getType() {
return delegate.getType();
}

@Override
public DecisionStrategy getDecisionStrategy() {
return delegate.getDecisionStrategy();
}

@Override
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
delegate.setDecisionStrategy(decisionStrategy);
}

@Override
public Logic getLogic() {
return delegate.getLogic();
}

@Override
public void setLogic(Logic logic) {
delegate.setLogic(logic);
}

@Override
public Map<String, String> getConfig() {
return delegate.getConfig();
}

@Override
public void setConfig(Map<String, String> config) {
delegate.setConfig(config);
}

@Override
public void removeConfig(String name) {
delegate.removeConfig(name);
}

@Override
public void putConfig(String name, String value) {
delegate.putConfig(name, value);
}

@Override
public String getName() {
return delegate.getName();
}

@Override
public void setName(String name) {
delegate.setName(name);
}

@Override
public String getDescription() {
return delegate.getDescription();
}

@Override
public void setDescription(String description) {
delegate.setDescription(description);
}

@Override
public ResourceServer getResourceServer() {
return delegate.getResourceServer();
}

@Override
public Set<Policy> getAssociatedPolicies() {
return delegate.getAssociatedPolicies();
}

@Override
public Set<Resource> getResources() {
return delegate.getResources();
}

@Override
public Set<Scope> getScopes() {
return delegate.getScopes();
}

@Override
public String getOwner() {
return delegate.getOwner();
}

@Override
public void setOwner(String owner) {
delegate.setOwner(owner);
}

@Override
public void addScope(Scope scope) {
delegate.addScope(scope);
}

@Override
public void removeScope(Scope scope) {
delegate.removeScope(scope);
}

@Override
public void addAssociatedPolicy(Policy associatedPolicy) {
delegate.addAssociatedPolicy(associatedPolicy);
}

@Override
public void removeAssociatedPolicy(Policy associatedPolicy) {
delegate.removeAssociatedPolicy(associatedPolicy);
}

@Override
public void addResource(Resource resource) {
delegate.addResource(resource);
}

@Override
public void removeResource(Resource resource) {
delegate.removeResource(resource);
}

private void checkIfSupportedPolicyType() throws BadRequestException {
if (AdminPermissionsAuthorizationSchema.INSTANCE.isSupportedPolicyType(session, getResourceServer(), getType())) {
return;
}

throw new ModelValidationException("Policy type not supported by feature " + Feature.ADMIN_FINE_GRAINED_AUTHZ_V2.getVersionedKey()); }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
import java.util.function.Predicate;
import java.util.stream.Collectors;

import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
Expand All @@ -44,7 +42,6 @@
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.authorization.AdminPermissionsAuthorizationSchema;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
Expand All @@ -56,7 +53,6 @@
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.common.Profile.Feature;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
Expand Down Expand Up @@ -94,16 +90,11 @@ public Object getResource(@PathParam("type") String type) {
PolicyProviderFactory providerFactory = getPolicyProviderFactory(type);

if (providerFactory != null) {
checkIfSupportedPolicyType(type);
return doCreatePolicyTypeResource(type);
}

Policy policy = authorization.getStoreFactory().getPolicyStore().findById(resourceServer, type);

if (policy != null) {
checkIfSupportedPolicyType(policy.getType());
}

return doCreatePolicyResource(policy);
}

Expand Down Expand Up @@ -144,8 +135,6 @@ protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
throw new RuntimeException("Failed to deserialize representation", cause);
}

checkIfSupportedPolicyType(representation.getType());

return representation;
}

Expand Down Expand Up @@ -292,7 +281,6 @@ public Response findAll(@QueryParam("policyId") String id,
}

protected AbstractPolicyRepresentation toRepresentation(Policy model, String fields, AuthorizationProvider authorization) {
checkIfSupportedPolicyType(model.getType());
return ModelToRepresentation.toRepresentation(model, authorization, true, false, fields != null && fields.equals("*"));
}

Expand Down Expand Up @@ -356,12 +344,4 @@ private void audit(AbstractPolicyRepresentation resource, String id, OperationTy
adminEvent.operation(operation).resourcePath(session.getContext().getUri()).representation(resource).success();
}
}

private void checkIfSupportedPolicyType(String type) throws BadRequestException {
if (AdminPermissionsAuthorizationSchema.INSTANCE.isSupportedPolicyType(authorization.getKeycloakSession(), resourceServer, type)) {
return;
}

throw new BadRequestException("Policy type not supported by feature " + Feature.ADMIN_FINE_GRAINED_AUTHZ_V2.getVersionedKey());
}
}
Loading

0 comments on commit 32fc366

Please sign in to comment.