Enforce permissions in API guards and retire interim access #52

Closed
opened 2026-07-04 14:52:25 +02:00 by fable-5 · 1 comment
Collaborator

Context

Every route must pass through the real permission guard; the M2 interim service is deleted.

Scope

Implement the Nest PermissionGuard + decorators (@RequiresPondRole('reader'|'editor'|'pond_admin'), page-level variants) loading grants+labels efficiently (per-request cache; per (user,pond) cache with invalidation on grant/label change), apply to every existing route (ponds, pages, files, labels, search, versions, trash), delete InterimAccessService, and add grant CRUD endpoints (/ponds/:id/grants) guarded by pond_admin.

Acceptance criteria

  • grep proves InterimAccessService is gone; all routes carry an explicit permission decorator (lint rule or test enumerating routes)
  • fixture matrix e2e: reader cannot edit, editor cannot manage members, pond admin can, foreign user sees 404/403 consistently
  • grant changes invalidate the cache (test: revoke then immediate request is denied)
  • audit log lines for every grant change

Technical notes

  • permissions.md, security.md (default-closed, audit).
  • 404 vs 403 policy: unauthorized reads return 404 (existence hiding); write attempts on readable pages return 403 — document in the API conventions.

Dependencies

Depends on #51.

Size: ~2 days


Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add de and en), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.

## Context Every route must pass through the real permission guard; the M2 interim service is deleted. ## Scope Implement the Nest `PermissionGuard` + decorators (`@RequiresPondRole('reader'|'editor'|'pond_admin')`, page-level variants) loading grants+labels efficiently (per-request cache; per (user,pond) cache with invalidation on grant/label change), apply to every existing route (ponds, pages, files, labels, search, versions, trash), delete `InterimAccessService`, and add grant CRUD endpoints (`/ponds/:id/grants`) guarded by pond_admin. ## Acceptance criteria - [ ] grep proves `InterimAccessService` is gone; all routes carry an explicit permission decorator (lint rule or test enumerating routes) - [ ] fixture matrix e2e: reader cannot edit, editor cannot manage members, pond admin can, foreign user sees 404/403 consistently - [ ] grant changes invalidate the cache (test: revoke then immediate request is denied) - [ ] audit log lines for every grant change ## Technical notes - permissions.md, security.md (default-closed, audit). - 404 vs 403 policy: unauthorized reads return 404 (existence hiding); write attempts on readable pages return 403 — document in the API conventions. ## Dependencies Depends on #51. **Size**: ~2 days --- *Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add `de` **and** `en`), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.*
fable-5 added this to the M5 — Permissions & quotas milestone 2026-07-04 14:52:25 +02:00
fable-5 added the
auth
backend
labels 2026-07-04 14:52:25 +02:00
Author
Collaborator

Implemented in 0c6494f, pipeline fully green (CI + CD: Test deploy, smoke, Int promotion), verified live on Test and Int.

Acceptance criteria:

  • InterimAccessService is gonegrep -rn InterimAccessService apps packages over source is empty; the service file is deleted. Every route now carries an explicit access declaration (@RequiresPondRole / @RequiresPagePermission / @RequiresAttachmentPermission / @AuthenticatedOnly / @Public / Site-Admin guard), enforced by the new PermissionGuard, and the route-enumeration test route-permissions.e2e.db.test.ts fails CI for any future route without one.
  • Fixture matrix e2e (permissions.e2e.db.test.ts): reader cannot edit (403 on readable pages, per the documented 404/403 policy), editor cannot manage members/labels/settings (403), a second pond admin can, foreign users see 404 consistently (pond, page, grants, trash, pond list). Also covered: label-scope deny beating pond-scope allow, authenticated-subject grants, trash gated on write capability, ro/rw collab tokens, personal-pond admin rules.
  • Grant changes invalidate the cache — revoke followed by an immediate request is denied (test in the matrix); every grant mutation invalidates the pond context cache and fires pond_access_changed for the collab server (#39/#53).
  • Audit log lines for every grant changeaudit: grant created / audit: grant deleted with pond, subject, role, scope, effect, and acting user.

Also in this change: grant CRUD under /ponds/:id/grants (pond_admin-gated) with referential validation (grant_scope_not_found / grant_subject_not_found) and last-admin protection (grant_last_admin, de+en); owner Pond-Admin grants backfilled by migration 20260709140000_owner_admin_grants and created transactionally with every new pond; page lists, search, backlinks, phantom links, and trash listings filtered per page through the shared resolver; 404/403 policy documented in the architecture README conventions.

Live verification: after the deploy, all ponds on Test and Int carry their owner's pond_admin grant (migration backfill), the grants endpoint is pond_admin-gated, and pond visibility follows grants (fixture-user sees only their pond; site admin sees all).

**Implemented in `0c6494f`, pipeline fully green (CI + CD: Test deploy, smoke, Int promotion), verified live on Test and Int.** Acceptance criteria: - **`InterimAccessService` is gone** — `grep -rn InterimAccessService apps packages` over source is empty; the service file is deleted. Every route now carries an explicit access declaration (`@RequiresPondRole` / `@RequiresPagePermission` / `@RequiresAttachmentPermission` / `@AuthenticatedOnly` / `@Public` / Site-Admin guard), enforced by the new `PermissionGuard`, and the route-enumeration test `route-permissions.e2e.db.test.ts` fails CI for any future route without one. - **Fixture matrix e2e** (`permissions.e2e.db.test.ts`): reader cannot edit (403 on readable pages, per the documented 404/403 policy), editor cannot manage members/labels/settings (403), a second pond admin can, foreign users see 404 consistently (pond, page, grants, trash, pond list). Also covered: label-scope deny beating pond-scope allow, `authenticated`-subject grants, trash gated on write capability, ro/rw collab tokens, personal-pond admin rules. - **Grant changes invalidate the cache** — revoke followed by an immediate request is denied (test in the matrix); every grant mutation invalidates the pond context cache and fires `pond_access_changed` for the collab server (#39/#53). - **Audit log lines for every grant change** — `audit: grant created` / `audit: grant deleted` with pond, subject, role, scope, effect, and acting user. Also in this change: grant CRUD under `/ponds/:id/grants` (pond_admin-gated) with referential validation (`grant_scope_not_found` / `grant_subject_not_found`) and last-admin protection (`grant_last_admin`, de+en); owner Pond-Admin grants backfilled by migration `20260709140000_owner_admin_grants` and created transactionally with every new pond; page lists, search, backlinks, phantom links, and trash listings filtered per page through the shared resolver; 404/403 policy documented in the architecture README conventions. Live verification: after the deploy, all ponds on Test and Int carry their owner's `pond_admin` grant (migration backfill), the grants endpoint is pond_admin-gated, and pond visibility follows grants (fixture-user sees only their pond; site admin sees all).
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#52
No description provided.