1717from sqlalchemy .dialects .postgresql import insert as pg_insert
1818
1919from tracecat .authz .enums import ScopeSource
20- from tracecat .authz .scopes import (
21- ADMIN_SCOPES ,
22- EDITOR_SCOPES ,
23- ORG_ADMIN_SCOPES ,
24- ORG_MEMBER_SCOPES ,
25- ORG_OWNER_SCOPES ,
26- VIEWER_SCOPES ,
27- )
20+ from tracecat .authz .scopes import PRESET_ROLE_SCOPES
2821from tracecat .db .models import Organization , Role , RoleScope , Scope
2922from tracecat .logger import logger
3023
130123# Preset Role Definitions
131124# =============================================================================
132125
133- # All preset role slugs
134- PRESET_ROLE_SLUGS : frozenset [str ] = frozenset (
135- {"owner" , "admin" , "editor" , "viewer" , "member" }
136- )
137-
138- # Preset role definitions: (slug, name, description, scopes)
139- #
140- # Roles can be assigned at org level (workspace_id=NULL) or workspace level (workspace_id set).
141- # The same role can be used at both levels; the assignment context determines what access applies.
142- #
143- # Role hierarchy:
144- # - owner: Organization owner with full control (org-level only)
145- # - admin: Full administrative access (can be org-level or workspace-level)
146- # - editor: Create/edit access without admin capabilities (workspace-level)
147- # - viewer: Read-only access (workspace-level)
148- # - member: Basic org membership without workspace access (org-level only)
149- #
150- # Note: The "admin" role combines org admin and workspace admin scopes since
151- # it may be assigned at either level.
152- PRESET_ROLE_DEFINITIONS : list [tuple [str , str , str , frozenset [str ]]] = [
153- (
154- "owner" ,
155- "Owner" ,
156- "Full organization control" ,
157- ORG_OWNER_SCOPES ,
158- ),
159- (
160- "admin" ,
161- "Admin" ,
162- "Full administrative access at organization or workspace level" ,
163- ADMIN_SCOPES | ORG_ADMIN_SCOPES , # Combined for flexibility
126+ # Preset roles seeded per-organization.
127+ # 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" : (
131+ "Viewer" ,
132+ "Read-only access to workspace resources" ,
133+ PRESET_ROLE_SCOPES ["workspace-viewer" ],
164134 ),
165- (
166- "editor" ,
135+ "workspace-editor" : (
167136 "Editor" ,
168137 "Create and edit resources, no delete or admin access" ,
169- EDITOR_SCOPES ,
138+ PRESET_ROLE_SCOPES [ "workspace-editor" ] ,
170139 ),
171- (
172- "viewer" ,
173- "Viewer" ,
174- "Read-only access to workspace resources" ,
175- VIEWER_SCOPES ,
140+ "workspace-admin" : (
141+ "Admin" ,
142+ "Full workspace capabilities" ,
143+ PRESET_ROLE_SCOPES ["workspace-admin" ],
176144 ),
177- (
178- "member" ,
145+ "organization-owner" : (
146+ "Owner" ,
147+ "Full organization control" ,
148+ PRESET_ROLE_SCOPES ["organization-owner" ],
149+ ),
150+ "organization-admin" : (
151+ "Admin" ,
152+ "Organization admin without delete or billing manage" ,
153+ PRESET_ROLE_SCOPES ["organization-admin" ],
154+ ),
155+ "organization-member" : (
179156 "Member" ,
180157 "Basic organization membership" ,
181- ORG_MEMBER_SCOPES ,
158+ PRESET_ROLE_SCOPES [ "organization-member" ] ,
182159 ),
183- ]
160+ }
161+
162+ PRESET_ROLE_SLUGS : frozenset [str ] = frozenset (PRESET_ROLE_DEFINITIONS )
184163
185164
186165# =============================================================================
@@ -376,7 +355,7 @@ async def seed_system_roles_for_org(
376355 # Prepare role values with pre-generated IDs
377356 role_values = []
378357 role_id_by_slug : dict [str , UUID ] = {}
379- for slug , name , description , _ in PRESET_ROLE_DEFINITIONS :
358+ for slug , ( name , description , _ ) in PRESET_ROLE_DEFINITIONS . items () :
380359 role_id = uuid4 ()
381360 role_id_by_slug [slug ] = role_id
382361 role_values .append (
@@ -401,15 +380,15 @@ async def seed_system_roles_for_org(
401380 # Re-query to get actual role IDs (may differ if roles already existed)
402381 existing_roles_stmt = select (Role .id , Role .slug ).where (
403382 Role .organization_id == organization_id ,
404- Role .slug .in_ ([ slug for slug , _ , _ , _ in PRESET_ROLE_DEFINITIONS ] ),
383+ Role .slug .in_ (PRESET_ROLE_DEFINITIONS ),
405384 )
406385 existing_roles_result = await session .execute (existing_roles_stmt )
407386 actual_role_id_by_slug : dict [str | None , UUID ] = {
408387 slug : role_id for role_id , slug in existing_roles_result .tuples ().all ()
409388 }
410389
411390 # Link scopes to roles
412- for slug , _ , _ , scope_names in PRESET_ROLE_DEFINITIONS :
391+ for slug , ( _ , _ , scope_names ) in PRESET_ROLE_DEFINITIONS . items () :
413392 role_id = actual_role_id_by_slug .get (slug )
414393 if role_id is None :
415394 logger .warning (
0 commit comments