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

Add OPAPathSelector to customize path selection #23

Open
ali-jalaal opened this issue Jan 26, 2025 · 3 comments
Open

Add OPAPathSelector to customize path selection #23

ali-jalaal opened this issue Jan 26, 2025 · 3 comments

Comments

@ali-jalaal
Copy link
Contributor

Expected behaviour

Enable clients to choose path based on incoming HTTP request.

Actual behaviour

Currently opaPath can only be set by constructor. This means that the same opaPath will be used for all HTTP
requests. However, to better structure the policies, it would be beneficial to be able to set the opaPath based on
the incoming HTTP request attributes (Authentication or RequestAuthorizationContext objects).

How to improve

  1. Defining OPAPathSelector interface in the library:
@FunctionalInterface
public interface OPAPathSelector {
    String selectPath(Authentication authentication, RequestAuthorizationContext requestAuthorizationContext);
}
  1. This bean can be passed to the OPAAuthorizationManager constructor(s) and be called in the opaRequest method:
public class OPAAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
    ...
    private OPAPathSelector opaPathSelector;

    public OPAAuthorizationManager(OPAClient opaClient, OPAPathSelector opaPathSelector, ContextDataProvider newProvider, String reasonKey) {
        this.opa = opaClient;
        this.opaPathSelector = opaPathSelector;
        this.ctxProvider = newProvider;
        this.reasonKey = reasonKey;
    }
    ...

    public OPAResponse opaRequest(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
        String path = opaPathSelector != null ? opaPathSelector.selectPath(authentication.get(), object) : opaPath;
        if (path != null) {
            logger.trace("OPA path is {}", path);
            resp = opa.evaluate(
                    path,
                    iMap,
                    new TypeReference<OPAResponse>() {}
            );
        ...
    }
  1. Default implementation can be provided in OPAAutoConfiguration and default OPAAuthorizationManager bean will be updated (depends on Add OPAAutoConfiguration to define common beans #22):
public class OpaAutoConfiguration {
    ...
    @Bean
    @ConditionalOnMissingBean
    public OPAPathSelector opaPathSelector(OPAProperties opaProperties) {
        return (authentication, requestAuthorizationContext) -> opaProperties.opaPath;
    }

    @Bean
    @ConditionalOnMissingBean(OPAAuthorizationManager.class)
    public OPAAuthorizationManager opaAuthorizationManager(OpaProperties opaProperties) {
        return new OPAAuthorizationManager(opaClient(opaProperties), opaPathSelector(opaProperties), null, opaProperties.getReasonKey());
    }
}
  1. Clients can then create a bean for customized OPAPathSelector:
@Configuration
public class OPAConfig {
    @Bean
    public OPAPathSelector opaPathSelector() {
        return (authentication, requestAuthorizationContext) -> {
            String httpRequestPath = requestAuthorizationContext.getRequest().getServletPath();
            if (httpRequestPath.startsWith("/customers")) {
                return "customer";
            } else if (httpRequestPath.startsWith("/tickets")) {
                return "ticket";
            } else {
                return "default";
            }
        };
    }
}
@chendrix
Copy link
Collaborator

So the "other method" for doing this right now is by hitting a single opaPath and then doing the routing in rego, which was the approach we were originally thinking people would adopt as it centralizes the logic into rego instead of being split across the application/rego divide.

What are your thoughts on that approach vs in-code? Do you have an example of where you would require or prefer in-code?

@ali-jalaal
Copy link
Contributor Author

Adding OPAPathSelector is meaningful where selecting the target policy would need some contextual info that is:

  • only needed for policy selection (not for making decision)
  • potentially large in size or not easily serializable

For example, when using this library in an API Gateway (e.g., Spring Cloud Gateway), which supports various types of Authentication objects, we might want to select a specific policy based on attributes of each authentication object.

That said, I agree that if the approach is to keep routing in rego to avoid splitting decision logic, it would be preferable to continue doing so.

@anderseknert
Copy link
Member

Routing in Rego when possible has the benefit of OPA reusing the same prepared query across all requests. But I agree that there are cases where you might have have a few entirely different queries, and having a distinct path to represent might be a better option. So I think this sound like a good addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants