"""应用级权限(§3.12):use 能用(入口、视图、自己的实例);observe 另能看全部实例;manage 能搭建、发布、授权、管钩子。 - 关登录:全部放行,等同 manage(界面上挂横幅说明,签批人是自报的署名); - admin:所有应用 manage; - builder:所有应用至少 observe(能用、能看全部实例);自己建的、或者有 manage 授权的才 manage; - user:按授权(给本人、给所在的组、给所有人)取最高的那一档,最高到 observe:搭建、发布、绑定岗位、授权、钩子、按草稿 预览、改派催办都不给 user 角色(§3.12),授给它的 manage 按 observe 算。没有授权就看不到这个应用。 """ from __future__ import annotations from typing import Any from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import Principal from app.db.models_apps import AppGrant RANK = {"use": 1, "observe": 2, "manage": 3} def at_least(role: str | None, need: str) -> bool: return RANK.get(role or "", 0) >= RANK[need] def granted_role(principal: Principal, grants: list[Any]) -> str | None: """授权里这个人能拿到的最高一档。""" best: str | None = None groups = set(principal.groups) for g in grants: hit = (g.principal_kind == "everyone" or (g.principal_kind == "user" and principal.username and g.principal == principal.username) or (g.principal_kind == "group" and g.principal in groups)) if hit and RANK.get(g.role, 0) > RANK.get(best or "", 0): best = g.role return best def role_of(principal: Principal, app: Any, grants: list[Any]) -> str | None: if principal.mode == "off" or principal.role == "admin": return "manage" granted = granted_role(principal, grants) if principal.role == "builder": if app.created_by and principal.username and app.created_by == principal.username: return "manage" return granted if at_least(granted, "manage") else "observe" return "observe" if at_least(granted, "manage") else granted async def grants_of(session: AsyncSession, app_id: str) -> list[AppGrant]: return list((await session.execute(select(AppGrant).where(AppGrant.app_id == app_id) .order_by(AppGrant.created_at))).scalars()) async def app_role(session: AsyncSession, principal: Principal, app: Any) -> str | None: if principal.mode == "off" or principal.role == "admin": return "manage" return role_of(principal, app, await grants_of(session, app.id))