# Access Control Model: Deep Dive

This page provides a technical deep dive into the access control model of Curiosity Workspace. It is intended for developers building connectors, custom endpoints, or understanding the security implications of the graph model.

# Overview: Relationship-Based Access Control (ReBAC)

Curiosity Workspace employs a Relationship-Based Access Control (ReBAC) model. Unlike traditional Role-Based Access Control (RBAC) where permissions are assigned to roles and roles to users, ReBAC determines access based on the relationships between subjects (users) and objects (resources) in the graph.

As discussed in this Auth0 blog post on ReBAC, ReBAC is powerful because it allows for fine-grained, dynamic permissions that scale with your data model. Access is not a static list; it is a question answered by traversing the graph: "Is there a path from User U to Resource R via ownership or membership edges?"

# The Graph Model

The access control model relies on specific node types and edge types within the graph.

# Nodes

  • _User: Represents a user in the system.
  • _AccessGroup: Represents a group of users (often displayed as a Team in the UI).
  • Resources: Any node (File, Folder, entity, etc.) can be a resource protected by this model.

# Edges

  • Ownership: Defines who owns a resource.

    • _OwnedBy: Points from Resource to Owner (User or AccessGroup).
    • _Owns: Points from Owner to Resource.
    • Note: These are maintained as a pair to allow bidirectional traversal.
  • Membership: Defines who belongs to a group.

    • _MemberOf: Points from User to AccessGroup.
    • _HasMember: Points from AccessGroup to User.

# Propagation Logic

Access is granted if a valid path exists from the user to the resource.

  1. Direct Ownership: User -[_Owns]-> Resource
  2. Group Membership: User -[_MemberOf]-> AccessGroup -[_Owns]-> Resource
  3. Nested Groups: User -[_MemberOf]-> AccessGroup A -[_MemberOf]-> AccessGroup B -[_Owns]-> Resource (if supported by specific implementations, though direct membership is the primary pattern).

In essence, if you are a member of a team, you inherit the access rights (ownerships) of that team.

# Special Access Groups

The system uses specific UIDs for special access scenarios.

# Public Access Group

  • UID: PUBL1CaccesSgr8up11111
  • Behavior: Any resource that has an _OwnedBy edge pointing to this node is considered Public. It is visible to all authenticated users (and potentially unauthenticated ones depending on deployment configuration).
  • Enforcement: The search engine and query engine treat this group as a "wildcard" that everyone is implicitly a member of.

# Private Access Group

  • UID: PRivAtEAcceSSGroUP1111
  • Behavior: This is a system-managed group often used to explicitly mark content as "Private" or "Restricted" in internal logic, effectively ensuring it is not connected to the Public group.
  • Usage: You typically interact with this via higher-level helper methods (like MarkFileAsPrivate) rather than manipulating edges to this node directly.

# Implementation: Data Connector

When building Data Connectors using Curiosity.Library, you interact with this model using helper methods on the Graph object.

# Creating Teams and Users

// Create a Team (_AccessGroup)
var teamNode = await graph.CreateTeamAsync("Engineering Team", "All engineering staff");

// Create a User (_User)
var userNode = await graph.CreateUserAsync("jane.doe", "jane@example.com", "Jane", "Doe");

// Add User to Team
graph.AddUserToTeam(userNode, teamNode);
// This creates _MemberOf / _HasMember edges

# Restricting Access

To restrict access to a specific team or user, you establish ownership edges.

var secretDoc = Node.FromKey("Document", "secret-plans.pdf");

// Restrict to a Team
// Adds: secretDoc -[_OwnedBy]-> teamNode AND teamNode -[_Owns]-> secretDoc
graph.RestrictAccessToTeam(secretDoc, teamNode);

// Restrict to a specific User
// Adds: secretDoc -[_OwnedBy]-> userNode AND userNode -[_Owns]-> secretDoc
graph.RestrictAccessToUser(secretDoc, userNode);

# Making Content Private

If a file was previously public (owned by PUBL1CaccesSgr8up11111), you can revoke that access.

var fileNode = Node.FromUID("some-file-uid", "_FileEntry");

// Removes the _OwnedBy edge to the Public Access Group
graph.MarkFileAsPrivate(fileNode);

# Enforcement

Access control is enforced at multiple layers of the stack.

# 1. APIs

When fetching a node by ID or traversing the graph via the API, the system checks if the requesting user has a valid path to the target node. If no path exists (and the node is not public), the API returns a 403 Forbidden or 404 Not Found.

# 2. Query Engine

Graph queries (GQL) are executed within the context of the user's permissions. The engine implicitly filters the graph traversal. If a user tries to match a pattern involving nodes they cannot see, those nodes are excluded from the result set.

# 3. Search Engine

The search index stores Access Control Lists (ACLs) alongside document content. These ACLs are derived from the graph relationships (users and groups that have access).

  • Indexing Time: When a document is ingested, its ownerships are resolved and indexed.
  • Query Time: When a user searches, their query is augmented with a filter: (access_groups:Public OR access_groups:UserID OR access_groups:{User'sTeamIDs}).
  • Result: Users never see search results for content they don't have access to.

# Key Points & Best Practices

  • Consistency: Always maintain bidirectional edges (_Owns / _OwnedBy) if manipulating the graph manually. Curiosity.Library methods handle this for you.
  • Orphaned Resources: A resource with no _OwnedBy edges might be effectively invisible to everyone except admins, or might fall back to default visibility rules depending on the system configuration. Always assign an owner (User, Team, or Public).
  • Group Cycles: While the graph engine can handle cycles (Team A is member of Team B is member of Team A), avoid them to keep your permission model understandable.
  • Least Privilege: Start by restricting access to the specific owner (User) or a small Team. Only grant Public access when explicitly intended.