#StackBounty: #spring #spring-security #authorization #abac How to integrate ABAC in a Spring application?

Bounty: 50

Spring Security offers means for authorization like annotations (@PreAuthorize) and security-specific SpEL expression like hasRole. In principle this expression language could be extended to support ABAC, however that wouldn’t really solve the architectural problem where to do it.

The application architecture is inspired by the Clean Architecture (which is in turn inspired by Hexagonal Architecture and the Onion Architecture). The basic building blocks look like this:

  • Adapter: connects to the outside world, e. g. HTTP endpoints
  • Application services: transforms to and from DTOs, provide transactions, perform authorization
  • Domain: describes business data, rules and behaviours

From my point of view the application layer is the only suitable layer to perform authorization. It wouldn’t fit into the adapter layer, because there could be multiple adapters for a single application service, so that the authorization would have to be repeated in several places. It wouldn’t obviously fit into the domain, because it is a technical aspect that shouldn’t be part of the domain logic.

A typical application service method gets some more or less "raw" parameters from the client, converts it and delegate the execution to the domain:

@Transactional
class MyApplicationService {
    fun cancelOrder(id: Long) {
        val orderId = OrderId(orderId)
        val order = orderRepository.findById(orderId)
        order.cancel()
    }
}

In order to be able to perform attribute-based access control (ABAC), the order object must be loaded before, so that the attributes can be extracted in the first place. That does also mean that an annotation like @PreAuthorize above the cancelOrder function wouldn’t work, because the order object wouldn’t be available there (SpEL would be able to reference function parameters, but the order is not passed as parameter!).

The only remaining possibility, that I can think of, is to call an authorization function explicitly before proceeding with business logic:

    fun cancelOrder(id: Long, principal: Principal) {
        val orderId = OrderId(id)
        val order = orderRepository.findById(orderId)
        checkPolicy(principal, order, Context.Complaint) // ?
        order.cancel()
    }

What concrete ABAC server (like OPA, AuthzForce, etc.) is not yet decided, but the solution at this level should be independent from it.

Is there a common approach for what I outlined as checkPolicy? Is there a typical "Spring approach" to integrate an external policy decision point? Or is there a fundamentally better solution?


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.