Defining a Schema

The ReBAC schema is comprised of three main components: namespaces, relation definitions, and relations. Namespaces are top-level entities in your application, like documents, folders, and organizations. Relation definitions defines the relationship between namespaces, like a user being a member of an organization. Relations are the actual relationships between entities, like a specific user being a member of a particular organization.

The general flow of defining a schema involves creating namespaces and relation definitions.

An example schema

Download an example schema here.

Breaking down the schema

Getting into the details of how a schema is constructed, we'll start from a top down approach. The first data type we have is the schema.

Schema

The AuthzSchema holds the full schema (all namespaces) for a project.

type AuthzSchema = {
  name?: string;
  namespaces: AuthzNamespace[];
};

Namespaces

Within the AuthzSchema, we have the AuthzNamespace array, which holds all the namespaces of a project.

type AuthzNamespace = {
  name: string;
  relationDefinitions: AuthzRelationDefinition[];
};
AuthzRelationDefinition

Within each AuthzNamespace, we have the AuthzRelationDefinition array. In this object, have a name to quickly define a direct relation and the optional complexDefinition parameter to define implicit or recursive relations via a tree of AuthzNode objects.

type AuthzRelationDefinition = {
  name: string;
  complexDefinition?: AuthzNode;
};
AuthzNode

Each AuthzNode object has a nType to define the type of node it is, including child, union, intersect, or sub.

  • Union (union): Combines the permissions of all child nodes. If any of the children grant a permission, that permission is present in the resulting set.
  • Intersection (intersect): Only the permissions common to the child nodes are included. A permission must be granted by every child to be present in the resulting set.
  • Subtraction (sub): Starts with permissions granted by the first child node, then removes any permissions that are also granted by subsequent child nodes. It effectively excludes permissions from the resulting set that are granted by the other nodes.

Optionally, we have:

  • The children array is an array of AuthzNode objects, allowing for a more complex tree structure.
  • The expression parameter is completed with an AuthzNodeExpression, which defines the relationship between the node, target, and relation definition it is a part of.
type AuthzNode = {
  nType: AuthzNodeType;
  children?: AuthzNode[];
  expression?: AuthzNodeExpression;
};
 
enum AuthzNodeType {
  child = 'child', // regular child node in relation definition
  // union node of multiple children
  // Example: editor of document is union between
  // 1. Direct editor relation - someone that has editor on document
  // 2. Owner relation - someone who is owner of document
  // 3. Editor of containing folder relation - someone who is editor of the folder that has parent relation to doc
  union = 'union',
  intersect = 'intersect', // intersect between multiple children
  sub = 'sub', // sub between the first child and the rest
}
Practical Example of Node Types

To delve into a practical example of why we would use these node types, let's look at access management for a document/folder system:

  • Node 1 (union): Represents editors of a document
  • Node 2 (union): Represents editors of a parent folder which the document resides in.

If we want to determine who has ultimate edit access to the document, we'd look at the union of these two nodes, meaning the editors of the document itself and editors of the parent folder. However, if we would like to exclude certain users from editing specific documents despite having edit permissions on the parent folder or are direct editors, what would we do?

  • Node 3 (sub): Would be used to subtract specific users or roles that are forbidden from editing this document for some reason, maybe due to a conflict of interest or security protocol.

Using the sub permissions type we can refine the permissions set to exclude certain users or roles. The result of the sub operation will be the final set of users who are authorized to edit the document after applying the exclusion criteria. Below is an example of the ReBAC schema for a document that has parent, restricted, and editor relations, leveraging union and sub:

name: files
namespaces:
  - name: document
    relationDefinitions:
      - name: parent
      - name: restricted
      - name: editor
        complexRelation:
          nType: sub
          children:
            - nType: union
            - children:
              - nType: child
                expression:
                  neType: self
              - nType: child
                expression:
                  neType: relationLeft
                  relationDefinition: parent
                  targetRelationDefinition: editor
            - nType: child
              expression:
                neType: targetSet
                targetRelationDefinition: restricted
                targetRelationDefinitionNamespace: document
AuthzNodeExpression

The AuthzNodeExpression object is used to define the relationship between the AuthzNode and the AuthzRelationDefinition it is a part of. It takes a type, neType, to define the type of relation expression it is. Optionally, we have:

  • relationDefinition and relationDefinitionNamespace to define the relation definition and namespace of the relation definition that the node is a part of.
  • targetRelationDefinition and targetRelationDefinitionNamespace to define the relation definition and namespace of the relation definition that the node is targeting.
type AuthzNodeExpression = {
  neType: AuthzNodeExpressionType;
  relationDefinition?: string;
  relationDefinitionNamespace?: string;
  targetRelationDefinition?: string;
  targetRelationDefinitionNamespace?: string;
};
 
enum AuthzNodeExpressionType {
  self = 'self', // direct relation expression
  targetSet = 'targetSet', // expression via another relation definition target
  relationLeft = 'relationLeft', // expression via parent relation like org within org and membership
  relationRight = 'relationRight', // expression via child relation like folder within folder and owner
}

When thinking about AuthzNodeExpressions, it can be good to think about the Resources, Relation Definitions, and Targets in a table format.

ResourceRelation DefinitionTarget
folder_1owneruser_1
folder_2parentfolder_1

This table read right to left, as in:

  • user_1 is the owner of folder_1
  • folder_1 is the parent of folder_2

So generally, the Target is the Relation Definition of the Resource.

Next

We'll move on to implementing a schema.

Was this helpful?

On this page