Commit Graph

5 Commits

Author SHA1 Message Date
wmantly 4879769cc7 Add standalone mode with @simpleworkjs/orm-backed user/host stores
- Add standalone.enabled config flag to switch between LDAP+SSO and
  ORM-backed backends without changing the production code path
- New ORM models: StandaloneUser (uid, passwordHash, sshPublicKeys,
  groups) and StandaloneHost (slug, displayName, kind, metadata)
- user_file.js and hosts_file.js implement the same interfaces as the
  LDAP client and accessibleHosts() respectively
- models/user_ldap.js and utils/access.js become conditional facades
  that delegate based on conf.standalone.enabled at require time
- Zero changes to ssh_server.js core logic, bridge.js, key_inject.js,
  tui_picker.js, or any other consumer
- Fix ssh_server.js: use ?? instead of || for listenPort (0 is falsy)
- Fix ssh_server.js: register session listeners before awaiting
  audit.create() so client exec/shell requests aren't rejected
- Patch StringField.toSequelize() and IntegerField.toSequelize() to
  pass through primaryKey (the ORM's UUIDField already does this)
- 47 tests pass (24 existing + 15 new unit + 3 existing integration
  + 5 new standalone integration)
- Defaults to SQLite; any Sequelize dialect works via conf.orm

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-26 21:34:53 -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 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>
2026-07-25 15:55:03 -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 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