DB cleanup: removed get_permission_organizations and build_org. Using db.data() for read access at call sites.
This commit is contained in:
+6
-4
@@ -53,13 +53,15 @@ async def check_admin_credentials() -> bool:
|
||||
"""
|
||||
try:
|
||||
# Get permission organizations to find admin users
|
||||
permission_orgs = db.get_permission_organizations("auth:admin")
|
||||
|
||||
if not permission_orgs:
|
||||
p = next(
|
||||
(p for p in db.data().permissions.values() if p.scope == "auth:admin"), None
|
||||
)
|
||||
if not p or not p.orgs:
|
||||
return False
|
||||
|
||||
# Get users from the first organization with admin permission
|
||||
org_users = db.get_organization_users(str(permission_orgs[0].uuid))
|
||||
first_org_uuid = next(iter(p.orgs))
|
||||
org_users = db.get_organization_users(first_org_uuid)
|
||||
admin_users = [user for user, role in org_users if role == "Administration"]
|
||||
|
||||
if not admin_users:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Database module for WebAuthn passkey authentication.
|
||||
|
||||
Read: Access db() directly, use build_* to convert to public structs.
|
||||
Read: Access data() directly, use build_* to convert to public structs.
|
||||
CTX: get_session_context(key) returns SessionContext with effective permissions.
|
||||
Write: Functions validate and commit, or raise ValueError.
|
||||
|
||||
@@ -9,7 +9,7 @@ Usage:
|
||||
from paskia import db
|
||||
|
||||
# Read (after init)
|
||||
user_data = db.db().users[user_uuid]
|
||||
user_data = db.data().users[user_uuid]
|
||||
user = db.build_user(user_uuid)
|
||||
|
||||
# Context
|
||||
@@ -29,7 +29,6 @@ from paskia.db.operations import (
|
||||
add_permission_to_organization,
|
||||
add_permission_to_role,
|
||||
bootstrap,
|
||||
build_org,
|
||||
cleanup_expired,
|
||||
create_credential,
|
||||
create_credential_session,
|
||||
@@ -53,7 +52,6 @@ from paskia.db.operations import (
|
||||
get_organization_users,
|
||||
get_permission,
|
||||
get_permission_by_scope,
|
||||
get_permission_organizations,
|
||||
get_reset_token,
|
||||
get_role,
|
||||
get_roles_by_organization,
|
||||
@@ -92,7 +90,7 @@ from paskia.db.structs import (
|
||||
)
|
||||
|
||||
|
||||
def db() -> DB:
|
||||
def data() -> DB:
|
||||
"""Get the database instance for direct read access."""
|
||||
from paskia.db.operations import _db
|
||||
|
||||
@@ -111,7 +109,7 @@ __all__ = [
|
||||
"SessionContext",
|
||||
"User",
|
||||
# Instance
|
||||
"db",
|
||||
"data",
|
||||
"init",
|
||||
# Background
|
||||
"start_background",
|
||||
@@ -120,7 +118,6 @@ __all__ = [
|
||||
"stop_cleanup",
|
||||
# Builders
|
||||
"build_credential",
|
||||
"build_org",
|
||||
"build_permission",
|
||||
"build_reset_token",
|
||||
"build_role",
|
||||
@@ -133,7 +130,6 @@ __all__ = [
|
||||
"get_organization_users",
|
||||
"get_permission",
|
||||
"get_permission_by_scope",
|
||||
"get_permission_organizations",
|
||||
"get_reset_token",
|
||||
"get_role",
|
||||
"get_roles_by_organization",
|
||||
|
||||
+5
-31
@@ -48,19 +48,6 @@ async def init(*args, **kwargs):
|
||||
_db = _store.db
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Builders: Convert internal _*Data to public structs
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_org(uuid: UUID, include_roles: bool = False) -> Org:
|
||||
o = _db.orgs[uuid]
|
||||
o.permissions = {pid for pid, p in _db.permissions.items() if uuid in p.orgs}
|
||||
if include_roles:
|
||||
o.roles = [_db.roles[rid] for rid, r in _db.roles.items() if r.org == uuid]
|
||||
return o
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Read/lookup functions
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -116,19 +103,6 @@ def list_permissions() -> list[Permission]:
|
||||
return list(_db.permissions.values())
|
||||
|
||||
|
||||
def get_permission_organizations(scope: str) -> list[Org]:
|
||||
"""Get organizations that can grant a permission scope.
|
||||
|
||||
Call sites:
|
||||
- Get organizations that can grant auth:admin to find admin users (bootstrap.py:67)
|
||||
- Get organizations with auth:admin permission to find admin users for reset targets (reset.py:29,40,55)
|
||||
"""
|
||||
for p in _db.permissions.values():
|
||||
if p.scope == scope:
|
||||
return [build_org(org_uuid) for org_uuid in p.orgs]
|
||||
return []
|
||||
|
||||
|
||||
def get_organization(uuid: UUID) -> Org | None:
|
||||
"""Get organization by UUID.
|
||||
|
||||
@@ -136,7 +110,7 @@ def get_organization(uuid: UUID) -> Org | None:
|
||||
- Get organization when creating a role to check grantable permissions (admin.py:271)
|
||||
- Get organization when adding permission to role to check if org can grant it (admin.py:352)
|
||||
"""
|
||||
return build_org(uuid, include_roles=True) if uuid in _db.orgs else None
|
||||
return _db.orgs.get(uuid)
|
||||
|
||||
|
||||
def list_organizations() -> list[Org]:
|
||||
@@ -146,7 +120,7 @@ def list_organizations() -> list[Org]:
|
||||
- List organizations during migration (migrate/__init__.py:131)
|
||||
- Admin API endpoint to list organizations (admin.py:94)
|
||||
"""
|
||||
return [build_org(uuid, include_roles=True) for uuid in _db.orgs]
|
||||
return list(_db.orgs.values())
|
||||
|
||||
|
||||
def get_organization_users(org_uuid: UUID) -> list[tuple[User, str]]:
|
||||
@@ -216,7 +190,7 @@ def get_user_organization(user_uuid: UUID) -> tuple[Org, str]:
|
||||
raise ValueError(f"Role {role_uuid} not found")
|
||||
role_data = _db.roles[role_uuid]
|
||||
org_uuid = role_data.org
|
||||
return build_org(org_uuid, include_roles=True), role_data.display_name
|
||||
return _db.orgs[org_uuid], role_data.display_name
|
||||
|
||||
|
||||
def get_credential_by_id(credential_id: bytes) -> Credential | None:
|
||||
@@ -346,7 +320,7 @@ def get_session_context(
|
||||
session = _db.sessions[session_key]
|
||||
user = _db.users[s.user]
|
||||
role = _db.roles[role_uuid]
|
||||
org = build_org(org_uuid)
|
||||
org = _db.orgs[org_uuid]
|
||||
|
||||
# Credential must exist (sessions are cascade-deleted when credential is deleted)
|
||||
if s.credential not in _db.credentials:
|
||||
@@ -355,7 +329,7 @@ def get_session_context(
|
||||
|
||||
# Effective permissions: role's permissions that the org can grant
|
||||
# Also filter by domain if host is provided
|
||||
org_perm_uuids = org.permissions # set[UUID] computed by build_org
|
||||
org_perm_uuids = {pid for pid, p in _db.permissions.items() if org_uuid in p.orgs}
|
||||
normalized_host = normalize_host(host)
|
||||
host_without_port = normalized_host.rsplit(":", 1)[0] if normalized_host else None
|
||||
|
||||
|
||||
+13
-6
@@ -109,8 +109,12 @@ async def admin_list_orgs(request: Request, auth=AUTH_COOKIE):
|
||||
return {
|
||||
"uuid": str(o.uuid),
|
||||
"display_name": o.display_name,
|
||||
"permissions": o.permissions,
|
||||
"roles": [role_to_dict(r) for r in o.roles],
|
||||
"permissions": {
|
||||
pid for pid, p in db.data().permissions.items() if o.uuid in p.orgs
|
||||
},
|
||||
"roles": [
|
||||
role_to_dict(r) for r in db.data().roles.values() if r.org == o.uuid
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"uuid": str(u.uuid),
|
||||
@@ -291,7 +295,9 @@ async def admin_create_role(
|
||||
display_name = payload.get("display_name") or "New Role"
|
||||
perms = payload.get("permissions") or []
|
||||
org = db.get_organization(org_uuid)
|
||||
grantable = org.permissions # set[UUID] computed by build_org
|
||||
if not org:
|
||||
raise HTTPException(status_code=404, detail="Organization not found")
|
||||
grantable = {pid for pid, p in db.data().permissions.items() if org_uuid in p.orgs}
|
||||
|
||||
# Normalize permission IDs to UUIDs
|
||||
permission_uuids: set[UUID] = set()
|
||||
@@ -371,8 +377,7 @@ async def admin_add_role_permission(
|
||||
perm = db.get_permission(permission_uuid)
|
||||
if not perm:
|
||||
raise HTTPException(status_code=404, detail="Permission not found")
|
||||
org = db.get_organization(org_uuid)
|
||||
if permission_uuid not in org.permissions:
|
||||
if org_uuid not in perm.orgs:
|
||||
raise ValueError("Permission not grantable by organization")
|
||||
|
||||
db.add_permission_to_role(role_uuid, permission_uuid, ctx=ctx)
|
||||
@@ -940,7 +945,9 @@ async def admin_list_permissions(request: Request, auth=AUTH_COOKIE):
|
||||
return [_perm_to_dict(p) for p in perms]
|
||||
|
||||
# Org admins only see permissions their org can grant (by UUID)
|
||||
grantable = ctx.org.permissions # set[UUID]
|
||||
grantable = {
|
||||
pid for pid, p in db.data().permissions.items() if ctx.org.uuid in p.orgs
|
||||
}
|
||||
filtered_perms = [p for p in perms if p.uuid in grantable]
|
||||
return [_perm_to_dict(p) for p in filtered_perms]
|
||||
|
||||
|
||||
+21
-11
@@ -16,7 +16,7 @@ import asyncio
|
||||
from uuid import UUID
|
||||
|
||||
from paskia import authsession as _authsession
|
||||
from paskia import db as _db
|
||||
from paskia import db
|
||||
from paskia.util import hostutil, passphrase
|
||||
|
||||
|
||||
@@ -26,9 +26,13 @@ async def _resolve_targets(query: str | None):
|
||||
targets: list[tuple] = []
|
||||
try:
|
||||
q_uuid = UUID(query)
|
||||
perm_orgs = _db.get_permission_organizations("auth:admin")
|
||||
for o in perm_orgs:
|
||||
users = _db.get_organization_users(str(o.uuid))
|
||||
p = next(
|
||||
(p for p in db.data().permissions.values() if p.scope == "auth:admin"),
|
||||
None,
|
||||
)
|
||||
if p:
|
||||
for org_uuid in p.orgs:
|
||||
users = db.get_organization_users(org_uuid)
|
||||
for u, role_name in users:
|
||||
if u.uuid == q_uuid:
|
||||
return [(u, role_name)]
|
||||
@@ -37,9 +41,12 @@ async def _resolve_targets(query: str | None):
|
||||
pass
|
||||
# Substring search
|
||||
needle = query.lower()
|
||||
perm_orgs = _db.get_permission_organizations("auth:admin")
|
||||
for o in perm_orgs:
|
||||
users = _db.get_organization_users(str(o.uuid))
|
||||
p = next(
|
||||
(p for p in db.data().permissions.values() if p.scope == "auth:admin"), None
|
||||
)
|
||||
if p:
|
||||
for org_uuid in p.orgs:
|
||||
users = db.get_organization_users(org_uuid)
|
||||
for u, role_name in users:
|
||||
if needle in (u.display_name or "").lower():
|
||||
targets.append((u, role_name))
|
||||
@@ -52,10 +59,13 @@ async def _resolve_targets(query: str | None):
|
||||
deduped.append((u, role_name))
|
||||
return deduped
|
||||
# No query -> master admin
|
||||
perm_orgs = _db.get_permission_organizations("auth:admin")
|
||||
if not perm_orgs:
|
||||
p = next(
|
||||
(p for p in db.data().permissions.values() if p.scope == "auth:admin"), None
|
||||
)
|
||||
if not p or not p.orgs:
|
||||
return []
|
||||
users = _db.get_organization_users(str(perm_orgs[0].uuid))
|
||||
first_org_uuid = next(iter(p.orgs))
|
||||
users = db.get_organization_users(first_org_uuid)
|
||||
admin_users = [pair for pair in users if pair[1] == "Administration"]
|
||||
return admin_users[:1]
|
||||
|
||||
@@ -63,7 +73,7 @@ async def _resolve_targets(query: str | None):
|
||||
async def _create_reset(user, role_name: str):
|
||||
token = passphrase.generate()
|
||||
expiry = _authsession.reset_expires()
|
||||
_db.create_reset_token(
|
||||
db.create_reset_token(
|
||||
passphrase=token,
|
||||
user_uuid=user.uuid,
|
||||
expiry=expiry,
|
||||
|
||||
Reference in New Issue
Block a user