24

We have a medium sized business app and we use Spring Security roles and permissions (RBAC) heavily with a big kludge to turn roles on and off for certain instances plus rules hidden in SpEL within @PreAuthorize tags.

I think what we have actually implemented (without knowing it is ABAC). XACML looks very complicated and bloated so I'm not keen on the answer here:

How to change Spring Security roles by context?

Has anybody done a light weight ABAC implementation without XACML? I have hopes that would give us separation of concerns as the domain objects just do @PreAuthorize(WRITE) etc and our authorisation policy would be decoupled from it.

From what I've read the basic principal of ABAC is very simple. You have an Action (very like a Permission) and a mechanism to resolve if the current Principal has that permission on a given Subject.

I'm aware of AccessDecisionVoter which is roughly the right sort of interface but I don't think it was intended for voting on permissions. However implementing our authorisation policy with instances of something like those seems very attractive.

Sorry for the rambling question! Basically I'm interesting in ABAC but would like to avoid home brew but worried what XACML is a jumbo jet when we need a Cessna.

Neloop
  • 139
  • 7
salk31
  • 995
  • 2
  • 8
  • 13
  • What makes you think XACML is a tractor wheel? It's straightforward to implement – David Brossard Aug 14 '15 at 23:44
  • There's a direct integration between spring security and XACML through Axiomatics spring plugin – David Brossard Aug 14 '15 at 23:44
  • 3
    Hi David, Looking at XACML source is very verbose and complex. I've played with what the rules would look like in Java it is simple and we already have that skill set, it is compile time checked... Also Axiomatics seems to be the only really active work on XACML. I can imagine XACML/Axiomatics would be good for a large enterprise but that is not us. Sorry "tractor wheel" was ill judged. Jumbo jet when we need a Cessna? I'll edit my question. – salk31 Aug 15 '15 at 07:12

3 Answers3

18

There seems to be two things you are aiming for:

  1. externalized authorization, where you want to move access control policies out of the code (or at least into a central place in the code and not scattered across the Spring code)
  2. attribute based authorization, where you want to use richer attributes than roles and permissions

I am not very sure of (2) since what you say you want to do, "action and a mechanism to resolve if the current Principal has that permission", is still, in my books, RBAC. Do you have other conditions on which access grant decisions will need to be based on? Location of the user, time of day, value of certain data in a database, properties of the resource being acted on etc.? If so, we stray into the ABAC world. Either way, I would say that RBAC is a subset of ABAC since a role is just one attribute.

Now, as for (1), the general pattern would be to first centralize the authorization engine and use the Spring annotations to call this authz. engine for access decisions. You have two choices here:

  • embedded authz. engine: where a library implements the engine and is called by the code just as a Java function. Could be a XACML engine or could be your own RBAC/ABAC implementation
  • as a network service: where a network based (micro) service answers access control decision questions. Could be a XACML engine or could be your own RBAC/ABAC implementation

In order to get the Spring based code to call this authz. engine, one approach would be to write your own Spring Security voter. Another way, which I found much easier, would be to write your own Spring Expression Language based expressions that you can then call with the existing @PreAuthorize, @PostAuthorize, @PreFilter and @PostFiler, sec:authorize tags and even from intercept-url conditions.

This is what I used when I worked on our Spring Security XACML PEP SDK. The approach should work equally well even if you decide not to use XACML for your access decision policies or the request/response communication.

Glenn
  • 8,932
  • 2
  • 41
  • 54
Srijith Nair
  • 570
  • 3
  • 12
  • Many thanks for the detailed answer. To try and explain why I think I might need ABAC: One rule is "if adminRole or (!this.submitted and !this.historic and ((this.confidential and powerRole) or (!this.confidential and inASetOfPeopleResponsibleForThisObject)) sorry for pseudo code and obscure domain. I totally agree that part of the solution is RBAC but we have very few "static" roles compared to those that depend on a relationship with the object. – salk31 Aug 15 '15 at 07:07
  • 2
    Yup, what you describe definitely goes beyond RBAC. I would think that the approach I mentioned above would still work for you. – Srijith Nair Aug 17 '15 at 07:23
  • Thanks Srijith. If you would be so kind one last bit of sanity checking/hand holding. There is no open source ABAC engine that you know of? Reasonable to write our own simple micro-framework? I've knocked together a proof of concept but that is always the easy bit ;) – salk31 Aug 17 '15 at 08:11
  • .. and it would be nice if a lightweight ABAC implementation didn't go out of its way to be XACML incompatible so people that did find they needed something more powerful had an easier upgrade path. – salk31 Aug 17 '15 at 08:17
  • 2
    These are few open source ones: - https://en.wikipedia.org/wiki/XACML#Implementations - http://incubator.apache.org/projects/openaz.html – Srijith Nair Aug 18 '15 at 05:59
  • 1
    Thanks. OpenAZ looks interesting but still ABAC? I'm glad ABAC is getting more effort though. I've always been puzzled that RBAC doesn't even seem to solve the problem "I can edit my own records but not others". Your help is much appreciated. – salk31 Aug 18 '15 at 07:21
5

A pretty nice approach without XACML can be found here. It's basically what you wanted. A lighweight approach on ABAC without implementing XACML. https://dzone.com/articles/simple-attribute-based-access-control-with-spring

Aceonline
  • 344
  • 2
  • 12
  • We really wanted to get rid of SpEL (so we could refactor etc more easily). Very biased as our home brew approach but our rules are plain Java, each one is a Service so it is loaded and registered, the rules use generics (so strongly typed)... Then just PreAuthorize(Action.WRITE) on the methods (not SpEL just a token pointing at the rule) – salk31 Apr 25 '18 at 16:15
  • @salk31 Sounds pretty interessting. How did you get rid of SpEL? Did you overwrite the PreAuthorize annotation? How do you evaluate the Java rules? Sorry for the many questions. I'm just curious because i'm currently trying to implement something similar. – Aceonline May 09 '18 at 12:07
  • 1
    we are still migrating so our SpelExpressionParser wraps our action names in hasPermission(this .... ) Then we register services that implement String getAction, Class getType and boolean permit(AuthContext context)... I can try and shove it on github if you are interested, nothing very clever... – salk31 May 09 '18 at 12:53
3

I know this question is rather old, but recently we had need for similar ABAC permissions implementation. At first I tried to find something existing which would fill our needs, but everything seemed to be over the top and very hard to use.

So I came up with very simple library called jaclp which can be found on GitHub. It supports classical RBAC approach and more complex ABAC which can be used for example on JPA entities acting as resources. The integration and setup should be fairly simple.

One of the disadvantages is that for now, definition of permissions is programmatically only. I plan to introduce configuration file from which permission rules would be loaded. Also jaclp library for now supports only static permission rules defined in code, this means that you cannot load permission rules dynamically for example from database.

Example Usage:

// Applying permissions on REST endpoints

@GetMapping("groups/{id}")
@PreAuthorize("hasPermission(#id, 'group', 'viewDetail')")
public GroupDetailDTO getGroupDetail(@PathVariable long id) {
    return this.groupService.getGroupDetail(id);
}

// Defining role with both RBAC and ABAC approach

Role userRole = RoleBuilder.create("user")
        .addAllowedRule("group",
                (UserDetails user, GroupEntity group) -> group.isPublic(), 
                "viewDetail")
        .addAllowedRule("group", "viewAll")
        .build();
Neloop
  • 139
  • 7