Skip to content

Commit

Permalink
Refactor the way HTTP permissions are applied
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Oct 29, 2019
1 parent 9b960e3 commit ba5a999
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 173 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

Expand All @@ -18,45 +19,45 @@
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;

import io.quarkus.arc.AlternativePriority;
import io.quarkus.oidc.OidcConfig;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpAuthorizer;
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy;
import io.vertx.ext.web.RoutingContext;

@Singleton
@AlternativePriority(1)
public class KeycloakPolicyEnforcerAuthorizer extends HttpAuthorizer {
public class KeycloakPolicyEnforcerAuthorizer
implements HttpSecurityPolicy, BiFunction<RoutingContext, SecurityIdentity, HttpSecurityPolicy.CheckResult> {

private KeycloakAdapterPolicyEnforcer delegate;

@Override
public CompletionStage<SecurityIdentity> checkPermission(RoutingContext routingContext) {
public CompletionStage<CheckResult> checkPermission(RoutingContext request, SecurityIdentity identity,
AuthorizationRequestContext requestContext) {
return requestContext.runBlocking(request, identity, this);
}

@Override
public CheckResult apply(RoutingContext routingContext, SecurityIdentity identity) {

VertxHttpFacade httpFacade = new VertxHttpFacade(routingContext);
AuthorizationContext result = delegate.authorize(httpFacade);

if (result.isGranted()) {
QuarkusHttpUser user = (QuarkusHttpUser) routingContext.user();

if (user == null) {
return attemptAnonymousAuthentication(routingContext);
}

return enhanceSecurityIdentity(user.getSecurityIdentity(), result);
SecurityIdentity newIdentity = enhanceSecurityIdentity(identity, result);
return new CheckResult(true, newIdentity);
}

return CompletableFuture.completedFuture(null);
return CheckResult.DENY;
}

private CompletableFuture<SecurityIdentity> enhanceSecurityIdentity(SecurityIdentity current,
private SecurityIdentity enhanceSecurityIdentity(SecurityIdentity current,
AuthorizationContext context) {
Map<String, Object> attributes = new HashMap<>(current.getAttributes());

attributes.put("permissions", context.getPermissions());

return CompletableFuture.completedFuture(new QuarkusSecurityIdentity.Builder()
return new QuarkusSecurityIdentity.Builder()
.addAttributes(attributes)
.setPrincipal(current.getPrincipal())
.addRoles(current.getRoles())
Expand All @@ -82,7 +83,7 @@ public CompletionStage<Boolean> apply(Permission permission) {

return CompletableFuture.completedFuture(false);
}
}).build());
}).build();
}

public void init(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,28 @@
import io.quarkus.vertx.http.runtime.security.HttpAuthorizer;
import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy;
import io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder;
import io.quarkus.vertx.http.runtime.security.PathMatchingHttpSecurityPolicy;
import io.quarkus.vertx.http.runtime.security.PermitSecurityPolicy;
import io.quarkus.vertx.http.runtime.security.RolesAllowedHttpSecurityPolicy;
import io.quarkus.vertx.http.runtime.security.SupplierImpl;

public class HttpSecurityProcessor {

@BuildStep
public void builtins(BuildProducer<HttpSecurityPolicyBuildItem> producer, HttpBuildTimeConfig buildTimeConfig) {
public void builtins(BuildProducer<HttpSecurityPolicyBuildItem> producer, HttpBuildTimeConfig buildTimeConfig,
BuildProducer<AdditionalBeanBuildItem> beanProducer) {
producer.produce(new HttpSecurityPolicyBuildItem("deny", new SupplierImpl<>(new DenySecurityPolicy())));
producer.produce(new HttpSecurityPolicyBuildItem("permit", new SupplierImpl<>(new PermitSecurityPolicy())));
producer.produce(
new HttpSecurityPolicyBuildItem("authenticated", new SupplierImpl<>(new AuthenticatedHttpSecurityPolicy())));

if (!buildTimeConfig.auth.permissions.isEmpty()) {
beanProducer.produce(AdditionalBeanBuildItem.unremovableOf(PathMatchingHttpSecurityPolicy.class));
}
for (Map.Entry<String, PolicyConfig> e : buildTimeConfig.auth.rolePolicy.entrySet()) {
producer.produce(new HttpSecurityPolicyBuildItem(e.getKey(),
new SupplierImpl<>(new RolesAllowedHttpSecurityPolicy(e.getValue().rolesAllowed))));
}

}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
import java.util.concurrent.CompletionStage;

import io.quarkus.security.identity.SecurityIdentity;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.RoutingContext;

/**
* permission checker that checks if the user is authenticated
*/
public class AuthenticatedHttpSecurityPolicy implements HttpSecurityPolicy {

@Override
public CompletionStage<CheckResult> checkPermission(HttpServerRequest request, SecurityIdentity identity) {
public CompletionStage<CheckResult> checkPermission(RoutingContext request, SecurityIdentity identity,
AuthorizationRequestContext requestContext) {
return CompletableFuture.completedFuture(identity.isAnonymous() ? CheckResult.DENY : CheckResult.PERMIT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import java.util.concurrent.CompletionStage;

import io.quarkus.security.identity.SecurityIdentity;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.RoutingContext;

public class DenySecurityPolicy implements HttpSecurityPolicy {

public static final DenySecurityPolicy INSTANCE = new DenySecurityPolicy();

@Override
public CompletionStage<CheckResult> checkPermission(HttpServerRequest request, SecurityIdentity identity) {
public CompletionStage<CheckResult> checkPermission(RoutingContext request, SecurityIdentity identity,
AuthorizationRequestContext requestContext) {
return CompletableFuture.completedFuture(CheckResult.DENY);
}
}
Loading

0 comments on commit ba5a999

Please sign in to comment.