Skip to content

Commit 7d82686

Browse files
committed
nit(rbac): use namedtuples for scope definitions
1 parent a34c7ca commit 7d82686

File tree

3 files changed

+105
-69
lines changed

3 files changed

+105
-69
lines changed

tracecat/api/app.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
auth_backend,
4545
fastapi_users,
4646
)
47+
from tracecat.authz.seeding import seed_all_system_data
4748
from tracecat.cases.attachments.internal_router import (
4849
router as internal_case_attachments_router,
4950
)
@@ -70,6 +71,7 @@
7071
from tracecat.cases.triggers.consumer import start_case_trigger_consumer
7172
from tracecat.contexts import ctx_role
7273
from tracecat.db.dependencies import AsyncDBSession
74+
from tracecat.db.engine import get_async_session_context_manager
7375
from tracecat.editor.router import router as editor_router
7476
from tracecat.exceptions import EntitlementRequired, ScopeDeniedError, TracecatException
7577
from tracecat.feature_flags import (
@@ -150,9 +152,6 @@ async def lifespan(app: FastAPI):
150152

151153
await ensure_default_organization()
152154

153-
# Seed RBAC defaults (scopes and preset roles for all orgs)
154-
from tracecat.db.engine import get_async_session_context_manager
155-
156155
async with get_async_session_context_manager() as session:
157156
await setup_rbac_defaults(session)
158157

@@ -240,8 +239,6 @@ async def setup_workspace_defaults(session: AsyncSession, admin_role: Role):
240239

241240
async def setup_rbac_defaults(session: AsyncSession):
242241
"""Seed system scopes and roles for RBAC."""
243-
from tracecat.authz.seeding import seed_all_system_data
244-
245242
try:
246243
result = await seed_all_system_data(session)
247244
logger.info("RBAC defaults seeded", **result)

tracecat/authz/seeding.py

Lines changed: 101 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,110 +8,140 @@
88
Seeding is idempotent - existing scopes/roles are not duplicated.
99
"""
1010

11-
from __future__ import annotations
12-
13-
from typing import TYPE_CHECKING
11+
from typing import NamedTuple
1412
from uuid import UUID, uuid4
1513

1614
from sqlalchemy import select
1715
from sqlalchemy.dialects.postgresql import insert as pg_insert
16+
from sqlalchemy.ext.asyncio import AsyncSession
1817

1918
from tracecat.authz.enums import ScopeSource
2019
from tracecat.authz.scopes import PRESET_ROLE_SCOPES
2120
from tracecat.db.models import Organization, Role, RoleScope, Scope
2221
from tracecat.logger import logger
2322

24-
if TYPE_CHECKING:
25-
from sqlalchemy.ext.asyncio import AsyncSession
26-
2723
# =============================================================================
2824
# System Scope Definitions
2925
# =============================================================================
3026

3127
# These are the canonical system scopes that should exist in every deployment.
32-
# Format: (name, resource, action, description)
3328

34-
SYSTEM_SCOPE_DEFINITIONS: list[tuple[str, str, str, str]] = [
29+
30+
class ScopeDefinition(NamedTuple):
31+
name: str
32+
resource: str
33+
action: str
34+
description: str
35+
36+
37+
SYSTEM_SCOPE_DEFINITIONS: list[ScopeDefinition] = [
3538
# Org-level scopes
36-
("org:read", "org", "read", "View organization settings"),
37-
("org:update", "org", "update", "Modify organization settings"),
38-
("org:delete", "org", "delete", "Delete organization"),
39+
ScopeDefinition("org:read", "org", "read", "View organization settings"),
40+
ScopeDefinition("org:update", "org", "update", "Modify organization settings"),
41+
ScopeDefinition("org:delete", "org", "delete", "Delete organization"),
3942
# Org member management
40-
("org:member:read", "org:member", "read", "List organization members"),
41-
("org:member:invite", "org:member", "invite", "Invite users to organization"),
42-
("org:member:remove", "org:member", "remove", "Remove users from organization"),
43-
("org:member:update", "org:member", "update", "Change member organization roles"),
43+
ScopeDefinition(
44+
"org:member:read", "org:member", "read", "List organization members"
45+
),
46+
ScopeDefinition(
47+
"org:member:invite", "org:member", "invite", "Invite users to organization"
48+
),
49+
ScopeDefinition(
50+
"org:member:remove", "org:member", "remove", "Remove users from organization"
51+
),
52+
ScopeDefinition(
53+
"org:member:update", "org:member", "update", "Change member organization roles"
54+
),
4455
# Billing
45-
("org:billing:read", "org:billing", "read", "View billing information"),
46-
("org:billing:manage", "org:billing", "manage", "Manage billing"),
56+
ScopeDefinition(
57+
"org:billing:read", "org:billing", "read", "View billing information"
58+
),
59+
ScopeDefinition("org:billing:manage", "org:billing", "manage", "Manage billing"),
4760
# RBAC administration
48-
(
61+
ScopeDefinition(
4962
"org:rbac:read",
5063
"org:rbac",
5164
"read",
5265
"View roles, scopes, groups, and assignments",
5366
),
54-
(
67+
ScopeDefinition(
5568
"org:rbac:manage",
5669
"org:rbac",
5770
"manage",
5871
"Create/update/delete roles, scopes, groups, and manage assignments",
5972
),
6073
# Workspace-level scopes
61-
("workspace:read", "workspace", "read", "View workspace settings"),
62-
("workspace:create", "workspace", "create", "Create workspaces"),
63-
("workspace:update", "workspace", "update", "Modify workspace settings"),
64-
("workspace:delete", "workspace", "delete", "Delete workspace"),
74+
ScopeDefinition("workspace:read", "workspace", "read", "View workspace settings"),
75+
ScopeDefinition("workspace:create", "workspace", "create", "Create workspaces"),
76+
ScopeDefinition(
77+
"workspace:update", "workspace", "update", "Modify workspace settings"
78+
),
79+
ScopeDefinition("workspace:delete", "workspace", "delete", "Delete workspace"),
6580
# Workspace member management
66-
("workspace:member:read", "workspace:member", "read", "List workspace members"),
67-
("workspace:member:invite", "workspace:member", "invite", "Add users to workspace"),
68-
(
81+
ScopeDefinition(
82+
"workspace:member:read", "workspace:member", "read", "List workspace members"
83+
),
84+
ScopeDefinition(
85+
"workspace:member:invite",
86+
"workspace:member",
87+
"invite",
88+
"Add users to workspace",
89+
),
90+
ScopeDefinition(
6991
"workspace:member:remove",
7092
"workspace:member",
7193
"remove",
7294
"Remove users from workspace",
7395
),
74-
(
96+
ScopeDefinition(
7597
"workspace:member:update",
7698
"workspace:member",
7799
"update",
78100
"Change member workspace roles",
79101
),
80102
# Workflow scopes
81-
("workflow:read", "workflow", "read", "View workflows and their details"),
82-
("workflow:create", "workflow", "create", "Create new workflows"),
83-
("workflow:update", "workflow", "update", "Modify existing workflows"),
84-
("workflow:delete", "workflow", "delete", "Delete workflows"),
85-
("workflow:execute", "workflow", "execute", "Run/trigger workflows"),
103+
ScopeDefinition(
104+
"workflow:read", "workflow", "read", "View workflows and their details"
105+
),
106+
ScopeDefinition("workflow:create", "workflow", "create", "Create new workflows"),
107+
ScopeDefinition(
108+
"workflow:update", "workflow", "update", "Modify existing workflows"
109+
),
110+
ScopeDefinition("workflow:delete", "workflow", "delete", "Delete workflows"),
111+
ScopeDefinition("workflow:execute", "workflow", "execute", "Run/trigger workflows"),
86112
# Case scopes
87-
("case:read", "case", "read", "View cases"),
88-
("case:create", "case", "create", "Create new cases"),
89-
("case:update", "case", "update", "Modify existing cases"),
90-
("case:delete", "case", "delete", "Delete cases"),
113+
ScopeDefinition("case:read", "case", "read", "View cases"),
114+
ScopeDefinition("case:create", "case", "create", "Create new cases"),
115+
ScopeDefinition("case:update", "case", "update", "Modify existing cases"),
116+
ScopeDefinition("case:delete", "case", "delete", "Delete cases"),
91117
# Table scopes
92-
("table:read", "table", "read", "View tables"),
93-
("table:create", "table", "create", "Create new tables"),
94-
("table:update", "table", "update", "Modify existing tables"),
95-
("table:delete", "table", "delete", "Delete tables"),
118+
ScopeDefinition("table:read", "table", "read", "View tables"),
119+
ScopeDefinition("table:create", "table", "create", "Create new tables"),
120+
ScopeDefinition("table:update", "table", "update", "Modify existing tables"),
121+
ScopeDefinition("table:delete", "table", "delete", "Delete tables"),
96122
# Schedule scopes
97-
("schedule:read", "schedule", "read", "View schedules"),
98-
("schedule:create", "schedule", "create", "Create new schedules"),
99-
("schedule:update", "schedule", "update", "Modify existing schedules"),
100-
("schedule:delete", "schedule", "delete", "Delete schedules"),
123+
ScopeDefinition("schedule:read", "schedule", "read", "View schedules"),
124+
ScopeDefinition("schedule:create", "schedule", "create", "Create new schedules"),
125+
ScopeDefinition(
126+
"schedule:update", "schedule", "update", "Modify existing schedules"
127+
),
128+
ScopeDefinition("schedule:delete", "schedule", "delete", "Delete schedules"),
101129
# Agent scopes
102-
("agent:read", "agent", "read", "View agents"),
103-
("agent:create", "agent", "create", "Create new agents"),
104-
("agent:update", "agent", "update", "Modify existing agents"),
105-
("agent:delete", "agent", "delete", "Delete agents"),
106-
("agent:execute", "agent", "execute", "Run/trigger agents"),
130+
ScopeDefinition("agent:read", "agent", "read", "View agents"),
131+
ScopeDefinition("agent:create", "agent", "create", "Create new agents"),
132+
ScopeDefinition("agent:update", "agent", "update", "Modify existing agents"),
133+
ScopeDefinition("agent:delete", "agent", "delete", "Delete agents"),
134+
ScopeDefinition("agent:execute", "agent", "execute", "Run/trigger agents"),
107135
# Secret scopes
108-
("secret:read", "secret", "read", "View secrets"),
109-
("secret:create", "secret", "create", "Create new secrets"),
110-
("secret:update", "secret", "update", "Modify existing secrets"),
111-
("secret:delete", "secret", "delete", "Delete secrets"),
136+
ScopeDefinition("secret:read", "secret", "read", "View secrets"),
137+
ScopeDefinition("secret:create", "secret", "create", "Create new secrets"),
138+
ScopeDefinition("secret:update", "secret", "update", "Modify existing secrets"),
139+
ScopeDefinition("secret:delete", "secret", "delete", "Delete secrets"),
112140
# Wildcard action scopes (for role assignments)
113-
("action:*:execute", "action", "execute", "Execute any registry action"),
114-
(
141+
ScopeDefinition(
142+
"action:*:execute", "action", "execute", "Execute any registry action"
143+
),
144+
ScopeDefinition(
115145
"action:core.*:execute",
116146
"action:core",
117147
"execute",
@@ -125,34 +155,42 @@
125155

126156
# Preset roles seeded per-organization.
127157
# Slugs match the keys in PRESET_ROLE_SCOPES from scopes.py.
128-
PRESET_ROLE_DEFINITIONS: dict[str, tuple[str, str, frozenset[str]]] = {
129-
# slug → (name, description, scopes)
130-
"workspace-viewer": (
158+
159+
160+
class RoleDefinition(NamedTuple):
161+
name: str
162+
description: str
163+
scopes: frozenset[str]
164+
165+
166+
PRESET_ROLE_DEFINITIONS: dict[str, RoleDefinition] = {
167+
# slug → RoleDefinition(name, description, scopes)
168+
"workspace-viewer": RoleDefinition(
131169
"Viewer",
132170
"Read-only access to workspace resources",
133171
PRESET_ROLE_SCOPES["workspace-viewer"],
134172
),
135-
"workspace-editor": (
173+
"workspace-editor": RoleDefinition(
136174
"Editor",
137175
"Create and edit resources, no delete or admin access",
138176
PRESET_ROLE_SCOPES["workspace-editor"],
139177
),
140-
"workspace-admin": (
178+
"workspace-admin": RoleDefinition(
141179
"Admin",
142180
"Full workspace capabilities",
143181
PRESET_ROLE_SCOPES["workspace-admin"],
144182
),
145-
"organization-owner": (
183+
"organization-owner": RoleDefinition(
146184
"Owner",
147185
"Full organization control",
148186
PRESET_ROLE_SCOPES["organization-owner"],
149187
),
150-
"organization-admin": (
188+
"organization-admin": RoleDefinition(
151189
"Admin",
152190
"Organization admin without delete or billing manage",
153191
PRESET_ROLE_SCOPES["organization-admin"],
154192
),
155-
"organization-member": (
193+
"organization-member": RoleDefinition(
156194
"Member",
157195
"Basic organization membership",
158196
PRESET_ROLE_SCOPES["organization-member"],

tracecat/registry/sync/jobs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222
from tracecat.db.models import PlatformRegistryVersion
2323
from tracecat.logger import logger
24+
from tracecat.registry.actions.schemas import RegistryActionCreate
2425
from tracecat.registry.constants import DEFAULT_REGISTRY_ORIGIN
2526
from tracecat.registry.repositories.platform_service import PlatformRegistryReposService
2627
from tracecat.registry.sync.platform_service import PlatformRegistrySyncService
@@ -176,7 +177,7 @@ async def _sync_as_leader(session: AsyncSession, target_version: str) -> None:
176177

177178
async def _seed_registry_scopes(
178179
session: AsyncSession,
179-
actions: list,
180+
actions: list[RegistryActionCreate],
180181
) -> None:
181182
"""Seed registry scopes for synced actions.
182183

0 commit comments

Comments
 (0)