diff --git a/packages/tracecat-ee/tracecat_ee/rbac/router.py b/packages/tracecat-ee/tracecat_ee/rbac/router.py index f1374c0723..010c833b31 100644 --- a/packages/tracecat-ee/tracecat_ee/rbac/router.py +++ b/packages/tracecat-ee/tracecat_ee/rbac/router.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.exc import IntegrityError -from tracecat.auth.dependencies import OrgUserRole +from tracecat.auth.dependencies import OrgActorRole from tracecat.authz.controls import require_scope from tracecat.authz.enums import ScopeSource from tracecat.db.dependencies import AsyncDBSession @@ -38,7 +38,7 @@ async def _require_rbac_entitlement( - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, ) -> None: """Router-level dependency that gates all EE RBAC endpoints behind the RBAC entitlement.""" @@ -60,7 +60,7 @@ async def _require_rbac_entitlement( @require_scope("org:rbac:read") async def list_scopes( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, include_system: bool = Query(True, description="Include system/registry scopes"), source: ScopeSource | None = Query(None, description="Filter by scope source"), @@ -81,7 +81,7 @@ async def list_scopes( @require_scope("org:rbac:read") async def get_scope( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, scope_id: UUID, ) -> ScopeRead: @@ -101,7 +101,7 @@ async def get_scope( @require_scope("org:rbac:create") async def create_scope( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, params: ScopeCreate, ) -> ScopeRead: @@ -131,7 +131,7 @@ async def create_scope( @require_scope("org:rbac:delete") async def delete_scope( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, scope_id: UUID, ) -> None: @@ -165,7 +165,7 @@ async def delete_scope( @require_scope("org:rbac:read") async def get_role( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, role_id: UUID, ) -> RoleReadWithScopes: @@ -197,7 +197,7 @@ async def get_role( @require_scope("org:rbac:create") async def create_role( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, params: RoleCreate, ) -> RoleReadWithScopes: @@ -238,7 +238,7 @@ async def create_role( @require_scope("org:rbac:update") async def update_role( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, role_id: UUID, params: RoleUpdate, @@ -281,7 +281,7 @@ async def update_role( @require_scope("org:rbac:delete") async def delete_role( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, role_id: UUID, ) -> None: @@ -319,7 +319,7 @@ async def delete_role( @require_scope("org:rbac:read") async def list_groups( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, ) -> GroupList: """List groups for the organization. @@ -351,7 +351,7 @@ async def list_groups( @require_scope("org:rbac:read") async def get_group( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID, ) -> GroupReadWithMembers: @@ -393,7 +393,7 @@ async def get_group( @require_scope("org:rbac:create") async def create_group( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, params: GroupCreate, ) -> GroupReadWithMembers: @@ -429,7 +429,7 @@ async def create_group( @require_scope("org:rbac:update") async def update_group( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID, params: GroupUpdate, @@ -479,7 +479,7 @@ async def update_group( @require_scope("org:rbac:delete") async def delete_group( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID, ) -> None: @@ -508,7 +508,7 @@ async def delete_group( @require_scope("org:rbac:update") async def add_group_member( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID, params: GroupMemberAdd, @@ -541,7 +541,7 @@ async def add_group_member( @require_scope("org:rbac:update") async def remove_group_member( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID, user_id: UUID, @@ -572,7 +572,7 @@ async def remove_group_member( @require_scope("org:rbac:read") async def list_assignments( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, group_id: UUID | None = Query(None, description="Filter by group ID"), workspace_id: UUID | None = Query(None, description="Filter by workspace ID"), @@ -612,7 +612,7 @@ async def list_assignments( @require_scope("org:rbac:read") async def get_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, ) -> GroupRoleAssignmentReadWithDetails: @@ -647,7 +647,7 @@ async def get_assignment( @require_scope("org:rbac:create") async def create_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, params: GroupRoleAssignmentCreate, ) -> GroupRoleAssignmentReadWithDetails: @@ -693,7 +693,7 @@ async def create_assignment( @require_scope("org:rbac:update") async def update_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, params: GroupRoleAssignmentUpdate, @@ -727,7 +727,7 @@ async def update_assignment( @require_scope("org:rbac:delete") async def delete_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, ) -> None: diff --git a/tests/unit/test_service_accounts_validation.py b/tests/unit/test_service_accounts_validation.py index f9c75df30e..9423518e0c 100644 --- a/tests/unit/test_service_accounts_validation.py +++ b/tests/unit/test_service_accounts_validation.py @@ -101,6 +101,10 @@ def test_workspace_service_account_assignable_scope_rejects_user_only_scopes( "workspace:create", "workflow:update", "table:read", + "org:rbac:read", + "org:rbac:create", + "org:rbac:update", + "org:rbac:delete", "action:tools.slack.post_message:execute", ], ) @@ -119,7 +123,6 @@ def test_org_service_account_assignable_scope_allows_supported_api_key_scopes( [ "org:settings:read", "org:settings:update", - "org:rbac:read", "org:registry:read", "org:member:invite", "variable:read", diff --git a/tracecat/authz/rbac/router.py b/tracecat/authz/rbac/router.py index fb6cb341c5..4dd04b66e4 100644 --- a/tracecat/authz/rbac/router.py +++ b/tracecat/authz/rbac/router.py @@ -23,7 +23,7 @@ from tracecat_ee.rbac.service import RBACService from tracecat.auth.credentials import RoleACL -from tracecat.auth.dependencies import OrgUserRole +from tracecat.auth.dependencies import OrgActorRole from tracecat.auth.types import Role from tracecat.authz.controls import require_scope from tracecat.db.dependencies import AsyncDBSession @@ -82,7 +82,7 @@ async def get_my_scopes( @roles_router.get("", response_model=RoleList) async def list_roles( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, ) -> RoleList: """List roles for the organization. @@ -152,7 +152,7 @@ def _assignment_to_read( @require_scope("org:rbac:read") async def list_user_assignments( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, user_id: UUID | None = Query(None, description="Filter by user ID"), workspace_id: UUID | None = Query(None, description="Filter by workspace ID"), @@ -175,7 +175,7 @@ async def list_user_assignments( @require_scope("org:rbac:read") async def get_user_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, ) -> UserRoleAssignmentReadWithDetails: @@ -196,7 +196,7 @@ async def get_user_assignment( @require_scope("org:rbac:create") async def create_user_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, params: UserRoleAssignmentCreate, ) -> UserRoleAssignmentReadWithDetails: @@ -231,7 +231,7 @@ async def create_user_assignment( @require_scope("org:rbac:update") async def update_user_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, params: UserRoleAssignmentUpdate, @@ -254,7 +254,7 @@ async def update_user_assignment( @require_scope("org:rbac:delete") async def delete_user_assignment( *, - role: OrgUserRole, + role: OrgActorRole, session: AsyncDBSession, assignment_id: UUID, ) -> None: diff --git a/tracecat/service_accounts/constants.py b/tracecat/service_accounts/constants.py index b8ac276e39..f96a455da3 100644 --- a/tracecat/service_accounts/constants.py +++ b/tracecat/service_accounts/constants.py @@ -64,6 +64,12 @@ "org:secret:delete", "org:workspace:read", "workspace:create", + # RBAC management — lets a service account mirror user/role + # assignments across workspaces (e.g. membership-sync principals). + "org:rbac:read", + "org:rbac:create", + "org:rbac:update", + "org:rbac:delete", } ) )