v0.165.0: Indítópult megosztása — guest launcher via capability URL (+ optional password, QR)
Mint a 160-bit capability URL (/s/<token>) serving a standalone read-only guest launcher: same tiles, opens apps in new tabs, no account, no admin session. Information only, zero control — every privilege stays behind each app's own auth. - /s/ pre-auth pass-through (after the claim gate) + session-CSRF exemption; guest password POST carries its own pre-auth HMAC CSRF. - Constant-time token match; empty stored token = disabled = byte-identical mux 404. - Optional per-share password: separate bcrypt hash + own attempt map; signed cookie = HMAC(token|passwordHash) keyed with web.session_secret, so rotate/change invalidates. - Guest labels ride the v0.164.0 ruling; never expose internal state vocabulary. - Token redacted in logs (/s/<redacted>); never in CHANGELOG/REPORT/CONTEXT. - Admin modal: copy-link, QR (go-qrcode), set/clear password, rotate, disable. - Tests: Groups A-G (14) + 3 red-proofs verified red.
This commit is contained in:
@@ -278,6 +278,44 @@ load the monogram shows through (the launcher does NOT use the app-placeholder h
|
||||
are `<a target="_blank" rel="noopener">` links; stopped/degraded apps render greyed + unclickable with
|
||||
the Hungarian state badge. Empty state links to `/stacks`.
|
||||
|
||||
#### Indítópult megosztása — guest launcher via capability URL (v0.165.0)
|
||||
|
||||
The admin launcher's **"Indítópult megosztása"** button mints a **capability URL** —
|
||||
`https://<host>/s/<token>`, where `token` is a 160-bit `crypto/rand` value
|
||||
(`newShareToken`, base64.RawURLEncoding, 27 chars) — that serves a **standalone, read-only guest
|
||||
launcher** with **no account and no admin session**. The link grants **information only, zero
|
||||
control**: app names + public URLs; every privilege stays behind each app's own auth and the
|
||||
controller admin password. The tile visual is shared with the admin launcher via the `launch_tile`
|
||||
template partial; the app slice comes from the extracted `Server.launcherApps()` helper.
|
||||
|
||||
- **Routing** (`internal/web/share.go`, `share_handlers.go`): `/s/<token>` joins the RequireAuth
|
||||
pre-auth allowlist **after** the claim-gate block (an unclaimed box never serves the guest page —
|
||||
the claim gate stays supreme) and is exempted from session CSRF (the guest password POST carries a
|
||||
pre-auth HMAC CSRF, `validShareCSRF`, mirroring the claim POST). Token match is
|
||||
`subtle.ConstantTimeCompare`; an empty stored token (= sharing OFF, there is no separate flag)
|
||||
matches nothing, so a wrong/disabled token returns a **byte-identical mux-default 404** (`share404`).
|
||||
Guest responses set `X-Robots-Tag: noindex, nofollow` / `Referrer-Policy: no-referrer` /
|
||||
`Cache-Control: no-store`. The token is a secret: the ServeHTTP debug line and the 404 WARN redact
|
||||
`/s/` paths to `/s/<redacted>`.
|
||||
- **Optional per-share password** (`settings.LauncherSharePasswordHash`): a SEPARATE bcrypt credential
|
||||
(never the admin `PasswordHash`), guarded by its OWN per-IP 5/1-min attempt map (`shareAttempts`,
|
||||
never the admin `loginAttempts`). A correct password mints a signed gate cookie =
|
||||
HMAC-SHA256(`token|passwordHash`) keyed with the persisted, box-scoped `web.session_secret` — so
|
||||
rotating the token OR changing the password invalidates every outstanding cookie with no bookkeeping.
|
||||
- **Guest state labels** ride the v0.164.0 ruling and never expose internal vocabulary: clickable ⇔
|
||||
`isOperationalState && !routeUnpublished` (operational AND route actually published, so a tap never
|
||||
dead-ends); `StateStopped` ⇒ "A tulajdonos leállította"; any other non-clickable state ⇒
|
||||
"Átmenetileg nem elérhető". Empty ⇒ "Jelenleg nincs elérhető alkalmazás." (`buildGuestApps` is the
|
||||
pure, tested mapping; templates `launcher_shared.html` + `launcher_share_password.html`).
|
||||
- **Admin modal** (in `launcher.html`): current link + copy button, QR code
|
||||
(`GET /launcher/share/qr.png`, ~256px PNG via `github.com/skip2/go-qrcode`, admin-authed, `no-store`),
|
||||
set/clear share password, "Új link készítése" (rotate), "Megosztás kikapcsolása" (clears token AND
|
||||
password). The management POSTs live under `/launcher/share/*` and ride the normal admin session +
|
||||
session CSRF; rotate/disable use the inline `data-confirm` (felhomConfirm) affordance.
|
||||
|
||||
Design ruling: member accounts are superseded by this capability-URL model; per-member tile
|
||||
visibility is parked under the SSO arc.
|
||||
|
||||
#### Dashboard "Megnyitás" Button
|
||||
|
||||
Running apps on the Vezérlőpult now show a "Megnyitás ↗" button that opens the app's subdomain in a new tab. The `Subdomains` map is built in `dashboardHandler` from `app.yaml` env or metadata fallback.
|
||||
|
||||
Reference in New Issue
Block a user