Skip to main content
The permissions service implements a comprehensive RBAC (Role-Based Access Control) system with support for temporary privilege elevation using a master code.

Overview

Location: services/permissions.py This module provides:
  • Role-based permission checking across all resources
  • Temporary privilege elevation for assistants
  • Master code verification for sensitive operations
  • Centralized RBAC policy matrix
  • Permission enforcement helpers

Core Concepts

Roles

The system supports three user roles:
  • admin: Full access to all features and settings
  • assistant: Limited access, can elevate privileges with master code
  • artist: Access to own appointments, portfolio, and client interactions

Policy Values

allow

Full permission granted for this role

own

Permission granted only for owned resources (artist role)

locked

Requires master code elevation (assistant only)

deny

Permission explicitly denied

Resources

Protected resources include:
  • agenda - Appointment scheduling
  • clients - Client data management
  • staff - User and staff management
  • portfolio - Artist portfolio items
  • reports - Financial reports and transactions
  • inventory - Stock and inventory management
  • security - System settings and backups

RBAC Matrix

The complete permission matrix is defined in the RBAC dictionary at services/permissions.py:38-87.
RBAC = {
    # Agenda permissions
    ("agenda", "view"):     {"admin": "allow", "assistant": "allow", "artist": "allow"},
    ("agenda", "create"):   {"admin": "allow", "assistant": "allow", "artist": "own"},
    ("agenda", "edit"):     {"admin": "allow", "assistant": "allow", "artist": "own"},
    
    # Client permissions
    ("clients", "view"):    {"admin": "allow", "assistant": "allow", "artist": "allow"},
    ("clients", "edit"):    {"admin": "allow", "assistant": "locked", "artist": "deny"},
    ("clients", "delete"):  {"admin": "allow", "assistant": "locked", "artist": "deny"},
    
    # Security permissions
    ("security", "settings"):    {"admin": "allow", "assistant": "deny", "artist": "deny"},
    ("security", "backup"):      {"admin": "allow", "assistant": "deny", "artist": "deny"},
}

Functions

can

Checks if a role has permission to perform an action on a resource.
role
string
required
User role: “admin”, “assistant”, or “artist”
resource
string
required
Resource name (e.g., “agenda”, “clients”, “inventory”)
action
string
required
Action to perform (e.g., “view”, “create”, “edit”, “delete”)
owner_id
int
Resource owner’s artist_id (for “own” policy checks)
user_artist_id
int
Current user’s artist_id (for “own” policy checks)
user_id
int
Current user’s ID (for elevation status checks)
Returns: bool - True if permission is granted, False otherwise
from services.permissions import can

# Check if assistant can view clients
allowed = can("assistant", "clients", "view")
print(allowed)  # True

# Check if artist can delete clients
allowed = can("artist", "clients", "delete")
print(allowed)  # False

assistant_needs_code

Determines if a specific action requires master code elevation for assistants.
resource
string
required
Resource name
action
string
required
Action to check
Returns: bool - True if the action has “locked” policy for assistants
from services.permissions import assistant_needs_code

# Check if editing clients requires elevation
needs_code = assistant_needs_code("clients", "edit")
print(needs_code)  # True

# Check if viewing clients requires elevation
needs_code = assistant_needs_code("clients", "view")
print(needs_code)  # False

is_elevated_now

Checks if a user currently has elevated privileges.
user_id
int
required
User ID to check
Returns: bool - True if the user has active elevation
from services.permissions import is_elevated_now, elevate_for
import time

user_id = 10

print(is_elevated_now(user_id))  # False

elevate_for(user_id, minutes=1)
print(is_elevated_now(user_id))  # True

time.sleep(61)  # Wait for elevation to expire
print(is_elevated_now(user_id))  # False

elevate_for

Grants temporary elevated privileges to a user.
user_id
int
required
User ID to elevate
minutes
int
default:5
Duration of elevation in minutes
Returns: None
from services.permissions import elevate_for, is_elevated_now

user_id = 10

# Elevate for default 5 minutes
elevate_for(user_id)
print(is_elevated_now(user_id))  # True

# Elevate for custom duration
elevate_for(user_id, minutes=15)
print(is_elevated_now(user_id))  # True
Elevation is stored in-memory only. Server restarts will clear all active elevations.

verify_master_code

Verifies a master code against the stored hash in the database.
plain
string
required
Plain text master code to verify
db
Session
required
SQLAlchemy database session
Returns: bool - True if the code matches, False otherwise
from services.permissions import verify_master_code, elevate_for
from data.db.session import SessionLocal

user_id = 10
user_input = "master_code_123"

with SessionLocal() as db:
    if verify_master_code(user_input, db):
        elevate_for(user_id, minutes=5)
        print("Elevation granted")
    else:
        print("Invalid master code")
The master code hash is stored in the settings table with key "MASTER_CODE_HASH". Use the same bcrypt functions from the auth service to set it.

enforce

Raises a PermissionError if the current user lacks permission. Used for defense-in-depth within service functions.
resource
string
required
Resource being accessed
action
string
required
Action being performed
owner_id
int
Resource owner’s ID for ownership checks
db
Session
Database session (for future use)
Returns: None - Raises PermissionError if not allowed
from services.permissions import enforce, PermissionError

def delete_client(client_id: int):
    """Service function to delete a client."""
    try:
        # Check permissions before proceeding
        enforce(resource="clients", action="delete")
        
        # Proceed with deletion
        # ...
        
    except PermissionError as e:
        print(f"Access denied: {e}")
        raise
Use enforce() at the beginning of service functions that modify data to provide defense-in-depth security.

clear_elevation

Removes elevation status for a specific user.
user_id
int
required
User ID to clear elevation for
Returns: None
from services.permissions import elevate_for, clear_elevation, is_elevated_now

user_id = 10

elevate_for(user_id)
print(is_elevated_now(user_id))  # True

clear_elevation(user_id)
print(is_elevated_now(user_id))  # False
Call this function when a user logs out to ensure their elevation doesn’t persist.

Typical Workflow

1

User attempts restricted action

An assistant tries to edit client information, which is a “locked” action.
2

System checks permission

Call can() to verify if the action is allowed. Returns False for locked actions without elevation.
3

Prompt for master code

The UI prompts the assistant to enter the master code.
4

Verify and elevate

Call verify_master_code() with the input. If valid, call elevate_for() to grant temporary privileges.
5

Perform action

The assistant can now perform locked actions for the duration of the elevation period.
6

Elevation expires

After the specified time, is_elevated_now() returns False and locked actions are denied again.

Permission Examples

  • View: All roles can view the agenda
  • Create/Edit: Admin and assistant can manage any appointment; artists can only manage their own
  • Export: Admin and assistant can export; artists can export only their own data
  • View/Create: All roles allowed
  • Edit/Delete: Admin always allowed; assistant needs master code; artist denied
  • Export: Admin always allowed; assistant needs master code; artist denied
  • View: All roles can view staff list
  • Manage users: Admin only
  • Portfolio: All can view; artists can edit only their own
  • All actions: Admin only (settings, audit logs, backups, code rotation)

Authentication Service

User authentication and password management

User Roles

Detailed role descriptions and capabilities

Build docs developers (and LLMs) love