feat(tenant): grant points scopes on provisioned keys
Why: - points:read and points:write are named in ADR-0009 but were absent from DEFAULT_SCOPES, so a provisioned key could not reach the read paths that follow. The runbook documented only files:write and domains:write, which understated what a default key can now do. Changes: - Add points:read and points:write to DEFAULT_SCOPES. - Document the full scope table in the runbook, calling out that points:read grants the text of every chunk of every file -- so an upload-only key gets files:write alone. Impact: - Keys issued before this change keep their existing scopes; provisioning does not backfill. Reissue or widen an existing key explicitly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,10 +80,22 @@ Re-running with the same `--slug` reuses the tenant and any domains it already
|
|||||||
has, and issues an **additional** key. Both keys stay valid; this adds a key, it
|
has, and issues an **additional** key. Both keys stay valid; this adds a key, it
|
||||||
does not rotate one.
|
does not rotate one.
|
||||||
|
|
||||||
Scopes are the security boundary between uploading and managing the allowlist.
|
Scopes are the security boundary between uploading, reading chunks, and
|
||||||
Give an upload client `files:write` only. `domains:write` lets its holder create
|
managing the allowlist. Give an upload client `files:write` only. `domains:write`
|
||||||
new domains, which is exactly what the allowlist exists to prevent an upload key
|
lets its holder create new domains, which is exactly what the allowlist exists to
|
||||||
from doing.
|
prevent an upload key from doing, and `points:read` lets its holder read the text
|
||||||
|
of every chunk of every file — so an upload-only key gets neither.
|
||||||
|
|
||||||
|
| Scope | Grants |
|
||||||
|
|---|---|
|
||||||
|
| `files:write` | Upload a document and read its ingestion status. |
|
||||||
|
| `points:read` | Read, list, count, and keyword-search this tenant's points, including `GET /v1/files/{file_id}/points`. |
|
||||||
|
| `points:write` | Create, edit, reorder, and soft-delete points (plan 002 Phases 3-5; no route uses it yet). |
|
||||||
|
| `domains:read` / `domains:write` | Inspect and manage the domain allowlist. |
|
||||||
|
| `admin` | Satisfies every scope check. |
|
||||||
|
|
||||||
|
The command's `--scopes` default issues all of the above except `admin`, which
|
||||||
|
suits a first operator key; narrow it explicitly for per-client keys.
|
||||||
|
|
||||||
### Domains after the first one
|
### Domains after the first one
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ from src.infrastructure.postgres.repositories import tenants as tenants_repo
|
|||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
DEFAULT_SCOPES = ("files:write", "domains:read", "domains:write")
|
DEFAULT_SCOPES = (
|
||||||
|
"files:write",
|
||||||
|
"domains:read",
|
||||||
|
"domains:write",
|
||||||
|
"points:read",
|
||||||
|
"points:write",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user