Are there RBAC libraries capable of getting partial data from database based on policies?

I had the possibility of working at a project where we required fully configurable RBAC system capable of showing partial data for a user based on his context. I implemented at that time from scratch, guided by a white paper, I was wondering if something like that exists in elixir ecosystem currently?

As an example let’s say we have an organization entity, now each user assigned to a specific organization can see only the local data to that organization (related to how all users you create can see buckets created by you and not system wide in S3).

Now in terms of policies, there are 2 kinds (the names might be different in literature):

  1. General access policies (an example would be the possibility to add new data, policies like these are always used in frontend to either show/hide some part of functionality)
  2. Granular policies (an example of such policy is how you allow an user to have access only to one or more specific buckets in s3)

The requirements of the system we needed were the following (as to make it clear what kind of system is in place):

  1. Policies should be configurable at runtime from frontend; (just like you can configure AWS policies)
  2. Partial data access is not applicable to only one table. (ideally it should work correctly when the data is aggregated too)

Since finding a library for such a topic is harder than it seems, I was wondering if any of you have done something similar and know any libraries that can achieve this in elixir?

If not I was thinking about polishing the concept and releasing a library for painless dealing with this problem, as I find that I always arrive at the conclusion that 1 of 3 projects I do start as simple RBAC concepts and in the end with a custom implementation of this concept at database level.

1 Like

My current project has RBAC needs and when I looked for libraries a year ago or so I didn’t really find anything that fit the bill. Certainly not with the nuance I needed so I ended up just building something for the project.

I think one difference between what I’ve built and your requirements is that I think you’re looking for something which operates at a lower level of enforcement than my system aims to do; almost like a way to do application enforced row-based security instead of using the database roles for that purpose; I get this impression from your discussion of aggregates. In my system, for example, I might not have permission to view, say, product shipment details aside from my “own” orders, but I could well have permission to view aggregate shipment data which is derived from all shipping detail records. I take this to mean that what I’ve built is more disconnected from what it might be defining permissions for than what you’re thinking.

The link below describes what I ended up putting together:

Apologies for any bad writing in advance… wrote this pretty quickly primarily for future me.

I do think a library could be cool for more advanced RBAC needs either way.

2 Likes

We’ve an opinionated implementation at GitHub - bonfire-networks/bonfire_boundaries: Circles, ACLs, etc and while it’d be quite easy to extract into a more generic/reusable library, it would still require buying into Pointers (see Bonfire's Database - an intro — bonfire_umbrella v0.4.0-classic-beta.7 for a more verbose explanation). Is that something you’d be interested in?

2 Likes

This is exactly the most important requirement. Having permissions for a whole table is not granular enough, as in relational databases we tend to store data of the same structure in one table.

The other thing I was wondering if all of these concepts are standardized somewhere, because I’ve read a few short books and papers on this regard and most of them tend to use different terms and approaches to the problem, even the unix role system tends to get opinionated with some resources (for example directories).

Would you mind sharing the most helpful book/papers in this domain you’ve stumbled upon? I was intrigued by this discussion (had to look up RBAC), but was overwhelmed by the terminology. I could not find a good summarizing resource for this class of problems (access control in software?).

I would be happy to, however all this information was contained in my work laptop that I had to give when leaving the job :(.

I think that problem comes from the fact that we cannot generalize a resource, we might have different types of resources in the system and different policies for limiting them. For example we might have user permissions on frontend (simple permissions such as: view_posts, manage_users) and at the same time row level permissions for the data a user can see (for example an admin can edit only a specific set of users). From this problem people usually implement the easiest way to manage their specific permissions systems (for example for frontend you can use boolean flags).

A good starting point would be to look at books/talks/videos on how the unix system solved this problem, because most of the concepts from their implementation is used nowadays in design of new systems. My implementation was somewhat based on the unix approach with specific changes to the formula.

I would avoid reading any long books on the theory, as they tend to speculate a lot. The best sources of information I had is from practical examples of implementation, there you can analyze their implementation and possible problems or bottlenecks.

This is quite a lot of information, I kind of understood on why Pointers are used, however I got lost when it came to Mixins. I think the most important question is: is this system capable of row level enforcement at runtime(managed by user)?

Btw, @PJUllrich did blog about introducing a role based security system into phoenix applications.

1 Like

@D4no0 FYI Mixins are optional, you can stick to adding fields directly to Pointables instead.

And yes, you can:

  • set permissions for a row with Boundaries.set_boundaries(creator, object_or_id, opts) or Acls.cast(changeset, creator, opts)
  • check for permission with Boundaries.can?(user, :reply, object_or_id)
  • enforce permissions by filtering an ecto query with Queries.boundarise(query, object.id, verbs: [:read], current_user: user)