Skip to content

Permission Management

This document provides a guide on managing graph-based access permissions on the cloud using a structure of architypes. Each user has their own root graph, and access between users' architypes is restricted by default.

Anchors vs Architypes

  • Architypes are Jaclang class representation
  • Anchors are database class representation
type Anchors Architypes
Node NodeAnchor NodeArchitype
Edge EdgeAnchor EdgeArchitype
Walker WalkerAnchor WalkerArchitype
Object ObjectAnchor ObjectArchitype
Root NodeAnchor Root(NodeArchitype)
GenericEdge EdgeAnchor GenericEdge(EdgeArchitype)
node Human {
    has gender: str;
}

# DB NodeAnchor Representation
{
    _id: ObjectId('6735b60656e82d6799dc9772'),
    name: 'Human',
    root: ObjectId('6735b5e456e82d6799dc976e'),
    access: { all: 'NO_ACCESS', roots: { anchors: {} } },
    edges: [ 'e::6735b60656e82d6799dc9775', 'e:Friend:6735b60656e82d6799dc9776' ],

    # the actual NodeArchitype and it's context or `has attributes`
    architype: {
        gender: "boy"
    },
}

edge Friend {
    has best: bool;
}

# DB EdgeAnchor Representation
{
    _id: ObjectId('6735b60656e82d6799dc9776'),
    name: 'Friend',
    root: ObjectId('6735b5e456e82d6799dc976e'),
    access: { all: 'NO_ACCESS', roots: { anchors: {} } },
    source: 'n:B:6735b60656e82d6799dc9771',
    target: 'n:C:6735b60656e82d6799dc9772',
    is_undirected: false

    # the actual EdgeArchitype and it's context or `has attributes`
    architype: {
        best: true
    },
  }

Access Levels

  • NO_ACCESS: No other user can access current architype.
  • READ: Other user have Read-Only access to current architype.
  • CONNECT: Other user's node can connect to current node.
  • WRITE: Other user can do anything to current architype.

Default User Graph Structure

In this setup, each user's architype (node, edge, walker) is isolated with default permissions as follows:

{
    "all": "NO_ACCESS",
    "roots": {
        "anchors": {}
    }
}

  • all: Non specific access (Given to all users)
  • roots: Root specific access (specific to user)
    "roots": {
        "anchors": {
            "{{root_jid}}": "NO_ACCESS"
        }
    }
    

Example Structure in DB perspective

Consider the following structure:

User1 -> Root1 -> Node1
User2 -> Root2 -> Node2

By default, User2 cannot access Node1 owns by Root1. To allow User2 access to node1, we need to explicitly add a permission mapping in Root2.

Example of Granting Access

To grant READ access to User2 for Node1 owns by Root1, we modify Node1’s access permissions:

# Node1 is the architype
# Node1.__jac__ is the anchor

Node1.__jac__.access = {
    "all": "READ",
    "roots": {
        "anchors": {}
    }
}
If User1 wants to give only READ access to User2, we set permissions as follows:
# Node1 is the architype
# Node1.__jac__ is the anchor

Node1.__jac__.access = {
    "all": "NO_ACCESS",
    "roots": {
        "anchors": {
            "n::123445673 User2's Root JID": "READ"
        }
    }
}

Permission Management Walkers

Consider this scenario:

User1 -> Root1 -> Node:boy1
User2 -> Root2 -> Node:boy2
User3 -> Root3 -> Node:boy3

Granting Access

To grant boy1 in User1’s graph access to User2, we can use a walker.

Granting Access in jac-lang

# Run the walker in user1
walker set_access {
    has access: str;            # "READ", "WRITE", "CONNECT"
    has root_uuid: str;

    can give_access with boy entry {
        # here = boy1
        Jac.allow_root(here, UUID(self.root_ref_jid), self.access);
    }
}

Granting Access in jac-cloud

# Run the walker in user1
walker set_access {
    has access: str;            # "READ", "WRITE", "CONNECT"
    has root_ref_jid: str;

    can give_access with boy entry {
        # here = boy1
        Jac.allow_root(here, NodeAnchor.ref(self.root_ref_jid), self.access);
    }
}
This is equivalent to setting boy1’s access in database:
boy1.access = {
    "all": "NO_ACCESS",
    "roots": {
        "anchors": {
            "n::123445673 User2's Root JID": "READ"
        }
    }
}

Removing Access

To remove access, use the walker below.

Removing Access in jac-lang

# Run the walker in user1
walker remove_access {
    has root_uuid: str;

    can remove_access with boy entry {
        # here = boy1
        Jac.disallow_root(here, UUID(self.root_uuid));
    }
}

Removing Access in jac-cloud

# Run the walker in user1
walker remove_access {
    has root_ref_jid: str;

    can remove_access with boy entry {
        # here = boy1
        Jac.disallow_root(here, NodeAnchor.ref(self.root_ref_jid));
    }
}
This is equivalent to resetting boy1’s access:
boy1.access = {
    "all": "NO_ACCESS",
    "roots": {
        "anchors": {}
    }
}

Global Access Control

To grant read access to all, use the following syntax:

Jac.unrestrict(here, "READ")
Equivalent structure:
{
    "all": "READ",
    "roots": {
        "anchors": {}
    }
}

To remove access to all, use the following syntax:

Jac.restrict(here)
Equivalent structure:
{
    "all": "NO_ACCESS",
    "roots": {
        "anchors": {}
    }
}

Manual Access Management

In some cases, you may need to manually verify, filter, or update access permissions on nodes. The following Python examples demonstrate how to handle these tasks.

Checking Access Manually

To manually check access levels on a collection of nodes, you can use the following code. This script filters nodes by type and checks for READ, WRITE, and CONNECT access permissions.

for nodeanchor in NodeArchitype.Collection.find(
    {
        "type": "<type of the node>",
        "context.public": true
    }
):
    # Check read access
    if not Jac.check_read_access(nodeanchor):
        continue

    # Check write access
    if not Jac.check_write_access(nodeanchor):
        continue

    # Check connect access
    if not Jac.check_connect_access(nodeanchor):
        continue

Filtering Nodes by Type

To retrieve a specific node based on its type, use the following code snippet. This will find a node of the specified type that is also public in context.

node = NodeArchitype.Collection.find_one(
    {
        "type": "<type of the node>",
        "context.public": true
    }
)

Updating Access Permissions Manually

To manually update access permissions for multiple nodes, use the following code. This example sets the access.all permission to CONNECT for all nodes of a specific type that are publicly accessible.

NodeArchitype.Collection.update_many(
    {
        "type": "<type of the node>",
        "context.public": true
    },
    {
        "$set": {
            "access.all": "CONNECT"
        }
    }
)