Skip to content

Admin Permissions Quick Reference

The Correct Way to Check Admin Access

In Jinja2 Templates

CORRECT - Check for wildcard scope:

{% if user.scopes and "*" in user.scopes %}
    <!-- This user is an admin -->
{% endif %}

WRONG - This is legacy and won't work:

{% if user.attributes and "users" in user.attributes %}
    <!-- Don't use this -->
{% endif %}

In Python Routers

CORRECT - Use the dependency:

from app.auth.permissions import require_admin

@router.get("/admin-only", dependencies=[Depends(require_admin())])
async def admin_endpoint(request: Request):
    # This endpoint is protected - only admins can access
    pass

CORRECT - Use in handler:

from app.auth.auth import SessionData, verifier

@router.get("/admin-only")
async def admin_endpoint(session: SessionData = Depends(verifier)):
    if not session.has_scope("*"):
        raise HTTPException(status_code=403, detail="Admin access required")
    # Admin content here

For Specific Scopes

Check for a specific scope:

from app.auth.permissions import require_scope

@router.post("/templates", dependencies=[Depends(require_scope("templates:write"))])
async def create_template(...):
    pass

Check for multiple scopes (AND):

from app.auth.permissions import require_all_scopes

@router.delete("/resource", dependencies=[Depends(require_all_scopes("templates:write", "templates:delete"))])
async def delete_resource(...):
    pass

Check for any scope (OR):

from app.auth.permissions import require_any_scope

@router.get("/dashboard", dependencies=[Depends(require_any_scope("*", "admin:read"))])
async def dashboard(...):
    pass

Creating Admin Users

Using the CLI:

# Create an admin user (has "*" scope - full access)
slidefactory user create-local admin@example.com --name "Admin User" --preset admin

# View the user's scopes
slidefactory user show admin@example.com

Creating via database (if needed):

from app.auth.users.models import User
from app.util.auth_utils import hash_password

admin_user = User(
    email="admin@example.com",
    name="Admin User",
    password_hash=hash_password("secure_password"),
    active=True,
    scopes=['*'],  # Full admin access
    auth_provider='local'
)
db.add(admin_user)
db.commit()

What is an Admin?

A user is an admin if they have the "*" scope in their user.scopes list.

When a user logs in, their scopes are loaded from the database into the session:

scopes = user.scopes  # List of scope strings like ['*'] or ['templates:read', 'workflows:execute']

The "*" scope is a wildcard that grants access to everything: - All endpoints with require_admin() - All scope checks (the has_scope() method returns True for any scope if "*" is present)

Scope Hierarchy

Scopes follow a hierarchical naming convention:

*                           Full admin access (wildcard)
templates:read              Can view templates
templates:write             Can create/update templates
templates:delete            Can delete templates
workflows:read              Can list workflows
workflows:execute           Can run workflows
presentations:read          Can view presentations
presentations:write         Can create presentations
presentations:delete        Can delete presentations
results:read                Can view results
results:delete              Can delete results
contexts:read               Can view document contexts
contexts:write              Can manage contexts
users:read                  Can view users (admin)
users:write                 Can manage users (admin)

Admin users have ["*"] which bypasses all these checks.

Checking User Scopes

To see what scopes a user has:

slidefactory user show email@example.com

Output will show something like:

  Scopes:
    - *

Or for a limited user:

  Scopes:
    - templates:read
    - workflows:execute
    - presentations:read

Migration Notes

The system migrated from attribute-based access control to scope-based access control.

Legacy (don't use): - user.attributes["users"] - This is deprecated - Admin check via attributes - Don't use this pattern

Current (use this): - user.scopes - The current source of truth - Admin check via "*" in user.scopes - Use require_admin() and require_scope() dependencies

File Locations

Models: - User model: /app/auth/users/models.py - SessionData model: /app/auth/auth.py

Permission Helpers: - Permission dependencies: /app/auth/permissions.py

Navigation: - Main navigation: /templates/includes/base.html.j2 (line 230-239)