RPS — A Better Way to Authorize

wa wang
5 min readJan 21, 2020

In the previous article I presented a brief overview of RPS. Here I aim to provide a deep dive of the system. RPS Authorization provides a generalized authorization solution that’s suitable for early stage startups as well as large corporations. RPS supports direct interactions between tenants in a multi-tenant system. The system solves the shortcomings of role based authorization (RBAC) without inheriting the complications of rule or attribute based access control (ABAC).

Key Concepts

RPS has three main concepts: Resource Permission or RP, Roles, and Scopes.

  • RP: an action that’s being performed on a resource such read [permission]:book [resource], add:user, emailMyAccountant:taxReturn. Each instance of a resource should have a unique identifier (ResourceId).
  • Roles: a principal (subject)’s roles such as admin, customer, sales manager. RPs are assigned to roles. All roles are modified by scope.
  • Scope: an area that bounds a role’s presence and power. Scope is modeled after unix file ownership: User, Group, Other vs User, Group, and Global from RPS; however, unlike unix where the scope and its permissions are attached to individual files, scope in RPS is applied to both roles and RPs. This eliminates the need for individual resource objects to be concerned with permissions period. Similar to Resource, every Scope except for global should have a list of unique ids (ScopeId*) — ScopeId in this document refers to the scope name as well the identifier. Scope modifies roles in two ways:
  1. it controls which roles are present for a given ScopeId. A principal may have group_admin roles in groups A and B, but the principal may not access group C. A,B and C in this example are the scopeIds identifying the Group scope.
  2. it defines the area of each RP when assigned to a role. A global admin role may possess all RPs at the global scope — meaning it can perform all actions on all resources in a system, while a group_admin role may only have RPs at the group scope — meaning it can only perform RPs for a specific group.

Scopes can also be arbitrarily defined; default scopes are global, group (Group), user (User). Each resourceId may be associated with one or more ScopeIds. For example a truck may belong to a user and a company. At the user scope the truck is associated with the specified userId; at the company (group) scope the truck is associated with the specified companyId.

Scopes support inheritance and polymorphism. A scope may be used to describe any number types of groups. All scopeIds within the same scope must be unique. Multiple scopes may refer to the same physical set of scopeIds. We may define an ownership scope as well as a non-ownership scope addressing the same groups (ScopeIds). We would then assign ownership permissions at the ownership scope and non-ownership permissions at the non-ownership scope for all applicable roles.

Different ScopeIds may interact with the same resource object at the same scope or different scopes. If two ScopeIds were to share ownership of a resource object, they would both interact with the object at the ownership scope; however, if only one were to possess ownership, the latter would interact with the object at the applicable scope. Scope makes it very easy for one user, group, organization should be able to oversee or observe another user, group, or organization. An overseeing scope provides a new set of RPs for the applicable roles.

RPS components
Relationship between Roles, RPs, and Scopes
Request Authorization in RPS

Demythification

At this point I’ve often received feedback from readers that the concept of RPS is still foreign to them. Let me use a real world example to illustrate. Michael Jordan (MJ), for those of you don’t know, is the greatests basketball player of all time. He played the majority of his career with the Chicago Bulls in the NBA. Suppose we were to receive a request inquiring whether MJ can score for the Chicago Bulls on June 14th, 1998 in Game 6 of the NBA Championship between the Bulls and Utah Jazz, how do we answer the inquiry? Let’s break it down!

The subject in the request is MJ. The resource is NBA Game. The resource object is Game 6 of the 1998 NBA championship. The permission (action) is score. The game’s scope is NBA. The Bulls and the Jazz are the ScopeIds within the NBA Scope. MJ’s Role on the Bulls is Shooting Guard. The primary Permission of a Shooting Guard is score. Therefore the MJ can definitely score as a shooting guard for the Bulls in Game 6 of the 1998 NBA Championship. In fact MJ would score 45 points as well as the game winning shot of the Championship. Now one can try the same inquiry with some other all-time-great athletes like Lionel Messi, Roger Federer, or even Lebron James; all of them would return NO because they were not members of the Bulls on June 14th, 1998; however, for Scottie Pippen and Steve Kerr it would return YES.

Design Principles

  • Separation of authorization concerns from business logic. Authorization alone should be able to determine whether someone is able to perform an action on a resource.
  • Favor RP over roles for access control. RP are much more fine grained and can be attached to any number of roles.
  • Minimize storage as well as decision time.
  • Unopinionated about a principal’s data structure.
  • Unopinionated about the organizational structure. Scopes can accommodate any org structure.

Requirements

  • each resourceId is associated with the necessary ScopeIds.
  • Every role exists within ScopeIds. All roles are associated with the global scope.
  • Role -0< (¹0 — many) Scopes -0< RPs
  • Global scope has no scopeId

Generalized Solution

Given an RP and a principal we want to check every role in the principal’s possession for a matching RP at the global scope. If no match is found and a resourceId is provided, then for each scopeId associated with the resourceId, we want to find a role belonging to the same scopeId with the matching RP and scope. Once we find a match we would authorize; otherwise, deny.

Functional Representation: Given RP and ResourceId (optional)

For each scope  ScopeIdFunc(scope, resourceId) = ScopeIds  RolesFunc(principal, ScopeIds) = Roles  RPfunction(Roles, Scope) = RPs  RP exist in RPs ?

In the next article I shall provide a technical implementation of RPS. You may skip ahead to an implement of RPS in Typescript on Github and NPM. As always feel free to post any questions or concerns.

--

--

wa wang

Wa is a former software engineer with primary focus in physics.