quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.36k stars 2.56k forks source link

Unable to add datasource and keycloak policy enforcer tenants with runtime-properties #28961

Open joggeli34 opened 1 year ago

joggeli34 commented 1 year ago

Description

We are currently implementing a quarkus service where we need multi tenancy for the data sources and the authorization / authentication. We like to add new tenants without creating a new build, but we noticed that the properties for that are built time only.

So we are looking for a possibility to add datasources and authorization with runtime properties.

Implementation ideas

For oidc multitenancy, the properties for additional tenants (example: quarkus.oidc."tenant".auth-server-url) are not marked as build-time and seems to working correctly.

Probably the one for the policy-enforcer and the data sources could probably be handled similar?

quarkus-bot[bot] commented 1 year ago

/cc @pedroigor, @sberyozkin

sberyozkin commented 1 year ago

One of the Keycloak Authz properties is checked at build time, but I guess we may be able to move this specific property only to the build time config

joggeli34 commented 1 year ago

As a workarond to support adding policy-enfrocer tenants at runtime, we had to create our own provider for the PolicyIenforcerResolver:

@Dependent
@RequiredArgsConstructor
@Slf4j
public class PolicyEnforcerResolverProvider {
    private final HttpConfiguration httpConfiguration;
    private final KeycloakPolicyEnforcerConfig keycloakPolicyEnforcerConfig;
    private final TlsConfig tlsConfig;
    private final OidcTenantConfigResolver oidcTenantConfigResolver;
    private final TenantService tenantService;

    @Singleton
    PolicyEnforcerResolver providePolicyEnforcerResolver() {
        return new KeycloakPolicyEnforcerRecorder(httpConfiguration)
                .setup(oidcTenantConfigResolver.buildCustomOidcConfig(), buildCustomPolicyEnforcerConfig(), tlsConfig)
                .get();
    }

    KeycloakPolicyEnforcerConfig buildCustomPolicyEnforcerConfig() {
        var config = new KeycloakPolicyEnforcerConfig();
        config.defaultTenant = keycloakPolicyEnforcerConfig.defaultTenant;
        config.namedTenants = tenantService
                .streamTenantIds()
                .collect(Collectors.toMap(Function.identity(), id -> keycloakPolicyEnforcerConfig.defaultTenant));
        return config;
    }
}

This also has the advantage, that we only have to configure the paths once for the default tenant and not for each named tenant.

Our configuration looks then as following:

# must be disabled, as we provide a custom version of it
quarkus.keycloak.policy-enforcer.enable=false
quarkus.keycloak.policy-enforcer.enforcement-mode=enforcing
quarkus.keycloak.policy-enforcer.paths.q-health.name=resource:public
quarkus.keycloak.policy-enforcer.paths.q-health.path=/q/health
quarkus.keycloak.policy-enforcer.paths.q-health.enforcement-mode=disabled

As we disable the default-policy enforcer, we also have to add a Custom KeyclaokPolicyEnforcerAuthorizer:

@Singleton
@Startup
public class CustomKeycloakPolicyEnforcerAuthorizer extends KeycloakPolicyEnforcerAuthorizer {}

At the end, we had to do workarounds for the database, the oidc and the keycloak-authorization as non of them supports adding tenants with runtime-config. I think this could be a use-case many of the users of multi-tenant application have.