1
votes

I have a need to implement a quite sophisticated RBAC model in my application. It very much resembles Azure RBAC: https://docs.microsoft.com/en-us/azure/role-based-access-control/overview

Let me explain it on an example:

Say I have a resource type: server_group.

And then I have roles:

  1. admin (can do CRUD on server_group + other permissions)
  2. editor (can do RU on server_group + other permissions)
  3. viewer (can only R server_group + other permissions)

Those roles are tenant-wide roles: the scope of operation here is the organization within which the current user is registered.

This is the traditional model of assigning permissions on roles for resource types.

But then, what if you have a need to grant more privileges to a certain user that has a role of a viewer but within a scope of a particular instance of a resource type?

Mark is an administrator, he has created a server_group sg1 and he wants to assign a role of a server_group_editor to Alice, who has a viewer tenant-wide role.

The server_group_editor role has a scope of application: server_group.

server_group_editor adds the ability to update the server_group resource type.

Since Alice has the role of server_group_editor within the sg1 instance, she now can edit this resource.

Previously she couldn’t do that as her tenant-wide permissions allowed her to only list all server groups.

I hope it makes sense.

That being said, I would love to know if there are products out there that support such a flexible RBAC model, be it either SaaS or applications that can be deployed as a standalone service?

I was looking at Auth0, Okta, KeyCloak, and I cannot see them supporting this model.

Thanks in advance!

1
This is not an Azure-rbac issue, but an Auth0, Okta, KeyCloak rbac issue. I removed the Azure rbac tag. - Allen Wu

1 Answers

0
votes

We faced a similar kind of problem at the company I work for.

We use Auth0, but the built-in roles/permissions capability didn't quite meet our complex fine-grained authZ requirements - basically we needed to be able to deal with users that could be in one or more groups, but with different permissions in each group. We could have solved this by storing a complex permissions structure in app_metadata and then appending this to the user access token (via an Auth0 rule), but we anticipated this getting unwieldly...

We solved this by building an internal "entitlement" microservice and persisting some access control data on our side (i.e. users, groups, roles, & permissions).

Here's a high level diagram: enter image description here

When a user signs-up (using Auth0 customized Universal Login page that talks to 'IdpFacade' microservice) we publish an event to message broker (Azure Service Bus). This event triggers the creation of the user in the 'access control' data store (as well as the initial group, role, permission associations). Note: we can also update this data store via API (e.g. if we want to dynamically assign additional roles/permissions to a user).

When a user makes a request to an entitlements-enabled microservice, we defer fine-grained authZ decisioning to the entitlements service. The entitlement service first queries the users roles/permissions from the data store (based on the JWT subject, which is essentially the user identifier from Auth0/IDP). We then pass this information along with the HTTP method, URI / resource path, and (optional) additional metadata (in the form of key-value pairs) through to Open Policy Agent (a.k.a. OPA) using the Overload input pattern. OPA defines all the authZ policies in a DSL called Rego and does the decisioning.

The response from the 'entitlement' service is basically a single field that indicates whether access should be allowed or denied. The entitlements-enabled microservice would return a HTTP 403 if allow is false. FYI. we built a bit of boilerplate in .NET Core to simplify the call to entitlements. Here's an example: enter image description here

[HttpPost]
[CheckEntitlement()]
public async Task<IActionResult> PostAsync(

Note: in the attribute we can specify a policy name, as well as (optional) metadata.

Pros of this approach:

  • Simplified IDP implementation
  • Simplified authZ logic in microservices e.g. they don't need to know about complex authZ hierarchies/requirements, just call entitlements.
  • Decoupled architecture - ability to update authZ policies without touching microservices.
  • Real-time authZ i.e. can update roles/permissions and changes would be immediate, as opposed to permissions in JWT, which are valid until the JWT expires.

I should probably do a detailed blog about the approach, as I think it's a pretty nice approach (albeit a bit of work to set up initially).