20 Commits

Author SHA1 Message Date
Dave Kempe e8d19e38dd Sessions page role scoping + count header (stage 3)
Polish for the Sessions page now that it's the admin/poweruser
management view (share has moved to Connections, shadow landed in
stage 2).

- Page heading becomes a .section-head strip with a right-aligned
  count span ("12 active · 3 yours" for admins seeing others,
  "3 active" when the caller only sees their own).
- Empty state is role-aware: admins get the plain "No active
  sessions", non-admins get a nudge toward Connections / the ad-hoc
  form.
- Owner column is dimmed for own sessions and accent-teal bold for
  others — lets admins eye-scan own vs others at a glance.
- Delete button is hidden on rows the caller can't delete (non-admin
  viewing someone else's session). Backend check is still the
  authoritative gate; this just tidies the UI.
2026-04-18 21:56:20 +10:00
Dave Kempe 635bc061cc Shadow sessions: admin read-only viewer tokens
Part of the shadow-sessions plan (stage 2). Admins can now join any
active session from the Sessions page without the user having to
share — the backend mints a short-lived token and every mint is
logged to token_audit_log.

Backend
- Session struct gets a shadow_tokens: Vec<ShadowToken> (sha256 hex
  of the raw token, issuing admin, expiry). Expired entries are
  pruned on mint.
- validate_share_token now accepts either the owner's share_token or
  any non-expired shadow token, so the existing viewer path works
  unchanged.
- POST /api/sessions/{id}/shadow (admin-only) mints a raw token,
  pushes its hash + a 10-minute expiry onto the session, writes a
  token_audit_log row (action = "shadow_session", details includes
  session_id, owner, expiry, caller IP) and returns the viewer URL.

Frontend (Sessions page)
- /api/me fetched on load so we know the caller's display_name and
  role before rendering. API-key users default to admin and fetch
  /api/me to learn their name.
- Own active sessions still render "open" (anchor to client_url).
- Others' active sessions render "shadow" as an anchor (not a button)
  so both action cells line up in the same column. Shadow uses
  --status-pending (warning yellow) instead of the neutral accent,
  hovers to --primary, and shows a "minting..." busy state while
  the POST is in flight.
2026-04-18 21:56:20 +10:00
Dave Kempe eb1521dd6c Share button moves to Connections Active Sessions
Part of the Sessions-page rework (shadow-sessions plan, stage 1).
The Sessions page is becoming an admin/poweruser management view;
user-initiated share lives with the user's own active sessions in
Connections.

Connections
- Each active session card gets a Share button in a new action row
  below the thumbnail/meta.
- Share opens a themed modal with the full share URL pre-selected
  and a Copy button (async clipboard with execCommand fallback).
- Clicking the card still reconnects — Share handler stops
  propagation so the two actions don't collide.
- Overlay click or Close dismisses the modal.

Sessions page
- Share column removed (was columns 10 of 10 — down to 9).
- Dead CSS (.btn-share, .share-url) and JS (expandedShares,
  shareFullUrl, rowId, the share/copy click handlers) removed.
- Ad-hoc jump-host styling (.btn-add-hop) stays.
2026-04-18 21:56:20 +10:00
Dave Kempe 0d69e8fed4 Rename Address Book → Connections; allowed_groups picker; session privacy (#102)
Three pieces of v1.6.0 work that happened together and are easier to
review as one save point.

Rename: Address Book → Connections
- static/addressbook.html renamed to static/connections.html
- Nav links, page titles, empty states, onboarding, and prose updated
  across all 8 static pages (connections, admin, docs, index,
  recordings, reports, sessions, tokens).
- README, CLAUDE.md, and every file under docs/ updated.
- src/main.rs: connections.html added to the branded-page map and
  route list; /addressbook.html returns a 308 permanent redirect so
  existing bookmarks keep working.
- Backend API paths, Rust types, and Vault storage paths are
  deliberately unchanged — internal only.

Folder allowed_groups picker
- New SQLite table `seen_groups` tracks OIDC groups observed in any
  user login; OIDC callback upserts after extracting groups.
- `GET /api/auth/known-groups` (admin-only) returns the union of
  group_role_mappings and seen_groups.
- `GET /api/addressbook/folders/{scope}/{folder}/config` adds the
  missing endpoint the frontend was already calling — existing
  allowed_groups now prefill the edit-folder modal.
- Folder modal swaps the free-text comma-separated input for a chip
  picker with a themed combobox dropdown: autocomplete over known
  groups, keyboard nav, "+ add custom" row for unlisted groups.

Active session visibility (GitHub #102)
- `GET /api/sessions` scopes to the caller's own sessions by default;
  `?all=true` lets admins opt in (used by the Sessions page).
- `GET /api/sessions/{id}` and the thumbnail GET/PUT endpoints are
  now owner-or-admin, returning 404 for other callers so session
  existence isn't leaked.
- Connections' Active Sessions strip is now always owner-scoped —
  admins still manage everyone via the Sessions page.
2026-04-18 21:56:20 +10:00
Dave Kempe 88b754a11c Address book: subfolder tree UI + shared design system
Frontend for #101 — subfolder support (backend landed in c2a3822) — plus
a site-wide visual overhaul extracted into a single shared stylesheet.

Address book
- Folder sidebar renders as a tree with lazy-loaded children via the
  /api/addressbook/folders/{scope}/{path}/subfolders endpoint.
- Scope badge is now an icon with hover tooltip: ⊕ shared, ▣ instance.
- New "+ subfolder" button creates a nested folder under the selection.
- Move-entry dropdown includes any loaded subfolders.
- Batch folder API now returns path + has_children so the tree can
  render chevrons without a second request per folder.

Design system (rustguac.css)
- Extracted ~700 lines of near-duplicate CSS from each page into a
  shared stylesheet linked by every page.
- 18px body, strict 38/44/54px control heights, uppercase letter-spaced
  section labels, zebra table rows, active-nav underline bar.
- Uniform button ladder: primary (red) / accent (connect, teal) /
  ghost (+ buttons) / small (edit/delete chrome).
- Generic status colors, type badges, pagination, token-reveal,
  summary cards, hop cards, and flow diagram now live in one place.

Per-page updates
- addressbook, admin, docs, index, recordings, reports, sessions,
  tokens: style blocks reduced to page-specific layouts only.
- reports/recordings/sessions: bare <strong> page titles promoted
  to <h2> for proper heading hierarchy.
- Stripped inline padding/font-size attributes that were overriding
  the shared ladder.
2026-04-18 21:56:20 +10:00
Dave Kempe 77c7c535f4 Docs cleanup, default theme to aurora, Vault install guidance
- Switch default theme from dark to aurora across server config,
  all 9 static HTML pages, example config, and docs
- Fix theme docs: list all 8 presets (was 6, missing jaguar/aurora),
  add missing type_vdi_bg/type_vdi_fg fields
- Add Vault/address book setup as recommended post-install step in
  installation docs — the address book is the primary user-facing
  feature and requires Vault
- Renumber subsequent install steps
2026-04-16 16:54:25 +10:00
Dave Kempe 622569290a Security hardening: share URL redaction, auth rate limiting, fixes
- Redact share_url from session listings for non-owners — previously
  any authenticated user could enumerate share tokens and join sessions
  they didn't create (share_url now only returned to session creator
  and admins)
- Always rate-limit OIDC login/callback (1/sec burst 5 per IP)
  regardless of rate_limit config, preventing brute-force on auth
- Fix disconnect instruction detection: use instruction boundary
  matching instead of substring contains — clipboard content or typed
  text containing "10.disconnect;" could falsely trigger VDI container
  destruction
- Restrict sudoers chown rule to rustguac:rustguac only, preventing
  arbitrary ownership changes on the LUKS mount point
2026-04-11 21:49:03 +10:00
Dave Kempe fb0f965bb2 Fix nav: Logout + Settings float right together on all pages
Logout link moved inside the float-right user-menu-wrapper so both
Logout and Settings appear on the far right of the nav bar. Fixed
addressbook.html Settings font size to match other pages (1.3em).
2026-04-05 10:20:10 +10:00
Dave Kempe c48be9ed3f Move Logout button out of Settings dropdown on all pages
Logout is now a standalone nav link next to Settings (Logout Settings
order, right-aligned). Applied consistently across all 7 HTML pages.
Removed Logout from the settings dropdown menu.
2026-04-05 10:01:35 +10:00
Dave Kempe 8567d93c4f Sessions page: collapsible ad-hoc form, VDI option
- New Session form collapsed by default (click to expand)
- Added VDI (Docker) to session type dropdown with container image field
- Jump hosts hidden for VDI sessions (local containers)
2026-04-05 07:59:38 +10:00
Dave Kempe 501bd14a0d VDI Docker driver: ephemeral container desktops
Add 5th session type (Vdi) that spawns Docker containers with xrdp
and connects via RDP through guacd. Containers persist after disconnect
for configurable idle timeout, allowing reconnection to same desktop.

- VdiDriver trait (dyn-compatible) for downstream extensibility
- DockerDriver: bollard API, deterministic container naming per user,
  start/reuse/stop lifecycle, TCP+settle readiness polling
- Background reaper removes idle containers after timeout
- Address book: container_image, cpu/mem limits, env vars fields
- Config: [vdi] section with enabled, docker_socket, limits, timeouts
- Test image: contrib/vdi-test-image (Debian trixie + xrdp + xfce4)
2026-04-05 06:28:03 +10:00
Dave Kempe 7acbb302fa v0.8.5: Reports, session history, enriched recordings
Session history:
- New session_history SQLite table tracks all sessions with user,
  entry, folder, hostname, duration, and recording file
- Automatic cleanup via session_history_retention_days config (default 90)
- 8 new unit tests for session history DB functions

Reports page (poweruser+ only):
- Summary cards: total sessions, hours, unique users, active now
- Sortable/filterable session history table with pagination
- Top Connections and Top Users leaderboards
- CSV export for session history with filters

Enriched recordings:
- Recording .meta sidecars now include user, folder, entry name, session type
- Recordings API returns enriched metadata from .meta files
- Recordings page shows User, Entry, Folder columns

Other:
- Reports nav link on all pages, hidden for operator/viewer roles
- Reports documentation in docs/reports.md
- Onboarding wizard for new users (role-scoped, dismissable)
- Settings menu label visibility improvement
- Fixed stale credential variable test (hyphen support)
2026-03-22 20:36:26 +11:00
Dave Kempe 6bafc79d60 v0.8.0: Credential variables, bug fixes
Credential variables — address book entries reference $domain_username /
$domain_password instead of storing static credentials. Users fill in their
own values via My Credentials (gear menu), stored per-user in Vault KV.
All variables set → silent launch; missing → prompted. Hyphens allowed in
variable names. Docs section added.

Bug fixes:
- Rate limiting disabled by default; opt-in via rate_limit = true (#62)
- Docker: copy FreeRDP guac-common-svc plugins to runtime image (#64)
- Docker/install: add chromium-sandbox package for non-root web sessions (#61)
- Logo: skip redundant JS src= when server-side branding already set (#65)
- Sessions page: hide Open/Share buttons for non-active sessions (#63)
- Drive: expose drive_configured in /api/auth/status, warn in UI when
  [drive] not configured
- install.sh: verify FreeRDP plugin installation

UI polish:
- Nav bar: border separator + spacing between header and nav on all pages
- Address book: password show/hide toggle on all password fields
- Drive diagnostic logging (session.rs, websocket.rs, client.html)

Closes #61, #62, #63, #64, #65
2026-03-13 13:47:11 +11:00
Dave Kempe 2bf34440e6 v0.5.1: RDP resize fix, new themes, Docker config persistence
- Fix RDP display resize for FreeRDP 3.x (patch 004: config.h struct layout)
- Add aurora theme (midnight blue with ambient glow gradients)
- Add jaguar theme (racing green & gold with subtle gradients)
- Add bg_pattern support for CSS gradient backgrounds in themes
- Fix Docker config.toml persistence across rebuilds (#38)
- Add Docker Compose volume mount documentation
- Increase API rate limit to 5/s burst 30 (fix spurious 429s)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:08:14 +11:00
Dave Kempe ea72c52a31 v0.5.0: Theme system, ARM64 builds, Docker fixes, dependency updates
Theme system:
- 6 built-in presets (dark, light, high-contrast, terminal, nord, corporate)
- Admin configures preset + per-color overrides in [theme] config section
- Client-side theme switching via localStorage (flash-free)
- All static pages updated with 28 CSS custom properties

Proxy telemetry:
- Track which side terminated connection (guacd/browser/cancelled)
- Timing-based log levels (guacd close <5s = warning)
- Clamp session dimensions to safe ranges (width 640-8192, height 480-8192, DPI 16-384)

Docker fixes (#37):
- Fix port mismatch: Dockerfile now uses 8089 consistently
- Auto-generate admin API key on first run
- Add API key setup docs and recordings volume to compose example

ARM64 support:
- Multi-platform Docker builds (linux/amd64 + linux/arm64)
- Native ARM64 .deb and tarball builds via ubuntu-24.04-arm runner

Dependency updates:
- rustls 0.23.37, chrono 0.4.44, clap 4.5.60, toml 1.0.3
- futures-util 0.3.32, uuid 1.21.0, pulldown-cmark 0.13.1
- actions/upload-artifact v7, actions/download-artifact v8

Also: FreeRDP 3.x NULL deref patch (003), .gitignore for .playwright-mcp/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 15:05:41 +11:00
Dave Kempe 066a063304 Improve RDP display sizing: match browser dimensions on connect and resize
- Send actual browser window dimensions at session creation (addressbook,
  sessions) instead of defaulting to 1920x1080
- Send initial sendSize() on WebSocket connect so guacd can resize the
  remote display via Display Update channel
- Debounce window resize handler (250ms) to avoid overwhelming RDP server

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 10:55:40 +11:00
Dave Kempe 3cf21933ea Add credential prompting for /api/connect deep-links, fix NetBox webhook docs
When an address book entry has prompt_credentials: true or no stored
credentials, /api/connect now returns an inline credential form instead
of failing or connecting without auth. The form POSTs to the existing
connect endpoint and redirects to the client page.

Fix NetBox webhook body template docs: use "type" not "session_type"
(matches Vault storage format), replace regex_replace/cut filters with
standard Jinja2 equivalents (lower, split) since NetBox's Jinja2
environment doesn't include Ansible or Django template filters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 19:14:53 +11:00
Dave Kempe 76c93bc502 Add multi-hop SSH tunnels, VNC sessions, and web session tunnel support
Multi-hop SSH tunnel chains allow routing any session type through
multiple bastion hosts. VNC is now a first-class session type.
Web browser sessions can tunnel through jump hosts with automatic
URL rewriting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 08:22:34 +11:00
Dave Kempe a94b743d6c Add user API tokens with role-based access and audit logging
User API tokens allow OIDC users to authenticate via bearer token for
automation and scripting. Powerusers and admins can create their own
tokens; admins can create tokens for operators. Tokens use SHA-256
hashing, optional max_role caps, optional expiry, and full audit
logging of create/revoke operations with client IPs.

- DB schema: user_api_tokens and token_audit_log tables
- Auth middleware: validates user tokens as fallback after admin keys
- API: 7 new endpoints (self-service + admin token management)
- UI: tokens.html (self-service) + admin.html token/audit sections
- Nav: Tokens link added to all pages (visible for operator+)
- Docs: API reference, security model, roles/access control updated
- Background cleanup: expired tokens + 90-day audit log retention

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 15:02:54 +11:00
Dave Kempe 67101e27ce Initial public release
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:38:53 +11:00