5fb1db4de4
All 11 chunks are implemented; update status docs to reflect the honest state rather than the stale 'Not started' labels. - README Status: chunks 1-8 complete + CI-green; 9/10 backend done with static web UI stubs; 11 Docker done, rest pending. - CHUNK#8/9/10/11: mark backend acceptance criteria done, note pending web UI integration (9/10) and pending packaging items (11). - Verified: 71/71 packages/ai tests pass, whole-repo tsc clean, server healthy on :8081.
5.0 KiB
5.0 KiB
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)
GET /api/admin/users— list all users (name, email, role, status, lastActive).POST /api/admin/users— create a user (email + role; server generates a one-time password or sends an invite link).PATCH /api/admin/users/:id— change role (admin/author) or disable/enable.GET /api/admin/activity— audit log tail (filtered by user/action/date).GET /api/admin/stats— instance-wide: user count, project count, storage used.- All routes behind
requireAdminmiddleware.
2. Sharing API (server/src/routes/projects.ts — extend Chunk 2)
POST /api/projects/:id/shares— invite by email, grantread/edit.PATCH /api/projects/:id/shares/:userId— change permission.DELETE /api/projects/:id/shares/:userId— revoke.POST /api/projects/:id/transfer— transfer ownership (owner only).GET /api/projects— return owned ∪ shared (withpermon each).
3. Access control (server/src/auth.ts — extend Chunk 2)
canAccessProject(projectId, perm)— enforces owner OR shared-with (correctperm). Middleware on every content route.- Multi-tenant isolation: every query filters by
project_idthe user can access. A user can never read another's project — enforced at the SQL layer, not just the app layer. - 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)
audit(id, at, user_id, action, detail JSON)— log: user create/disable, role change, share grant/revoke, ownership transfer, project delete.GET /api/admin/activityreturns the tail; retention: 1 year (v1, no purge).
5. Admin UI — web (apps/web/src/features/admin/AdminView.tsx)
- Users tab: table (name, email, role, status, lastActive); "Create user" modal; inline role toggle; disable/enable switch.
- Sharing tab (per project): list shared users with permission dropdown; transfer ownership; revoke.
- Activity tab: filterable audit log table.
- Stats tab: instance-wide counters.
6. Admin UI — desktop parity
- The desktop app renders the same
AdminView(it's part of the shared React app). No desktop-specific admin code — parity is structural. - Desktop uses bearer token for auth (Chunk 2); web uses cookie. Both hit the same API.
7. Invite flow
POST /api/admin/userswithsendInvite: 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
- Unit:
canAccessProject— owner can edit; shared-read can read not edit; unrelated user gets 403. - Integration: admin creates user → user logs in → sees only shared projects; admin disables user → user's next API call gets 401.
- Integration: project isolation — user A's adapter never returns user B's project (contract suite extension).
- 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.
- Admin UI (
apps/web/src/features/admin/AdminView.tsx): the Users, Sharing, Activity, and Stats tabs are wired to the backend. Currentlyapps/web/src/routes/Admin.tsxis a static stub awaiting this integration.
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.