Logging cleanup, linter.
This commit is contained in:
@@ -57,7 +57,6 @@ async def flush() -> None:
|
||||
|
||||
async def _background_loop():
|
||||
"""Background task that periodically flushes changes and cleans up."""
|
||||
_logger.info("Background loop starting")
|
||||
# Run cleanup immediately on startup to clear old expired items
|
||||
cleanup()
|
||||
await flush()
|
||||
@@ -77,18 +76,17 @@ async def _background_loop():
|
||||
await flush() # Flush cleanup changes
|
||||
last_cleanup = now
|
||||
except asyncio.CancelledError:
|
||||
_logger.info("Background loop cancelled, final flush")
|
||||
# Final flush before exit
|
||||
await flush()
|
||||
break
|
||||
except Exception:
|
||||
_logger.exception("Error in database background loop")
|
||||
_logger.debug("Error in database background loop", exc_info=True)
|
||||
|
||||
|
||||
async def start_background():
|
||||
"""Start the background flush/cleanup task."""
|
||||
global _background_task
|
||||
|
||||
|
||||
# Check if task exists but is no longer running (e.g., after uvicorn reload)
|
||||
if _background_task is not None:
|
||||
if _background_task.done():
|
||||
@@ -106,10 +104,9 @@ async def start_background():
|
||||
except Exception as e:
|
||||
_logger.debug("Error checking background task loop: %s, restarting", e)
|
||||
_background_task = None
|
||||
|
||||
|
||||
if _background_task is None:
|
||||
_background_task = asyncio.create_task(_background_loop())
|
||||
_logger.info("Database background task started")
|
||||
else:
|
||||
_logger.debug("Background task already running: %s", _background_task)
|
||||
|
||||
|
||||
+3
-4
@@ -84,7 +84,9 @@ def compute_diff(previous: dict, current: dict) -> dict | None:
|
||||
return diff if diff else None
|
||||
|
||||
|
||||
def create_change_record(action: str, diff: dict, user: str | None = None) -> _ChangeRecord:
|
||||
def create_change_record(
|
||||
action: str, diff: dict, user: str | None = None
|
||||
) -> _ChangeRecord:
|
||||
"""Create a change record for persistence."""
|
||||
return _ChangeRecord(
|
||||
ts=datetime.now(timezone.utc),
|
||||
@@ -121,9 +123,6 @@ async def flush_changes(
|
||||
# Append all lines in a single write (binary mode for Windows compatibility)
|
||||
async with aiofiles.open(db_path, "ab") as f:
|
||||
await f.write(b"\n".join(lines) + b"\n")
|
||||
_logger.debug(
|
||||
"Flushed %d change(s) to %s", len(changes_to_write), db_path
|
||||
)
|
||||
return True
|
||||
except OSError:
|
||||
_logger.exception("Failed to flush database changes")
|
||||
|
||||
+24
-44
@@ -7,9 +7,11 @@ Write operations: Functions that validate and commit, or raise ValueError.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import secrets
|
||||
import sys
|
||||
from collections import deque
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime, timezone
|
||||
@@ -113,19 +115,15 @@ class DB:
|
||||
user_display = self._data.users[user_uuid].display_name
|
||||
except (ValueError, KeyError):
|
||||
user_display = self._current_user
|
||||
import json
|
||||
|
||||
diff_json = json.dumps(diff, default=str)
|
||||
if user_display:
|
||||
_logger.info(
|
||||
"DB %s by %s: %s",
|
||||
self._current_action,
|
||||
user_display,
|
||||
json.dumps(diff, default=str),
|
||||
print(
|
||||
f"{self._current_action} by {user_display}: {diff_json}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
_logger.info(
|
||||
"DB %s: %s", self._current_action, json.dumps(diff, default=str)
|
||||
)
|
||||
print(f"{self._current_action}: {diff_json}", file=sys.stderr)
|
||||
|
||||
@contextmanager
|
||||
def transaction(
|
||||
@@ -527,9 +525,7 @@ def get_session_context(
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def create_permission(
|
||||
perm: Permission, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def create_permission(perm: Permission, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Create a new permission."""
|
||||
if perm.uuid in _db._data.permissions:
|
||||
raise ValueError(f"Permission {perm.uuid} already exists")
|
||||
@@ -542,9 +538,7 @@ def create_permission(
|
||||
)
|
||||
|
||||
|
||||
def update_permission(
|
||||
perm: Permission, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def update_permission(perm: Permission, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Update a permission's scope, display_name, and domain."""
|
||||
if perm.uuid not in _db._data.permissions:
|
||||
raise ValueError(f"Permission {perm.uuid} not found")
|
||||
@@ -583,9 +577,7 @@ def rename_permission(
|
||||
_db._data.permissions[key].domain = domain
|
||||
|
||||
|
||||
def delete_permission(
|
||||
uuid: str | UUID, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def delete_permission(uuid: str | UUID, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Delete a permission and remove it from all roles."""
|
||||
if isinstance(uuid, str):
|
||||
uuid = UUID(uuid)
|
||||
@@ -598,9 +590,7 @@ def delete_permission(
|
||||
del _db._data.permissions[uuid]
|
||||
|
||||
|
||||
def create_organization(
|
||||
org: Org, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def create_organization(org: Org, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Create a new organization with an Administration role.
|
||||
|
||||
Automatically creates an 'Administration' role with auth:org:admin permission.
|
||||
@@ -651,9 +641,7 @@ def update_organization_name(
|
||||
_db._data.orgs[uuid].display_name = display_name
|
||||
|
||||
|
||||
def delete_organization(
|
||||
uuid: str | UUID, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def delete_organization(uuid: str | UUID, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Delete organization and all its roles/users."""
|
||||
if isinstance(uuid, str):
|
||||
uuid = UUID(uuid)
|
||||
@@ -742,9 +730,7 @@ def remove_permission_from_organization(
|
||||
_db._data.permissions[permission_uuid].orgs.pop(org_uuid, None)
|
||||
|
||||
|
||||
def create_role(
|
||||
role: Role, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def create_role(role: Role, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Create a new role."""
|
||||
if role.uuid in _db._data.roles:
|
||||
raise ValueError(f"Role {role.uuid} already exists")
|
||||
@@ -809,9 +795,7 @@ def remove_permission_from_role(
|
||||
_db._data.roles[role_uuid].permissions.pop(permission_uuid, None)
|
||||
|
||||
|
||||
def delete_role(
|
||||
uuid: str | UUID, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def delete_role(uuid: str | UUID, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Delete a role."""
|
||||
if isinstance(uuid, str):
|
||||
uuid = UUID(uuid)
|
||||
@@ -824,9 +808,7 @@ def delete_role(
|
||||
del _db._data.roles[uuid]
|
||||
|
||||
|
||||
def create_user(
|
||||
new_user: User, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def create_user(new_user: User, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Create a new user."""
|
||||
if new_user.uuid in _db._data.users:
|
||||
raise ValueError(f"User {new_user.uuid} already exists")
|
||||
@@ -849,7 +831,7 @@ def update_user_display_name(
|
||||
ctx: SessionContext | None = None,
|
||||
) -> None:
|
||||
"""Update user display name.
|
||||
|
||||
|
||||
For self-service (user updating own name), ctx can be None and user is derived from uuid.
|
||||
For admin operations, ctx should be provided.
|
||||
"""
|
||||
@@ -909,9 +891,7 @@ def update_user_role_in_organization(
|
||||
_db._data.users[user_uuid].role = new_role_uuid
|
||||
|
||||
|
||||
def delete_user(
|
||||
uuid: str | UUID, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def delete_user(uuid: str | UUID, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Delete user and their credentials/sessions."""
|
||||
if isinstance(uuid, str):
|
||||
uuid = UUID(uuid)
|
||||
@@ -933,9 +913,7 @@ def delete_user(
|
||||
del _db._data.users[uuid]
|
||||
|
||||
|
||||
def create_credential(
|
||||
cred: Credential, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
def create_credential(cred: Credential, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Create a new credential."""
|
||||
if cred.uuid in _db._data.credentials:
|
||||
raise ValueError(f"Credential {cred.uuid} already exists")
|
||||
@@ -1056,7 +1034,7 @@ def set_session_host(key: str, host: str, *, ctx: SessionContext | None = None)
|
||||
|
||||
def delete_session(key: str, *, ctx: SessionContext | None = None) -> None:
|
||||
"""Delete a session.
|
||||
|
||||
|
||||
For logout (user deleting own session), ctx can be None and user is derived from session.
|
||||
For admin operations, ctx should be provided.
|
||||
"""
|
||||
@@ -1068,9 +1046,11 @@ def delete_session(key: str, *, ctx: SessionContext | None = None) -> None:
|
||||
del _db._data.sessions[key]
|
||||
|
||||
|
||||
def delete_sessions_for_user(user_uuid: str | UUID, *, ctx: SessionContext | None = None) -> None:
|
||||
def delete_sessions_for_user(
|
||||
user_uuid: str | UUID, *, ctx: SessionContext | None = None
|
||||
) -> None:
|
||||
"""Delete all sessions for a user.
|
||||
|
||||
|
||||
For logout-all (user deleting own sessions), ctx can be None and user is derived from user_uuid.
|
||||
For admin operations, ctx should be provided.
|
||||
"""
|
||||
@@ -1093,7 +1073,7 @@ def create_reset_token(
|
||||
ctx: SessionContext | None = None,
|
||||
) -> None:
|
||||
"""Create a reset token from a passphrase.
|
||||
|
||||
|
||||
For self-service (user creating own recovery link), ctx can be None and user is derived from user_uuid.
|
||||
For admin operations, ctx should be provided.
|
||||
"""
|
||||
|
||||
+5
-11
@@ -214,9 +214,7 @@ async def admin_add_org_permission(
|
||||
ctx = await authz.verify(
|
||||
auth, ["auth:admin"], host=request.headers.get("host"), match=permutil.has_all
|
||||
)
|
||||
db.add_permission_to_organization(
|
||||
str(org_uuid), permission_id, ctx=ctx
|
||||
)
|
||||
db.add_permission_to_organization(str(org_uuid), permission_id, ctx=ctx)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@@ -240,9 +238,7 @@ async def admin_remove_org_permission(
|
||||
"This would lock you out of admin access."
|
||||
)
|
||||
|
||||
db.remove_permission_from_organization(
|
||||
str(org_uuid), permission_id, ctx=ctx
|
||||
)
|
||||
db.remove_permission_from_organization(str(org_uuid), permission_id, ctx=ctx)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@@ -273,7 +269,7 @@ async def admin_create_role(
|
||||
perms = payload.get("permissions") or []
|
||||
org = db.get_organization(str(org_uuid))
|
||||
grantable = set(org.permissions or [])
|
||||
|
||||
|
||||
# Normalize permission IDs to UUIDs
|
||||
permission_uuids = []
|
||||
for pid in perms:
|
||||
@@ -284,7 +280,7 @@ async def admin_create_role(
|
||||
if perm_uuid_str not in grantable:
|
||||
raise ValueError(f"Permission not grantable by org: {pid}")
|
||||
permission_uuids.append(perm_uuid_str)
|
||||
|
||||
|
||||
role = RoleDC(
|
||||
uuid=role_uuid,
|
||||
org_uuid=org_uuid,
|
||||
@@ -1060,9 +1056,7 @@ async def admin_rename_permission(
|
||||
_check_admin_lockout(str(perm.uuid), domain_value, request.headers.get("host"))
|
||||
|
||||
# All current backends support rename_permission
|
||||
db.rename_permission(
|
||||
old_scope, new_scope, display_name, domain_value, ctx=ctx
|
||||
)
|
||||
db.rename_permission(old_scope, new_scope, display_name, domain_value, ctx=ctx)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
|
||||
+3
-1
@@ -120,7 +120,9 @@ async def second_org_session_token(
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
async def org_admin_role(test_db: DB, test_org: Org, org_admin_permission: Permission) -> Role:
|
||||
async def org_admin_role(
|
||||
test_db: DB, test_org: Org, org_admin_permission: Permission
|
||||
) -> Role:
|
||||
"""Create a role with org admin permission only (no global admin)."""
|
||||
role = Role(
|
||||
uuid=uuid7.create(),
|
||||
|
||||
Reference in New Issue
Block a user