Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to set OIDC tenant using annotations #22974

Closed
j-be opened this issue Jan 18, 2022 · 7 comments · Fixed by #23086
Closed

Allow to set OIDC tenant using annotations #22974

j-be opened this issue Jan 18, 2022 · 7 comments · Fixed by #23086
Labels
area/oidc kind/enhancement New feature or request
Milestone

Comments

@j-be
Copy link
Contributor

j-be commented Jan 18, 2022

Description

Lets assume one has a big API in a single Quarkus service (i.e. quarkus.oidc.application-type=service), but different APIs are for a different group of people. Like: this JAX-RS annotated class provides endpoints solely for HR, this other class solely for finances, this class is solely for engineering, and so on.

Now, usually one would solve that using roles, but a problem arises when those groups of people are authenticated by different OIDC providers.

We can still leverage OIDC multi-tenancy to solve the issue, but TenantResolver.resolve(RoutingContext) offers very little information (apart from the raw request path/URL) to base the decision on.

In such scenarios it would be really nice to be able to specify the tenant using annotations, like:

@TenantId("hr")
@RolesAllowed("admin")
@GET @Path("/")
public String getSomething() {
    return "something";
}

Implementation ideas

As proposed by @mkouba on Zulip one could leverage an @Interceptor to manipulate RoutingContext and use that in TenantResolver, like:

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface HrTenant {
}
@Interceptor
@HrTenant
public class HrTenantInterceptor {
    @Inject
    RoutingContext routingContext;

    @AroundInvoke
    Object setTenant(InvocationContext context) throws Exception {
        routingContext.put(OidcUtils.TENANT_ID_ATTRIBUTE, "hr");
        return context.proceed();
    }
}
@ApplicationScoped
public class OidcTenantResolver implements TenantResolver {
    @Override
    public String resolve(RoutingContext context) {
        return context.get(OidcUtils.TENANT_ID_ATTRIBUTE);
    }
}

As @sberyozkin pointed out: by patching DefaultTenantResolver to check for the value we should even be able to avoid a dedictated TenantResolver.

With a bit more effort it should also be able to implement a parameterized version of the annotation, like:

@Inherited
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface TenantId {
  @NonBinding String value();
}

The code providing support for @CacheInvalidateAll with its cacheName parameter should be exactly the same as we'd need here. But I think that is not worth posting here.

@j-be j-be added the kind/enhancement New feature or request label Jan 18, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Jan 18, 2022

/cc @pedroigor, @sberyozkin

@sberyozkin
Copy link
Member

@j-be I think your approach is the simplest; with Cache interceptors, since the annotation value is non-binding, they iterate over all the matching annotations. The custom interceptor you have typed is straightforward, so for now I believe we should just tweak quarkus-oidc a bit and doc how to write such an interrceptor

@j-be
Copy link
Contributor Author

j-be commented Jan 19, 2022

@sberyozkin Sounds good to me. If you point me to how and where I can do the documentation update - I'm more than happy to provide a rough draft. Same goes for DefaultTenantResolver - I can't seem to find any such class for OIDC tenants. https://quarkus.io/guides/security-openid-connect-multitenancy#writing-the-application only seems to suggest that there is one for HibernateORM.

@sberyozkin
Copy link
Member

@j-be Sorry, I mistyped, it is an internal DefaultTenantConfigResolver. I believe

String tenantId = null;

        if (tenantResolver.isResolvable()) {
            tenantId = context.get(CURRENT_STATIC_TENANT_ID);
            if (tenantId == null && context.get(CURRENT_STATIC_TENANT_ID_NULL) == null) {
                tenantId = tenantResolver.get().resolve(context);
                if (tenantId != null) {
                    context.put(CURRENT_STATIC_TENANT_ID, tenantId);
                } else {
                    context.put(CURRENT_STATIC_TENANT_ID_NULL, true);
                }
            }
        }

can be optimized/replaced with

String tenantId = context.get(CURRENT_STATIC_TENANT_ID);
 if (tenantId == null && context.get(CURRENT_STATIC_TENANT_ID_NULL) == null) {
       if (tenantResolver.isResolvable()) {
                tenantId = tenantResolver.get().resolve(context);
       } else {
                tenantId = context.get(OidcUtils.TENANT_ID_ATTRIBUTE);
       }
}

if (tenantId != null) {
                    context.put(CURRENT_STATIC_TENANT_ID, tenantId);
                } else {
                    context.put(CURRENT_STATIC_TENANT_ID_NULL, true);
                }

Looks like the oidc-multi-tenancy guide needs to be refactored a bit - I can look into it later on. But for now I'd suggest to add a new subsection, just above https://quarkus.io/guides/security-openid-connect-multitenancy#programmatically-resolving-tenants-configuration, say Resolving Tenant Identifiers with Annotations where we can start with something like You can use the annotations and CDI interceptors for resolving the tenant identifiers as an alternative to using quarkus.oidc.TenantResolver, for example:, with your examples, something like that ?

If you can create a PR then it would be appreciated

@j-be
Copy link
Contributor Author

j-be commented Jan 19, 2022

If you can create a PR then it would be appreciated

Sure thing, always happy to help 😃

@sberyozkin
Copy link
Member

@j-be Thanks

j-be added a commit to j-be/quarkus that referenced this issue Jan 21, 2022
j-be added a commit to j-be/quarkus that referenced this issue Feb 8, 2022
j-be added a commit to j-be/quarkus that referenced this issue Feb 8, 2022
@quarkus-bot quarkus-bot bot added this to the 2.8 - main milestone Feb 9, 2022
@Eng-Fouad
Copy link
Contributor

Related #34692

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/oidc kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants