Commit Graph

19 Commits

Author SHA1 Message Date
wmantly da274a3ced Merge pull request #6 from theta42/docs/screenshots-refresh
docs: add jump-host screenshots
2026-07-26 16:31:53 -04:00
wmantly b9be0fe4e1 docs: add jump-host screenshots (login, dashboard, sessions, audit)
jump-host's docs had no screenshots at all. Add the four core web UI
views and reference them from index.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 16:29:10 -04:00
wmantly 9db530565d Merge pull request #5 from theta42/feature/ui-unification
Release 1.3.0: unified front-end UI shell
v1.3.0
2026-07-26 00:30:12 -04:00
wmantly fe6306b7d3 Release 1.3.0: unified front-end UI shell across the theta42 apps
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 00:21:51 -04:00
wmantly 240563e093 logInRedirect: keep the query string on the legacy /login/<path> form
The OIDC provider sends an unauthenticated authorize request through
/login/oauth/authorize?client_id=…&state=…; dropping the query there
loses the whole authorization request. The ?redirect= form is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 23:55:08 -04:00
wmantly 047e54ce50 app.api.delete: accept the (url, data, callback) form formAJAX uses; defer the login-card reveal to DOM ready
formAJAX always passes the serialized form as the second argument, so a
DELETE-method form (the host/DNS delete buttons) landed its callback in
the data slot and never ran.

The login page's "reveal the card once we know you're logged out" branch
touched an element further down the same page, which threw when
isLoggedIn answered before the parser got there (it always did without a
stored token). It now runs on DOM ready.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 23:15:07 -04:00
wmantly ee76088f86 Unify the front-end UI shell across the theta42 apps
views/top.ejs, views/bottom.ejs and public/lib/js/app-base.js are now
byte-identical across sso-manager-node, proxy and jump-host. Everything
per-app moved into utils/ui.js, exposed to every render as `ui` via
app.locals (nav items + their group gates, footer repo/docs/ToS links,
favicon, profile/logout targets, update-banner on/off + label).

Client framework changes:
- One gating model everywhere: app-base.js reveals .group-required-<cn>
  for each of the current user user/me groups. sso-manager-node sends LDAP
  DNs in memberOf, the OIDC clients send CNs in groups; both normalise to
  CNs, and the clients isAdmin flag becomes a synthetic `admin` group, so
  proxy nav-admin items are now group-required-admin.
- user/me is fetched once per page load and cached (app.auth.loadUser);
  nav, forceLogin and group-required elements all read that one promise.
- isLoggedIn is dual-mode (Promise + node-style callback), so the async
  and callback call styles both work from one shared top.ejs.
- forceLogin no longer uses $.holdReady (removed in jQuery 4): it redirects
  to /login?redirect=<path>, and still enforces required groups.
- logOut only clears the session; the caller decides where to go next.
- post/put/delete are dual-mode Promise/callback, which also removes the
  undefined `callback2` reference that threw on a non-function callback.

Dependencies: jquery ^4.0.0 and ejs ^3.1.10 in all three apps.

jump-host specifics:
- .group-required base rule added to styles.css (no gated nav items yet).
- #spa-shell drops its inline margin-top; styles.css already sets it, and
  the shared shell adjusts it when a banner is shown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 22:57:34 -04:00
wmantly 8db46bd9d9 Merge pull request #4 from theta42/release/v1.2.0
Release 1.2.0
2026-07-25 16:38:50 -04:00
wmantly 67e2fc54c2 Release 1.2.0: adopt shared @simpleworkjs/* packages; fix directory envelope drift
Rewire onto the shared @simpleworkjs/oidc-client, /directory-schema, /ldap, and
/app-stack packages (deleting the byte-identical local forks). utils/access.js
now fetches reachable hosts through the shared directory client, which
validates the {results} envelope and treats envelope drift as a failed group
rather than silently returning []. models/user_ldap.js is a thin wrapper over
createLdapClient (loose TLS default preserved). build_info moves to utils/ with
the shared {buildVersion,buildHash,buildYear} shape. Align ldapts ^8.1.8 and
redis ^6.1.0. Lockfile regenerated from the registry (no file:/link:).

Co-Authored-By: Claude <noreply@anthropic.com>
v1.2.0
2026-07-25 15:55:03 -04:00
wmantly 23d99980ce Merge pull request #3 from theta42/feature/web-ui-rework
Rebuild web UI on the shared theta42 stack; OIDC + local-admin auth
v1.1.0
2026-07-23 20:58:54 -04:00
wmantly aa3b3ed515 feat: rebuild web UI on the shared theta42 app stack; OIDC + local admin auth
The web management UI was a bespoke minimal theme with LDAP-bind login.
Rebuild it to match the SSO Manager and Proxy — same stack, same look/feel,
same auth model. The SSH bridge, audit, metrics, and access logic are
unchanged; this is purely the web layer.

Frontend (mirrors proxy/sso):
- Express + EJS with the shared top.ejs/bottom.ejs shell, Bootstrap 5,
  jQuery, jq-repeat, FontAwesome, Socket.IO, and the shared app-base.js /
  val.js client framework (copied verbatim). Vendor libs served from
  node_modules via /static-modules; app assets via /static.
- Dashboard / Sessions / Audit pages render in the common look/feel,
  loading data through the authenticated /api/* endpoints.

Auth (mirrors proxy):
- OIDC against the SSO (utils/oidc.js + routes/auth.js + models/oidc_state)
  plus a local anti-lockout admin (models/user_redis.js, bootstrapped from
  auth.adminUsers[0] / auth.localAdminPass). AuthToken sessions carry the
  group snapshot; middleware gates the data API on adminGroups or the local
  admin. New config: oidc{} + auth.adminUsers/localAdminPass.
- /api/user/me drives the client login state; "Log in with SSO" hidden when
  oidc.enabled is false.

Verified end to end: local admin login -> token -> /api/user/me isAdmin,
metrics/sessions/audit 200 with token / 401 without / 401 bad password;
static + page shells serve; 26 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 20:57:34 -04:00
wmantly eb8e5b409e Release 1.0.1 — CI portability (test glob + redis service)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.0.1
2026-07-23 16:26:50 -04:00
wmantly eb2354a7ab Merge pull request #2 from theta42/docs/pages
docs: GitHub Pages site
2026-07-23 16:23:31 -04:00
wmantly 3f8f56ee35 ci: add redis service (audit/metrics/session models need it on import)
model-redis opens a client when models/index is required, so with no
redis the integration test saw ECONNREFUSED as a post-test unhandled
rejection. Provide redis:7 at 127.0.0.1:6379 in CI (mirrors how
sso-manager-node tests run).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 16:22:26 -04:00
wmantly 9ee5b4bbe0 fix: use shell-expanded globs in test scripts (CI portability)
node's own '**' glob expansion for --test worked on local node 22.23 but
not in CI (node 20/22 runners) — 'Could not find test/**/*.test.js'. Use
plain single-star globs the shell expands instead; test files are exactly
one level under test/unit and test/integration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 16:20:58 -04:00
wmantly 5968722dc0 docs: GitHub Pages site (index, connecting, architecture, installation)
Mirrors the proxy/sso-manager docs-site layout (Jekyll + Bootstrap
default layout, theta42 assets). Documents the username grammar, the TUI
picker, SFTP/WinSCP, how auth/access/key-injection/bridging work, and the
three install paths + the required sshPublicKey write-ACL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 16:15:47 -04:00
wmantly 6e6f42e891 test: loosen integration timeouts for CI robustness
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.0.0
2026-07-23 15:50:35 -04:00
wmantly 34b3f9f22a ci: PR test workflow + lockfile; require node >=20.14
node --test --test-force-exit needs Node 20.14+/22, so the matrix is
[20.x, 22.x] and engines is bumped to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:49:59 -04:00
wmantly 36e9d5b0b3 feat: initial jump-host — SSH jump host for the theta42 stack
An SSH jump host that authenticates users against the shared LDAP
directory, authorizes them from the SSO Manager's inventory graph, and
bridges them to downstream hosts — auditing everything.

- Username-grammar routing (uid_-_target@jump) + interactive TUI picker
- Inbound LDAP auth (publickey / password with off|local|all policy)
- Directory-driven access (LDAP groups x /api/discovery/resources?group=)
- Per-user key injection into sshPublicKey, connects downstream as the user
- Shell / exec / SFTP-subsystem bridging (WinSCP works)
- Web UI + HTTP API (:3002) for audit + metrics; LDAP-admin gated
- Packaged like proxy: ops/install.sh + systemd, all-in-one Docker, compose
- Tests: 23 unit + 3 integration (node --test), all green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:48:49 -04:00