74 lines
4.8 KiB
Markdown
74 lines
4.8 KiB
Markdown
# 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.
|