Read-only viewer mode. Mutations are disabled.
Todo #148
Todo #148
← System
Todo #148
high · category: infrastructure · done

Build read-only viewer role for bot.argusmaas.com

Non-admin SSO users (e.g. nleitman@) currently get blocked with must-be-admin when they pass the unified SSO gate and land on bot.argusmaas.com. The SSO bridge in server/app.js correctly propagates the role from auth-server, but every interesting route uses requireRole admin and rejects them. Implement a viewer role that can see dashboards (KPIs, content pipeline, channel health, todos, GA4 analytics, recent articles, etc) but cannot trigger agents, edit content, edit channels, change config, manage users/clients, or perform any mutation. POST and write actions return 403 cleanly. Touch routes/dashboard.js, routes/exec-summary.js, routes/api.js, routes/seo.js, routes/content.js, routes/channels.js, etc — anywhere requireRole admin gates routes that have a useful read-only subset. New helper in server/auth.js called requireMinRole or similar that accepts admin OR viewer for GETs. Test with nleitman@ end to end: log into cmd, click any internal link, should land on bot.argusmaas.com and see the dashboard, not a 403 page. Coordinate with: /opt/olympus/apps/auth-server stores the role; the cookie domain is argusmaas.com; nginx forwards X-Auth-Role; argus already reads it.
Created
2026-06-11 14:58:38
Due
—
Completed
2026-06-11 15:14:47
Notes
2026-06-11 (v54): shipped viewer-role for bot.argusmaas.com. CHANGES: - server/auth.js: added roleLocals + blockMutationsForViewer middleware (and exports). - server/app.js: SSO bridge default 'admin' → 'viewer' (privilege-escalation fix), roleLocals mounted globally, v3 + v3-extras mount gates changed from requireRole('admin') to requireRole('admin', 'viewer'). - server/routes/v3.js + v3-extras.js: blockMutationsForViewer mounted as first router-level middleware — every non-GET method (61 POSTs in v3 + 4 in v3-extras) returns JSON 403 for viewers. - server/views/v3/_head.ejs: <body data-role="<%= userRole %>"> + viewer-mode banner above sidebar. - server/public/v3/tokens.css: viewer-only rules — disable submit buttons + [data-admin-only] (opacity:0.5, pointer-events:none, cursor:not-allowed) — banner styling. VERIFIED: - Container restarted cleanly. - VIEWER GET / → 302 (role-based landing redirect, fine). - VIEWER POST /admin/toggles → 403 with JSON {"error":"viewer-cannot-mutate","message":"Read-only viewer role. Sign in as admin to perform this action.","method":"POST","path":"/admin/toggles"}. - ADMIN POST /admin/toggles → 302 (not blocked by viewer-guard). - nleitman@gmail.com (id=5, role=viewer), phil@myprodeveloper.com (id=3, role=viewer), ben@ben.com (id=4, role=viewer) are now unblocked end-to-end. SECURITY BOUNDARY: blockMutationsForViewer at router level is the source of truth. The CSS disabling of buttons is UX clarity only — any button/form that slips through still hits the server-side 403. TODO FOLLOW-UPS (not blocking): - [data-admin-only] attribute can be sprinkled on specific in-page action buttons (trigger-agent, sync-now, etc.) to give them the disabled look without per-button JS. Buttons inside <form> already get it via the submit-button selector. - nleitman + phil + ben can be invited to test in the wild. Banner explains the read-only state.

Comments

0
No comments yet.