-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Keycloak Authorization dynamic tenant config resolution
- Loading branch information
1 parent
24326d0
commit 24aef29
Showing
21 changed files
with
1,728 additions
and
364 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
...k-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/PolicyEnforcerResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package io.quarkus.keycloak.pep; | ||
|
||
import org.keycloak.adapters.authorization.PolicyEnforcer; | ||
|
||
import io.quarkus.oidc.OidcTenantConfig; | ||
import io.smallrye.mutiny.Uni; | ||
import io.vertx.ext.web.RoutingContext; | ||
|
||
/** | ||
* A {@link PolicyEnforcer} resolver. | ||
*/ | ||
public interface PolicyEnforcerResolver { | ||
|
||
Uni<PolicyEnforcer> resolvePolicyEnforcer(RoutingContext routingContext, OidcTenantConfig tenantConfig); | ||
|
||
long getReadTimeout(); | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
...thorization/runtime/src/main/java/io/quarkus/keycloak/pep/TenantPolicyConfigResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package io.quarkus.keycloak.pep; | ||
|
||
import io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerTenantConfig; | ||
import io.quarkus.oidc.OidcRequestContext; | ||
import io.quarkus.oidc.OidcTenantConfig; | ||
import io.smallrye.mutiny.Uni; | ||
import io.vertx.ext.web.RoutingContext; | ||
|
||
/** | ||
* A tenant resolver is responsible for resolving the {@link KeycloakPolicyEnforcerTenantConfig} for tenants, dynamically. | ||
*/ | ||
public interface TenantPolicyConfigResolver { | ||
|
||
/** | ||
* Returns a {@link KeycloakPolicyEnforcerTenantConfig} given a {@code RoutingContext} and tenant id. | ||
* | ||
* @param routingContext routing context; nullable | ||
* @param tenantConfig tenant config; never null | ||
* @param requestContext request context; never null | ||
* | ||
* @return the tenant configuration. If the uni resolves to {@code null}, indicates that the default | ||
* configuration/tenant should be chosen | ||
*/ | ||
Uni<KeycloakPolicyEnforcerTenantConfig> resolve(RoutingContext routingContext, OidcTenantConfig tenantConfig, | ||
OidcRequestContext<KeycloakPolicyEnforcerTenantConfig> requestContext); | ||
|
||
} |
119 changes: 119 additions & 0 deletions
119
.../runtime/src/main/java/io/quarkus/keycloak/pep/runtime/DefaultPolicyEnforcerResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package io.quarkus.keycloak.pep.runtime; | ||
|
||
import static io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerUtil.createPolicyEnforcer; | ||
import static io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerUtil.getOidcTenantConfig; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.function.Supplier; | ||
|
||
import jakarta.enterprise.inject.Instance; | ||
import jakarta.inject.Singleton; | ||
|
||
import org.keycloak.adapters.authorization.PolicyEnforcer; | ||
|
||
import io.quarkus.arc.InjectableInstance; | ||
import io.quarkus.keycloak.pep.PolicyEnforcerResolver; | ||
import io.quarkus.keycloak.pep.TenantPolicyConfigResolver; | ||
import io.quarkus.oidc.OidcTenantConfig; | ||
import io.quarkus.oidc.runtime.BlockingTaskRunner; | ||
import io.quarkus.oidc.runtime.OidcConfig; | ||
import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; | ||
import io.quarkus.tls.TlsConfiguration; | ||
import io.quarkus.tls.TlsConfigurationRegistry; | ||
import io.quarkus.vertx.http.runtime.HttpConfiguration; | ||
import io.smallrye.mutiny.Uni; | ||
import io.vertx.ext.web.RoutingContext; | ||
|
||
@Singleton | ||
public class DefaultPolicyEnforcerResolver implements PolicyEnforcerResolver { | ||
|
||
private final TenantPolicyConfigResolver dynamicConfigResolver; | ||
private final BlockingTaskRunner<KeycloakPolicyEnforcerTenantConfig> requestContext; | ||
private final Map<String, PolicyEnforcer> namedPolicyEnforcers; | ||
private final PolicyEnforcer defaultPolicyEnforcer; | ||
private final long readTimeout; | ||
private final boolean globalTrustAll; | ||
|
||
DefaultPolicyEnforcerResolver(OidcConfig oidcConfig, KeycloakPolicyEnforcerConfig config, | ||
HttpConfiguration httpConfiguration, BlockingSecurityExecutor blockingSecurityExecutor, | ||
Instance<TenantPolicyConfigResolver> configResolver, | ||
InjectableInstance<TlsConfigurationRegistry> tlsConfigRegistryInstance) { | ||
this.readTimeout = httpConfiguration.readTimeout.toMillis(); | ||
|
||
if (tlsConfigRegistryInstance.isResolvable()) { | ||
this.globalTrustAll = tlsConfigRegistryInstance.get().getDefault().map(TlsConfiguration::isTrustAll).orElse(false); | ||
} else { | ||
this.globalTrustAll = false; | ||
} | ||
|
||
this.defaultPolicyEnforcer = createPolicyEnforcer(oidcConfig.defaultTenant, config.defaultTenant(), globalTrustAll); | ||
this.namedPolicyEnforcers = createNamedPolicyEnforcers(oidcConfig, config, globalTrustAll); | ||
if (configResolver.isResolvable()) { | ||
this.dynamicConfigResolver = configResolver.get(); | ||
this.requestContext = new BlockingTaskRunner<>(blockingSecurityExecutor); | ||
} else { | ||
this.dynamicConfigResolver = null; | ||
this.requestContext = null; | ||
} | ||
} | ||
|
||
@Override | ||
public Uni<PolicyEnforcer> resolvePolicyEnforcer(RoutingContext routingContext, OidcTenantConfig tenantConfig) { | ||
if (tenantConfig == null) { | ||
return Uni.createFrom().item(defaultPolicyEnforcer); | ||
} | ||
if (dynamicConfigResolver == null) { | ||
return Uni.createFrom().item(getStaticPolicyEnforcer(tenantConfig.tenantId.get())); | ||
} else { | ||
return getDynamicPolicyEnforcer(routingContext, tenantConfig) | ||
.onItem().ifNull().continueWith(new Supplier<PolicyEnforcer>() { | ||
@Override | ||
public PolicyEnforcer get() { | ||
return getStaticPolicyEnforcer(tenantConfig.tenantId.get()); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
@Override | ||
public long getReadTimeout() { | ||
return readTimeout; | ||
} | ||
|
||
PolicyEnforcer getStaticPolicyEnforcer(String tenantId) { | ||
return tenantId != null && namedPolicyEnforcers.containsKey(tenantId) | ||
? namedPolicyEnforcers.get(tenantId) | ||
: defaultPolicyEnforcer; | ||
} | ||
|
||
boolean hasDynamicPolicyEnforcers() { | ||
return dynamicConfigResolver != null; | ||
} | ||
|
||
private Uni<PolicyEnforcer> getDynamicPolicyEnforcer(RoutingContext routingContext, OidcTenantConfig config) { | ||
return dynamicConfigResolver.resolve(routingContext, config, requestContext) | ||
.onItem().ifNotNull().transform(new Function<KeycloakPolicyEnforcerTenantConfig, PolicyEnforcer>() { | ||
@Override | ||
public PolicyEnforcer apply(KeycloakPolicyEnforcerTenantConfig tenant) { | ||
return createPolicyEnforcer(config, tenant, globalTrustAll); | ||
} | ||
}); | ||
} | ||
|
||
private static Map<String, PolicyEnforcer> createNamedPolicyEnforcers(OidcConfig oidcConfig, | ||
KeycloakPolicyEnforcerConfig config, boolean tlsConfigTrustAll) { | ||
if (config.namedTenants().isEmpty()) { | ||
return Map.of(); | ||
} | ||
|
||
Map<String, PolicyEnforcer> policyEnforcerTenants = new HashMap<>(); | ||
for (Map.Entry<String, KeycloakPolicyEnforcerTenantConfig> tenant : config.namedTenants().entrySet()) { | ||
OidcTenantConfig oidcTenantConfig = getOidcTenantConfig(oidcConfig, tenant.getKey()); | ||
policyEnforcerTenants.put(tenant.getKey(), | ||
createPolicyEnforcer(oidcTenantConfig, tenant.getValue(), tlsConfigTrustAll)); | ||
} | ||
return Map.copyOf(policyEnforcerTenants); | ||
} | ||
} |
Oops, something went wrong.