Files
PubScript/CHUNK#10.md
T

74 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CHUNK #10 — Multi-User Admin & Sharing
**Duration:** ~2 days · **Prereqs:** CHUNK #2 (auth skeleton, ServerAdapter) · **Follows:** CHUNK #9 · **Unblocks:** CHUNK #11 (parity: admin works in web + desktop identically)
**Purpose:** Complete the multi-user product: full admin user management (create, disable, change role), project sharing with read/edit permissions, an audit log, and hard multi-tenant isolation — all reachable from **both** the web app and the desktop app identically.
---
## Steps
### 1. Admin API (`server/src/routes/admin.ts`)
1. `GET /api/admin/users` — list all users (name, email, role, status, lastActive).
2. `POST /api/admin/users` — create a user (email + role; server generates a one-time password or sends an invite link).
3. `PATCH /api/admin/users/:id` — change role (`admin`/`author`) or disable/enable.
4. `GET /api/admin/activity` — audit log tail (filtered by user/action/date).
5. `GET /api/admin/stats` — instance-wide: user count, project count, storage used.
6. All routes behind `requireAdmin` middleware.
### 2. Sharing API (`server/src/routes/projects.ts` — extend Chunk 2)
1. `POST /api/projects/:id/shares` — invite by email, grant `read`/`edit`.
2. `PATCH /api/projects/:id/shares/:userId` — change permission.
3. `DELETE /api/projects/:id/shares/:userId` — revoke.
4. `POST /api/projects/:id/transfer` — transfer ownership (owner only).
5. `GET /api/projects` — return owned ∪ shared (with `perm` on each).
### 3. Access control (`server/src/auth.ts` — extend Chunk 2)
1. `canAccessProject(projectId, perm)` — enforces owner OR shared-with (correct `perm`). Middleware on every content route.
2. **Multi-tenant isolation**: every query filters by `project_id` the user can access. A user can never read another's project — enforced at the SQL layer, not just the app layer.
3. Session revocation: disabling a user → their session token is invalidated within one event round-trip (Chunk 2 acceptance criterion).
### 4. Audit log (`server/src/db.ts` — extend Chunk 2)
1. `audit(id, at, user_id, action, detail JSON)` — log: user create/disable, role change, share grant/revoke, ownership transfer, project delete.
2. `GET /api/admin/activity` returns the tail; retention: 1 year (v1, no purge).
### 5. Admin UI — web (`apps/web/src/features/admin/AdminView.tsx`)
1. **Users tab**: table (name, email, role, status, lastActive); "Create user" modal; inline role toggle; disable/enable switch.
2. **Sharing tab** (per project): list shared users with permission dropdown; transfer ownership; revoke.
3. **Activity tab**: filterable audit log table.
4. **Stats tab**: instance-wide counters.
### 6. Admin UI — desktop parity
1. The desktop app renders the **same** `AdminView` (it's part of the shared React app). No desktop-specific admin code — parity is structural.
2. Desktop uses bearer token for auth (Chunk 2); web uses cookie. Both hit the same API.
### 7. Invite flow
1. `POST /api/admin/users` with `sendInvite: true` → generates an invite token, stores a pending invite, (v1) logs the invite link to the audit log; the invitee clicks → `/invite/:token` → sets password → becomes an author.
### 8. Tests
1. Unit: `canAccessProject` — owner can edit; shared-read can read not edit; unrelated user gets 403.
2. Integration: admin creates user → user logs in → sees only shared projects; admin disables user → user's next API call gets 401.
3. Integration: project isolation — user A's adapter never returns user B's project (contract suite extension).
4. E2E: admin creates a user, shares a project (edit) → user sees it; revoke → it disappears.
---
## Acceptance criteria (all must pass)
- [ ] Admin creates/disables/changes-role of users from the web app.
- [ ] Admin manages sharing (grant/revoke/transfer) from the web app.
- [ ] Admin sees activity log + instance stats.
- [ ] The **same** admin UI works identically in the desktop app.
- [ ] A shared-with-read user cannot edit; a shared-with-edit user can.
- [ ] Multi-tenant isolation: user A can never read user B's project (enforced at SQL layer).
- [ ] Disabling a user revokes their session within one event round-trip.
- [ ] Audit log records all admin actions.
- [ ] Contract suite still green on both adapters.
## Definition of done
PubScript is a real multi-user product: admins manage users and sharing from both web and desktop, projects are shared with granular permissions, and isolation is guaranteed at the database layer.
## Risks & fallbacks
- **Permission bugs → data leaks** → enforce isolation at the SQL layer (WHERE project_id in accessible); contract suite tests cross-user access.
- **Session revocation latency** → check token validity on every request (small cost) rather than caching; cap the auth lookup.
- **Invite email delivery** → v1 logs the invite link (no SMTP); document SMTP as a later addition.