Permissions API Reference¶
Complete API documentation for ParquetFrame's Zanzibar-style permissions system (Phase 2).
Core Classes¶
RelationTuple¶
class RelationTuple:
namespace: str
object_id: str
relation: str
subject_namespace: str
subject_id: str
Represents a relation tuple in the Zanzibar permission model.
Fields:
namespace(str): Type of resource (e.g., "board", "task", "document")object_id(str): Specific resource identifierrelation(str): Type of access (e.g., "owner", "editor", "viewer")subject_namespace(str): Type of actor (e.g., "user", "group")subject_id(str): Specific actor identifier
Example:
```python path=null start=null from parquetframe.permissions import RelationTuple
User alice has viewer access to document doc1¶
tuple = RelationTuple( namespace="document", object_id="doc1", relation="viewer", subject_namespace="user", subject_id="alice" )
### TupleStore
```python
class TupleStore:
def __init__(self, storage_path: str = None)
def add_tuple(self, tuple: RelationTuple) -> None
def remove_tuple(self, tuple: RelationTuple) -> None
def get_tuples(self, **filters) -> list[RelationTuple]
def save(self, path: str) -> None
@classmethod
def load(cls, path: str) -> "TupleStore"
def stats(self) -> dict
Storage and management for relation tuples.
Example:
```python path=null start=null from parquetframe.permissions import TupleStore, RelationTuple
Create store¶
store = TupleStore()
Add permissions¶
store.add_tuple(RelationTuple("doc", "doc1", "owner", "user", "alice")) store.add_tuple(RelationTuple("doc", "doc1", "viewer", "user", "bob"))
Save to disk¶
store.save("permissions.parquet")
Load from disk¶
store = TupleStore.load("permissions.parquet")
Get statistics¶
stats = store.stats() print(f"Total tuples: {stats['total_tuples']}")
## Zanzibar APIs
### check()
```python
def check(
store: TupleStore,
subject_namespace: str,
subject_id: str,
relation: str,
object_namespace: str,
object_id: str,
allow_indirect: bool = True
) -> bool
Checks if a subject has a specific relation to an object.
Parameters:
store(TupleStore): Permission store to querysubject_namespace(str): Type of subject (e.g., "user")subject_id(str): Subject identifierrelation(str): Relation to check (e.g., "viewer")object_namespace(str): Type of object (e.g., "document")object_id(str): Object identifierallow_indirect(bool): Whether to check indirect permissions via graph traversal
Returns:
True if subject has the relation to object, False otherwise.
Example:
```python path=/Users/temp/Documents/Projects/parquetframe/examples/integration/todo_kanban/permissions.py start=110 def check_permission( self, user_id: str, resource_type: str, resource_id: str, relation: str, allow_indirect: bool = True, ) -> bool: """ Check if a user has a specific permission.
Uses Zanzibar check() API for both direct and indirect permissions.
"""
return check(
store=self.store,
subject_namespace="user",
subject_id=user_id,
relation=relation,
object_namespace=resource_type,
object_id=resource_id,
allow_indirect=allow_indirect,
)
```
expand()¶
python
def expand(
store: TupleStore,
subject_namespace: str,
subject_id: str,
relation: str,
object_namespace: str = None,
allow_indirect: bool = True
) -> list[tuple[str, str]]
Finds all objects a subject has a specific relation to.
Parameters:
store(TupleStore): Permission store to querysubject_namespace(str): Type of subjectsubject_id(str): Subject identifierrelation(str): Relation to expandobject_namespace(str, optional): Filter by object typeallow_indirect(bool): Whether to check indirect permissions
Returns:
List of (object_namespace, object_id) tuples.
Example:
```python path=null start=null from parquetframe.permissions import expand
Find all documents alice can view¶
viewable_docs = expand( store=store, subject_namespace="user", subject_id="alice", relation="viewer", object_namespace="document" )
for namespace, doc_id in viewable_docs: print(f"Alice can view {namespace}:{doc_id}")
### list_objects()
```python
def list_objects(
store: TupleStore,
relation: str,
object_namespace: str
) -> list[tuple[str, str]]
Finds all objects with a specific relation in a namespace.
Parameters:
store(TupleStore): Permission store to queryrelation(str): Relation to filter byobject_namespace(str): Type of objects to find
Returns:
List of (object_namespace, object_id) tuples.
Example:
```python path=null start=null from parquetframe.permissions import list_objects
Find all documents with any editors¶
documents_with_editors = list_objects( store=store, relation="editor", object_namespace="document" )
print(f"Found {len(documents_with_editors)} documents with editors")
### list_subjects()
```python
def list_subjects(
store: TupleStore,
relation: str,
object_namespace: str,
object_id: str,
subject_namespace: str
) -> list[tuple[str, str]]
Finds all subjects with a specific relation to an object.
Parameters:
store(TupleStore): Permission store to queryrelation(str): Relation to checkobject_namespace(str): Type of objectobject_id(str): Object identifiersubject_namespace(str): Type of subjects to find
Returns:
List of (subject_namespace, subject_id) tuples.
Example:
```python path=null start=null from parquetframe.permissions import list_subjects
Find all users who can edit a document¶
editors = list_subjects( store=store, relation="editor", object_namespace="document", object_id="doc1", subject_namespace="user" )
for namespace, user_id in editors: print(f"User {user_id} can edit the document")
## PermissionManager
Higher-level permission management for applications.
### __init__()
```python path=/Users/temp/Documents/Projects/parquetframe/examples/integration/todo_kanban/permissions.py start=46
def __init__(self, storage_path: str = "./kanban_data/permissions"):
"""
Initialize the permission manager.
Args:
storage_path: Path to store permission tuples
"""
self.store = TupleStore()
self.storage_path = storage_path
grant_permission()¶
Grants a permission to a user.
Example:
```python path=null start=null perm_mgr = PermissionManager()
Grant editor access¶
perm_mgr.grant_permission( user_id="alice", resource_type="document", resource_id="doc1", relation="editor" )
### revoke_permission()
```python
def revoke_permission(
user_id: str,
resource_type: str,
resource_id: str,
relation: str
) -> None
Revokes a permission from a user.
Example:
```python path=null start=null
Revoke editor access¶
perm_mgr.revoke_permission( user_id="alice", resource_type="document", resource_id="doc1", relation="editor" )
### check_permission()
```python
def check_permission(
user_id: str,
resource_type: str,
resource_id: str,
relation: str,
allow_indirect: bool = True
) -> bool
Checks if a user has a permission.
Example:
```python path=null start=null can_edit = perm_mgr.check_permission( user_id="alice", resource_type="document", resource_id="doc1", relation="editor" )
if can_edit: # Allow editing pass
## Permission Patterns
### Permission Inheritance
Grant access that cascades down a hierarchy.
**Example from Todo/Kanban:**
```python path=/Users/temp/Documents/Projects/parquetframe/examples/integration/todo_kanban/permissions.py start=254
def grant_board_access(
self,
user_id: str,
board_id: str,
role: str,
) -> None:
"""
Grant board-level access to a user.
This automatically propagates permissions to all lists and tasks in the board.
"""
if role not in ["owner", "editor", "viewer"]:
raise ValueError(f"Invalid role: {role}. Must be owner, editor, or viewer")
self.grant_permission(user_id, "board", board_id, role)
Role Hierarchy¶
Implement role-based access with inheritance (owner > editor > viewer).
Example:
```python path=null start=null def check_board_access(self, user_id: str, board_id: str, required_role: str = "viewer") -> bool: # Check role hierarchy if required_role == "viewer": return ( self.check_permission(user_id, "board", board_id, "owner") or self.check_permission(user_id, "board", board_id, "editor") or self.check_permission(user_id, "board", board_id, "viewer") ) elif required_role == "editor": return ( self.check_permission(user_id, "board", board_id, "owner") or self.check_permission(user_id, "board", board_id, "editor") ) elif required_role == "owner": return self.check_permission(user_id, "board", board_id, "owner")
### Group Permissions
Grant permissions through group membership.
**Example:**
```python path=null start=null
# Add user to group
store.add_tuple(RelationTuple("group", "engineering", "member", "user", "alice"))
# Grant group access to resource
store.add_tuple(RelationTuple("document", "doc1", "viewer", "group", "engineering"))
# Check if alice has access (through group membership)
can_view = check(store, "user", "alice", "viewer", "document", "doc1")
# Returns True via graph traversal
Best Practices¶
1. Use Permission Manager¶
Prefer the higher-level PermissionManager over direct tuple manipulation.
```python path=null start=null
Good: Using PermissionManager¶
perm_mgr = PermissionManager() perm_mgr.grant_board_access("alice", "board1", "editor")
Avoid: Direct tuple manipulation¶
store.add_tuple(RelationTuple("board", "board1", "editor", "user", "alice"))
### 2. Implement Permission Inheritance
Design hierarchical permissions for better organization.
```python path=null start=null
# Grant board access (inherits to lists and tasks)
perm_mgr.grant_board_access("alice", "board1", "owner")
perm_mgr.inherit_board_permissions("board1", "list1")
perm_mgr.inherit_list_permissions("list1", "task1")
3. Use Role Hierarchies¶
Implement roles with built-in hierarchy (owner > editor > viewer).
```python path=null start=null
Check with minimum required role¶
can_edit = perm_mgr.check_board_access( user_id="alice", board_id="board1", required_role="editor" # Passes if owner or editor )
### 4. Persist Permissions
Save and load permission state for production use.
```python path=null start=null
# Save permissions
perm_mgr.save("permissions.parquet")
# Load permissions
perm_mgr.load("permissions.parquet")
See Also¶
- Permissions Overview - Complete permissions system guide
- Todo/Kanban Walkthrough - Real-world permissions example
- CLI Reference - Command-line permission management