diff --git a/backend/app/gateway/routers/service_accounts.py b/backend/app/gateway/routers/service_accounts.py index a6c85a09..d206d3cd 100644 --- a/backend/app/gateway/routers/service_accounts.py +++ b/backend/app/gateway/routers/service_accounts.py @@ -7,6 +7,8 @@ cross-workspace targets return 404 (existence hidden). from __future__ import annotations +from typing import Literal + from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel, Field @@ -19,8 +21,11 @@ router = APIRouter(prefix="/api/v1/service-accounts", tags=["service-accounts"]) class CreateServiceAccountRequest(BaseModel): name: str = Field(..., min_length=1, max_length=64) - role: str = Field(default="member") - identity_mode: str = Field(default="collapsed") + # Constrained to Stage-1-supported values; widen these Literals in + # future stages alongside the behavior (Stage 2 RBAC opens role + # admin/viewer; the passthrough PR opens identity_mode). + role: Literal["member"] = "member" + identity_mode: Literal["collapsed"] = "collapsed" class UpdateServiceAccountRequest(BaseModel): diff --git a/backend/tests/test_service_accounts_router.py b/backend/tests/test_service_accounts_router.py index 68dc7280..47e919db 100644 --- a/backend/tests/test_service_accounts_router.py +++ b/backend/tests/test_service_accounts_router.py @@ -121,3 +121,26 @@ async def test_patch_other_workspace_sa_404(tmp_path): assert r.status_code == 404 finally: await _cleanup() + + +async def test_list_is_scoped_to_current_workspace(tmp_path): + await _init_db(tmp_path) + try: + w1 = TestClient(_make_app(role="owner", workspace_id="w-1")) + w1.post("/api/v1/service-accounts", json={"name": "bot-w1"}) + # A caller in w-2 must NOT see w-1's service accounts. + w2 = TestClient(_make_app(role="owner", workspace_id="w-2")) + rows = w2.get("/api/v1/service-accounts").json() + assert rows == [] + finally: + await _cleanup() + + +async def test_create_rejects_unknown_role(tmp_path): + await _init_db(tmp_path) + try: + client = TestClient(_make_app(role="owner")) + r = client.post("/api/v1/service-accounts", json={"name": "x", "role": "superadmin"}) + assert r.status_code == 422 + finally: + await _cleanup()