Make default permissions use only : as separator.

This commit is contained in:
Leo Vasanko 2025-08-30 18:43:49 -06:00
parent 326a7664d3
commit d045e1c520
6 changed files with 17 additions and 21 deletions

View File

@ -66,7 +66,7 @@ async def bootstrap_system(
dict: Contains information about created entities and reset link dict: Contains information about created entities and reset link
""" """
# Create permission first - will fail if already exists # Create permission first - will fail if already exists
perm0 = Permission(id="auth/admin", display_name="Master Admin") perm0 = Permission(id="auth:admin", display_name="Master Admin")
await globals.db.instance.create_permission(perm0) await globals.db.instance.create_permission(perm0)
org = Org(uuid7.create(), org_name or "Organization") org = Org(uuid7.create(), org_name or "Organization")
@ -122,7 +122,7 @@ async def check_admin_credentials() -> bool:
try: try:
# Get permission organizations to find admin users # Get permission organizations to find admin users
permission_orgs = await globals.db.instance.get_permission_organizations( permission_orgs = await globals.db.instance.get_permission_organizations(
"auth/admin" "auth:admin"
) )
if not permission_orgs: if not permission_orgs:
@ -173,7 +173,7 @@ async def bootstrap_if_needed(
""" """
try: try:
# Check if the admin permission exists - if it does, system is already bootstrapped # Check if the admin permission exists - if it does, system is already bootstrapped
await globals.db.instance.get_permission("auth/admin") await globals.db.instance.get_permission("auth:admin")
# Permission exists, system is already bootstrapped # Permission exists, system is already bootstrapped
# Check if admin needs credentials (only for already-bootstrapped systems) # Check if admin needs credentials (only for already-bootstrapped systems)
await check_admin_credentials() await check_admin_credentials()

View File

@ -462,8 +462,7 @@ class DB(DatabaseInterface):
) )
# Automatically create an organization admin permission if not present. # Automatically create an organization admin permission if not present.
# Pattern: auth/org:<org-uuid> auto_perm_id = f"auth:org:{org.uuid}"
auto_perm_id = f"auth/org:{org.uuid}"
# Only create if it does not already exist (in case caller passed it) # Only create if it does not already exist (in case caller passed it)
existing_perm = await session.execute( existing_perm = await session.execute(
select(PermissionModel).where(PermissionModel.id == auto_perm_id) select(PermissionModel).where(PermissionModel.id == auto_perm_id)

View File

@ -10,7 +10,7 @@ This module contains all the HTTP API endpoints for:
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from fastapi import Body, Cookie, Depends, FastAPI, HTTPException, Response from fastapi import Body, Cookie, Depends, FastAPI, HTTPException, Query, Response
from fastapi.security import HTTPBearer from fastapi.security import HTTPBearer
from passkey.util import passphrase from passkey.util import passphrase
@ -38,14 +38,12 @@ def register_api_routes(app: FastAPI):
raise ValueError("Not authenticated") raise ValueError("Not authenticated")
role_perm_ids = set(ctx.role.permissions or []) role_perm_ids = set(ctx.role.permissions or [])
org_uuid_str = str(ctx.org.uuid) org_uuid_str = str(ctx.org.uuid)
is_global_admin = "auth/admin" in role_perm_ids is_global_admin = "auth:admin" in role_perm_ids
is_org_admin = f"auth/org:{org_uuid_str}" in role_perm_ids is_org_admin = f"auth:org:{org_uuid_str}" in role_perm_ids
return ctx, is_global_admin, is_org_admin return ctx, is_global_admin, is_org_admin
@app.post("/auth/validate") @app.post("/auth/validate")
async def validate_token( async def validate_token(perm=Query(None), auth=Cookie(None)):
response: Response, perm: list[str] | None = None, auth=Cookie(None)
):
"""Lightweight token validation endpoint. """Lightweight token validation endpoint.
Query Params: Query Params:
@ -137,9 +135,9 @@ def register_api_routes(app: FastAPI):
"permissions": ctx.org.permissions, "permissions": ctx.org.permissions,
} }
effective_permissions = [p.id for p in (ctx.permissions or [])] effective_permissions = [p.id for p in (ctx.permissions or [])]
is_global_admin = "auth/admin" in role_info["permissions"] is_global_admin = "auth:admin" in role_info["permissions"]
is_org_admin = ( is_org_admin = (
f"auth/org:{org_info['uuid']}" in role_info["permissions"] f"auth:org:{org_info['uuid']}" in role_info["permissions"]
if org_info if org_info
else False else False
) )

View File

@ -12,7 +12,7 @@ from ..globals import db
from ..util.tokens import session_key from ..util.tokens import session_key
async def verify(auth: str | None, perms: list[str] | None): async def verify(auth: str | None, perm: list[str] | str | None):
"""Validate session token and optional list of required permissions. """Validate session token and optional list of required permissions.
Returns the Session object on success. Raises HTTPException on failure. Returns the Session object on success. Raises HTTPException on failure.
@ -22,14 +22,16 @@ async def verify(auth: str | None, perms: list[str] | None):
if not auth: if not auth:
raise HTTPException(status_code=401, detail="Authentication required") raise HTTPException(status_code=401, detail="Authentication required")
session = await get_session(auth) session = await get_session(auth)
if perms: if perm is not None:
if isinstance(perm, str):
perm = [perm]
ctx = await db.instance.get_session_context(session_key(auth)) ctx = await db.instance.get_session_context(session_key(auth))
if not ctx: if not ctx:
raise HTTPException(status_code=401, detail="Session not found") raise HTTPException(status_code=401, detail="Session not found")
available = set(ctx.role.permissions or []) | ( available = set(ctx.role.permissions or []) | (
set(ctx.org.permissions or []) if ctx.org else set() set(ctx.org.permissions or []) if ctx.org else set()
) )
if any(p not in available for p in perms): if any(p not in available for p in perm):
raise HTTPException(status_code=403, detail="Permission required") raise HTTPException(status_code=403, detail="Permission required")
return session return session

View File

@ -4,7 +4,7 @@ import os
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from pathlib import Path from pathlib import Path
from fastapi import Cookie, FastAPI, HTTPException, Request, Response from fastapi import Cookie, FastAPI, HTTPException, Query, Request, Response
from fastapi.responses import FileResponse, JSONResponse from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@ -72,9 +72,7 @@ app.mount("/auth/ws", ws.app)
@app.get("/auth/forward-auth") @app.get("/auth/forward-auth")
async def forward_authentication( async def forward_authentication(request: Request, perm=Query(None), auth=Cookie(None)):
request: Request, perm: list[str] | None = None, auth=Cookie(None)
):
"""A validation endpoint to use with Caddy forward_auth or Nginx auth_request. """A validation endpoint to use with Caddy forward_auth or Nginx auth_request.
Query Params: Query Params:

View File

@ -30,5 +30,4 @@ def set_session_cookie(response: Response, token: str) -> None:
max_age=int(EXPIRES.total_seconds()), max_age=int(EXPIRES.total_seconds()),
httponly=True, httponly=True,
secure=True, secure=True,
path="/auth/",
) )