Access Control

This article explains the security model in Flexo MMS, which closely resembles role-based access control.

Named graph partitioning of Access Control elements

A Policy associates an Agent (User or Group) to a set of Roles along with some Scope. Any Agent (being a User or Group) can belong to any number of Groups. A Role permits a set of Permissions. A Permission can imply any number of other Permissions (e.g., the ability to write typically implies the ability read). Similarly, a Scope can imply any number of other Scopes (e.g., a project-level scope also implies the branch-level scope).

Partitioning Access Control elements into these three named graphs is done to limit queries and updates to predetermined boundaries. Typically, admins/managers should have access to Policies, automated directories should have access to Users and Groups, and sysadmins (in rare cases) should have access to Definitions.

Permissions

A Permission simply identifies a specific action that a user can take. In Flexo MMS, some named graphs are intended to allow authorized users to issue SPARQL updates cart blanche (e.g., the Model graph can accept arbitrary SPARQL updates). However, other named graphs in MMS are only meant to be directly editable by the Layer 1 service. These named graphs contain database objects that are either immutable, or must follow some set of rules when mutated (e.g., when adding a User to a Group, make sure the Group exists). Creating, reading, updating, and deleting (CRUD) these database objects are exposed as Linked Data Platform (LDP) resources. As a result, there are two categories of permissions: (1) reading/writing named graphs and (2) LDP actions.

Example of an Access Control Policy in action

Let’s walk through an example of applying a policy for an LDP action on a Branch object. When Flexo MMS receives a request from a client to create a new branch for a given project, it will submit a prepared SPARQL Update to the quadstore with an INSERT..WHERE operation. The WHERE clause of this operation will include a set of basic graph patterns (BGPs) that asserts the given user (or some group it belongs to) is part of a policy which gives it permission to create a new branch (aptly named “CreateBranch”). This technique embeds the policy-checking logic into the SPARQL Update itself, ensuring that the operation only succeeds if some policy allows the specific action being performed. With such an approach, there is no need to worry about revoking or upgrading stale auth tokens in response to policy changes as they are reflected in the database immediately.

The SPARQL code snippet below details the aforementioned BGPs responsible for the policy-checking logic in the “CreateBranch” scenario:

SPARQL BGPs enacting policy-checking logic for the “CreateBranch” action.
# locate the user and its group(s) graph m-graph:AccessControl.Agents { # user exists mu: a mms:User . # and optionally belongs to some group(s) optional { mu: mms:group ?group . ?group a mms:Group . } } # locate some policy that applies to this user/group within the given scope graph m-graph:AccessControl.Policies { # policy exists with some scope and role(s) ?policy a mms:Policy ; mms:scope ?scope ; mms:role ?role . # the scope of the policy must apply at the project-level ?scope mms:implies* mms-object:Scope.Project . { # the policy is about the user ?policy mms:agent mu: . } union { # the policy is about a group that the user belongs to ?policy mms:agent ?group . } } # find a path to the required "CreateBranch" permission graph m-graph:AccessControl.Definitions { # all permissions that the role directly permits ?role a mms:Role ; mms:permits ?directRolePermissions . # the permission itself or its implications contain the "CreateBranch" ?directRolePermissions a mms:Permission ; mms:implies* mms-object:Permission.CreateBranch . }