Spaces:
Running
Running
| from __future__ import annotations | |
| from collections.abc import Mapping, Sequence | |
| ALL_SCOPES = frozenset( | |
| { | |
| "templates:read", | |
| "templates:run", | |
| "templates:create", | |
| "templates:update", | |
| "templates:delete", | |
| "templates:apply", | |
| "templates:publish", | |
| "operations:read", | |
| "operations:execute", | |
| "jobs:read", | |
| "jobs:create", | |
| "jobs:cancel", | |
| "assets:read", | |
| "assets:write", | |
| "assets:delete", | |
| "mcp:read", | |
| "mcp:execute", | |
| "system:read", | |
| "social:accounts:read", | |
| "social:accounts:write", | |
| "social:posts:read", | |
| "social:posts:write", | |
| "social:posts:publish", | |
| "social:schedules:read", | |
| "social:schedules:write", | |
| "social:analytics:read", | |
| "analytics:read", | |
| "analytics:sync", | |
| "analytics:export", | |
| "generation:providers:read", | |
| "generation:requests:read", | |
| "generation:requests:create", | |
| "generation:jobs:cancel", | |
| "ai:read", | |
| "ai:generate", | |
| "ai:transform", | |
| "ai:analyze", | |
| "ai:create", | |
| "copilot:read", | |
| "copilot:execute", | |
| "projects:read", | |
| "projects:create", | |
| "projects:update", | |
| "projects:delete", | |
| # Frontend Architecture v2 uses this aggregate solely for capability | |
| # rendering. Enforcement remains method-specific in ScopePolicy. | |
| "projects:write", | |
| "members:read", | |
| "members:invite", | |
| "members:update", | |
| "members:remove", | |
| "teams:read", | |
| "teams:create", | |
| "teams:update", | |
| "teams:delete", | |
| "projects:share", | |
| "projects:collaborate", | |
| "comments:read", | |
| "comments:create", | |
| "approvals:read", | |
| "approvals:create", | |
| "approvals:review", | |
| "approvals:manage", | |
| "admin", | |
| } | |
| ) | |
| DEFAULT_ROLE_SCOPES: dict[str, frozenset[str]] = { | |
| "admin": frozenset({"admin"}), | |
| "developer": frozenset( | |
| { | |
| "templates:read", | |
| "templates:run", | |
| "templates:create", | |
| "templates:update", | |
| "templates:delete", | |
| "templates:apply", | |
| "templates:publish", | |
| "operations:read", | |
| "operations:execute", | |
| "jobs:read", | |
| "jobs:create", | |
| "jobs:cancel", | |
| "assets:read", | |
| "assets:write", | |
| "mcp:read", | |
| "mcp:execute", | |
| "system:read", | |
| "social:accounts:read", | |
| "social:accounts:write", | |
| "social:posts:read", | |
| "social:posts:write", | |
| "social:posts:publish", | |
| "social:schedules:read", | |
| "social:schedules:write", | |
| "social:analytics:read", | |
| "analytics:read", | |
| "analytics:sync", | |
| "analytics:export", | |
| "generation:providers:read", | |
| "generation:requests:read", | |
| "generation:requests:create", | |
| "generation:jobs:cancel", | |
| "ai:read", | |
| "ai:generate", | |
| "ai:transform", | |
| "ai:analyze", | |
| "ai:create", | |
| "copilot:read", | |
| "copilot:execute", | |
| "projects:read", | |
| "projects:create", | |
| "projects:update", | |
| "projects:delete", | |
| "projects:write", | |
| "members:read", | |
| "members:invite", | |
| "members:update", | |
| "members:remove", | |
| "teams:read", | |
| "teams:create", | |
| "teams:update", | |
| "teams:delete", | |
| "projects:share", | |
| "projects:collaborate", | |
| "comments:read", | |
| "comments:create", | |
| "approvals:read", | |
| "approvals:create", | |
| "approvals:review", | |
| "approvals:manage", | |
| } | |
| ), | |
| "operator": frozenset( | |
| { | |
| "templates:read", | |
| "templates:run", | |
| "templates:create", | |
| "templates:update", | |
| "templates:delete", | |
| "templates:apply", | |
| "templates:publish", | |
| "operations:read", | |
| "operations:execute", | |
| "jobs:read", | |
| "jobs:create", | |
| "jobs:cancel", | |
| "assets:read", | |
| "mcp:read", | |
| "mcp:execute", | |
| "system:read", | |
| "social:accounts:read", | |
| "social:accounts:write", | |
| "social:posts:read", | |
| "social:posts:write", | |
| "social:posts:publish", | |
| "social:schedules:read", | |
| "social:schedules:write", | |
| "social:analytics:read", | |
| "analytics:read", | |
| "analytics:sync", | |
| "generation:providers:read", | |
| "generation:requests:read", | |
| "generation:requests:create", | |
| "generation:jobs:cancel", | |
| "ai:read", | |
| "ai:generate", | |
| "ai:transform", | |
| "ai:analyze", | |
| "ai:create", | |
| "copilot:read", | |
| "copilot:execute", | |
| "projects:read", | |
| "projects:create", | |
| "projects:update", | |
| "projects:delete", | |
| "projects:write", | |
| "members:read", | |
| "teams:read", | |
| "projects:share", | |
| "projects:collaborate", | |
| "comments:read", | |
| "comments:create", | |
| "approvals:read", | |
| "approvals:review", | |
| } | |
| ), | |
| "viewer": frozenset( | |
| { | |
| "templates:read", | |
| "operations:read", | |
| "jobs:read", | |
| "assets:read", | |
| "mcp:read", | |
| "system:read", | |
| "social:accounts:read", | |
| "social:posts:read", | |
| "social:schedules:read", | |
| "social:analytics:read", | |
| "analytics:read", | |
| "generation:providers:read", | |
| "generation:requests:read", | |
| "ai:read", | |
| "copilot:read", | |
| "projects:read", | |
| "members:read", | |
| "teams:read", | |
| "projects:collaborate", | |
| "comments:read", | |
| "approvals:read", | |
| } | |
| ), | |
| } | |
| def configured_roles( | |
| overrides: Mapping[str, Sequence[str]] | None = None, | |
| ) -> dict[str, frozenset[str]]: | |
| roles = dict(DEFAULT_ROLE_SCOPES) | |
| for role, scopes in (overrides or {}).items(): | |
| normalized_role = role.strip().lower() | |
| normalized_scopes = frozenset(scope.strip().lower() for scope in scopes) | |
| unknown = normalized_scopes - ALL_SCOPES | |
| if unknown: | |
| raise ValueError(f"Role '{normalized_role}' has unsupported scopes: {sorted(unknown)}") | |
| roles[normalized_role] = normalized_scopes | |
| return roles | |
| def effective_scopes( | |
| role: str | None, | |
| explicit_scopes: Sequence[str], | |
| overrides: Mapping[str, Sequence[str]] | None = None, | |
| ) -> frozenset[str]: | |
| scopes = set(scope.strip().lower() for scope in explicit_scopes) | |
| if role: | |
| scopes.update(configured_roles(overrides).get(role.strip().lower(), ())) | |
| project_mutations = {"projects:create", "projects:update", "projects:delete"} | |
| if "projects:write" in scopes: | |
| scopes.update(project_mutations) | |
| if project_mutations.issubset(scopes): | |
| scopes.add("projects:write") | |
| if "admin" in scopes: | |
| return ALL_SCOPES | |
| return frozenset(scopes) | |