feat(web): add Hub-gated admin UI with allowlist and settings #14
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/admin-ui-hub-gated"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Add a minimal, server-rendered admin web UI (step 5 of the multi-user remote MCP server epic), gated on the signed-in user's is_admin flag from the step-3 Hub login session. It lets a global admin manage who can use an internet-facing deployment and tune operational settings, with no JavaScript.
New src/web/admin.rs hosts the admin surface and the browser login it depends on. require_admin resolves the signed-in user from the existing yt_mcp_session cookie and refuses any non-admin caller (unauthenticated, unknown, disabled, or merely non-admin) with a 403, so the admin routes are reachable only by an admin. GET /admin renders the dashboard (allowlist, users with their active-session counts, settings); POST /admin/allowlist/add and /admin/allowlist/remove manage allowlist entries; POST /admin/users/disable disables a user and revokes all their access and refresh tokens in one transaction; POST /admin/settings inserts or updates a setting. GET /login renders the sign-in page and GET /login/start begins the upstream Hub round-trip, stashing /admin as the post-login destination so the shared hub_callback resumes there.
New src/web/templates.rs plus src/web/templates/ (askama 0.12, configured via askama.toml to load from src/web/templates/) provide the login, consent, admin, and error pages over a shared base layout. Every interpolated value is HTML-escaped by default. render() turns any template into a text/html response and error_page() renders the standard error page at a given status.
src/web/oauth_as.rs gains a consent interstitial: an authenticated /authorize now renders the consent page (showing the requesting client and scope) until the user grants or denies, instead of issuing a code unconditionally; granting re-submits with consent=granted and issues the code, denying redirects back with error=access_denied. The post-login return_to is generalized to a full same-origin path (guarded by is_safe_local_path against open redirects) so both the OAuth flow (/authorize?...) and the admin login (/admin) resume through one hub_callback. The session-cookie name and the cookie / redirect helpers are made pub(crate) for reuse by the admin module.
src/db/mod.rs adds list_users, list_settings (plus a Setting row type), count_active_access_tokens, and disable_user_and_revoke_tokens (status -> disabled and all tokens revoked in a single transaction). The three oci-build Dockerfiles copy askama.toml alongside the sources so the compile-time template loader resolves in the release builds.
#YTMCP-7
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com