Commit 5d1813

2026-03-17 06:07:11 Claude (MCP): [mcp] Phase 2: consolidate collaborators into Otterwiki User Management, remove dashboard tab
Design/Admin_Panel_Reenablement.md ..
@@ 15,7 15,7 @@
## Upstream bug found
- `handle_permissions_and_registration` in `otterwiki/preferences.py` is missing the `has_permission("ADMIN")` guard that every other admin handler has. A non-admin authenticated user can POST and overwrite READ_ACCESS/WRITE_ACCESS settings. Fix submitted as `fix/permissions-admin-guard` on our fork — PR pending to redimp. Red/green verified: POST returns 302 (succeeds) without fix, 403 with fix.
+ `handle_permissions_and_registration` in `otterwiki/preferences.py` is missing the `has_permission("ADMIN")` guard that every other admin handler has. Fix submitted as `fix/permissions-admin-guard` — PR pending to redimp. Red/green verified.
## Panel Status
@@ 23,61 23,56 @@
| Panel | Route | Notes |
|-------|-------|-------|
- | Application Preferences | `/-/admin` | SITE_NAME, SITE_DESCRIPTION, SITE_LOGO, etc. All stored in per-wiki preferences. `update_app_config()` reloads from the right DB. `app_renderer` sees changes via live reference to `app.config`. |
+ | Application Preferences | `/-/admin` | SITE_NAME, SITE_DESCRIPTION, SITE_LOGO, etc. SERVER_NAME hidden in PLATFORM_MODE. |
| Sidebar Preferences | `/-/admin/sidebar_preferences` | Pure preferences. Works. |
| Content and Editing | `/-/admin/content_and_editing` | COMMIT_MESSAGE, WIKILINK_STYLE, etc. Works. |
- | Repository Management | `/-/admin/repository_management` | GIT_WEB_SERVER, remote push/pull. Works but **git remote push/pull + SSH key storage is a security concern** — wiki owners storing arbitrary SSH keys that the platform executes. Consider gating push/pull behind a platform-level flag. |
+ | Repository Management | `/-/admin/repository_management` | GIT_WEB_SERVER, remote push/pull. **Git remote push/pull + SSH key storage is a security concern** — consider gating in PLATFORM_MODE. |
- **Flag:** `SERVER_NAME` in Application Preferences could break Flask's URL generation in multi-tenant mode. Should be hidden or read-only in `PLATFORM_MODE`.
+ ### Phase 1: Permissions panel ✅ DEPLOYED (2026-03-17)
- ### Phase 1: Permissions panel (in progress)
+ `READ_ACCESS` / `WRITE_ACCESS` / `ATTACHMENT_ACCESS` configurable via Otterwiki admin. Resolver enforces by intersecting with platform ACL. Registration fields hidden in PLATFORM_MODE. `READ_ACCESS` replaces `is_public` as sole source of truth for anonymous access.
- **Route:** `/-/admin/permissions_and_registration`
+ ### Phase 2: User Management (consolidated)
- **What it controls:**
- - `READ_ACCESS` — ANONYMOUS / REGISTERED / APPROVED
- - `WRITE_ACCESS` — same options
- - `ATTACHMENT_ACCESS` — same options
- - Registration settings (DISABLE_REGISTRATION, AUTO_APPROVAL, etc.) — **not applicable to ATProto auth**
+ **Key decision (2026-03-17): Remove the "Collaborators" tab from the platform dashboard entirely. All user/access management happens in Otterwiki's User Management panel.** This consolidates three layers (platform ACL, wiki preferences, per-wiki roster) into two places the wiki owner actually sees: the Permissions panel and the User Management panel.
- **Permission model (three layers):**
- 1. Platform ACL grants the **ceiling** (owner/editor/viewer → READ, WRITE, UPLOAD, ADMIN)
- 2. Per-wiki preferences (READ_ACCESS/WRITE_ACCESS/ATTACHMENT_ACCESS) restrict further
- 3. Per-user flags in per-wiki user table (Phase 2) restrict further still
+ **Routes:** `/-/admin/user_management`, `/-/user/<uid>`
- Wiki owners can restrict but never escalate beyond the platform ACL ceiling. ADMIN permission is never affected by wiki preferences.
+ #### Design: Explicit roster, not activity tracking
- **`READ_ACCESS` as source of truth for public access:** The wiki's `READ_ACCESS` preference replaces the separate `is_public` flag in robot.db. If `READ_ACCESS=ANONYMOUS`, anonymous users get through. Otherwise, authentication is required. One place to manage access, in Otterwiki's native admin UI.
+ The per-wiki `user` table is an **explicit roster managed by wiki admins**, not an activity log. Users only exist there because an admin added them.
- **APPROVED level:** Treated as REGISTERED until Phase 2 (user tracking). This is safe — more restrictive than intended, never less.
+ **Admin workflow:**
+ 1. Admin opens User Management panel in Otterwiki's admin
+ 2. Enters a DID handle (e.g., `@alice.bsky.social`) to add a user
+ 3. Sets per-user flags: role (viewer/editor/admin), `is_approved`
+ 4. User can now access the wiki at whatever level their flags grant
- **MCP bearer tokens:** Full access regardless of wiki preferences. MCP tokens are platform-granted; wiki owners shouldn't be able to break their own integrations.
+ **Wiki admins can create other wiki admins.**
- **Changes (two repos):**
- 1. **otterwiki fork (wikibot-io):** Remove `@platform_mode_disabled`, hide registration fields in template, server-side guard for registration saves
- 2. **robot.wtf resolver:** `_apply_wiki_access_restrictions()` intersects ACL permissions with wiki preferences before injecting proxy headers
+ #### Consolidating platform ACL into User Management
- ### Phase 2: User Management
+ Currently, the platform dashboard has a "Collaborators" tab that manages the `acls` table in robot.db (granting owner/editor/viewer roles). Phase 2 moves this into Otterwiki's User Management panel:
- **Routes:** `/-/admin/user_management`, `/-/user/<uid>`
+ - **Remove the Collaborators tab** from the dashboard (`wiki_settings.html`)
+ - **Remove the Collaborators API endpoints** from the management API (or deprecate)
+ - **User Management becomes the single place** to manage who can access the wiki and at what level
+ - **The per-wiki `user` table replaces the platform `acls` table** as the source of truth for per-wiki access
+ - **The resolver reads from the per-wiki user table** instead of (or in addition to) the platform ACL table
+ - **The platform ACL ceiling is enforced implicitly:** the wiki owner's own role (from the `wikis` table) is the ceiling. The owner can grant up to their own level but not beyond.
- #### Design: Explicit roster, not activity tracking
+ #### Migration path
- The per-wiki `user` table is an **explicit roster managed by wiki admins**, not an activity log. Users only exist there because an admin added them. No passive tracking — if a wiki is publicly readable, authenticated visitors are not recorded.
-
- **Admin workflow:**
- 1. Admin opens User Management panel
- 2. Enters a DID handle (e.g., `@alice.bsky.social`) to add a user
- 3. Sets per-user flags: `is_approved`, `allow_read`, `allow_write`, `allow_upload`, `is_admin`
- 4. User can now access the wiki at whatever level their flags grant (intersected with wiki preferences and platform ACL ceiling)
-
- **Wiki admins can create other wiki admins.** No reason to restrict this — the platform ACL is still the outer boundary.
+ 1. On Phase 2 deploy, migrate existing `acls` entries into per-wiki `user` tables
+ 2. Remove Collaborators tab from dashboard
+ 3. Remove or deprecate ACL management API endpoints
+ 4. Resolver reads permissions from per-wiki user table
#### `email` field decision — deferred
Otterwiki's User model keys on `email`. Two options:
- **Appropriate `email` for DID handles** — quick, minimal schema divergence, but blocks future email notifications
- - **Add a `handle` column** — cleaner, but more fork divergence and schema migration work
+ - **Add a `handle` column** — cleaner, but more fork divergence
Decision deferred. Future notification mechanism (email vs. BlueSky DMs) affects this choice.
@@ 87,21 82,30 @@
1. Wiki owner sets `READ_ACCESS=APPROVED` in Permissions panel
2. Resolver checks per-wiki `user` table for the authenticated user's handle
3. If found and `is_approved=True`: READ permission granted
- 4. If not found or not approved: READ stripped (along with WRITE, UPLOAD per dependency chain)
+ 4. If not found or not approved: READ stripped
#### Required changes
- 1. **User Management UI:** Admin enters DID handles to add users, sets flags. Hide password/email-editing fields in PLATFORM_MODE.
- 2. **`get_all_user()` override:** `ProxyHeaderAuth.get_all_user()` must query the per-wiki user table instead of returning `[current_user]`.
- 3. **Resolver permission check:** Extend `_apply_wiki_access_restrictions()` to query per-wiki user table when access level is APPROVED.
- 4. **User Edit page:** Same PLATFORM_MODE treatment — hide password actions, show handle.
+ **Otterwiki fork:**
+ 1. Re-enable User Management + User Edit panels in PLATFORM_MODE
+ 2. Customize add-user form: accept DID handle, hide password fields
+ 3. Override `ProxyHeaderAuth.get_all_user()` to query per-wiki user table
+ 4. Map user flags to roles (viewer/editor/admin) for display
+
+ **robot.wtf:**
+ 1. Remove Collaborators tab from `wiki_settings.html`
+ 2. Remove or deprecate ACL management endpoints in `management/routes.py`
+ 3. Extend `_apply_wiki_access_restrictions()` to query per-wiki user table for APPROVED level and per-user role
+ 4. Migration script: copy `acls` entries to per-wiki `user` tables
+ 5. Resolver reads per-wiki user table for permission computation
### Keep disabled: Mail Preferences
- Per-wiki SMTP config is a security/spam risk. If mail is needed, platform provides a shared service. Notification mechanism (email vs. BlueSky DMs) is an open question.
+ Per-wiki SMTP config is a security/spam risk. Notification mechanism (email vs. BlueSky DMs) is an open question.
## Implementation Order
- 1. **Permissions panel** — in progress (Phase 1)
- 2. **Hide SERVER_NAME** — quick template fix (opportunistic)
- 3. **User Management** — explicit roster + APPROVED level (Phase 2)
+ 1. ~~Permissions panel~~ ✅ Deployed
+ 2. ~~Hide SERVER_NAME~~ ✅ Deployed
+ 3. ~~Remove is_public toggle~~ ✅ Deployed
+ 4. **User Management** — consolidate collaborators + explicit roster + APPROVED level (Phase 2)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9