Skip to content

Add OPAPathSelector to customize path selection #23

Open
@ali-jalaal

Description

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";
            }
        };
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions