Skip to content

v1.3.0 — unified front-end UI shell

Choose a tag to compare

@wmantly wmantly released this 26 Jul 04:31
· 108 commits to master since this release
9db5305

[1.3.0] - 2026-07-26

Changed

  • Unified the front-end UI shell across the three theta42 apps. views/top.ejs, views/bottom.ejs and public/lib/js/app-base.js are now byte-identical in sso-manager-node, proxy and jump-host, so the apps look and behave the same and a shell change lands in one edit per repo instead of three divergent ones. Everything that differs between the apps moved into a new nodejs/utils/ui.js, exposed to every render as ui via app.locals: nav items and the groups that may see them, footer repo/license/docs/Terms links, favicon, the profile and post-logout targets, and whether the update banner exists at all.
  • One nav-gating model everywhere. app-base.js reveals .group-required-<cn> elements for each group the current user is in, read from GET /api/user/me. sso-manager-node reports LDAP DNs in memberOf and the OIDC clients report CNs in groups; both normalise to CNs client-side, and the clients' effective-rights isAdmin flag is exposed as a synthetic admin group — so one gating model covers a group-based provider and boolean-admin clients without either app learning the other's response shape.
  • GET /api/user/me is fetched once per page load and cached (app.auth.loadUser). The nav, per-view forceLogin and every group-gated element read that one promise instead of issuing their own request.
  • app.auth.isLoggedIn is dual-mode: it returns a Promise and invokes an optional node-style callback, so the async and callback call styles both work against one shared top.ejs.
  • app.auth.forceLogin no longer uses $.holdReady (removed in jQuery 4). An unauthenticated user is redirected to /login?redirect=<path>; group requirements are still enforced, and logOut now only clears the session, leaving the destination to the caller (ui.logoutRedirect).
  • Dependency alignment across all three apps: jquery ^4.0.0 and ejs ^3.1.10.

Fixed

  • app.api.delete dropped its callback when called by formAJAX. formAJAX always passes the serialized form as the second argument, so a DELETE-method form's callback landed in the data slot and never ran. delete now accepts both (url, callback) and (url, data, callback).
  • app.api.post/put referenced an undefined callback2 and threw when handed a non-function callback. Both are now dual-mode Promise/callback.
  • The login page's "reveal the card once we know you're logged out" branch threw (Cannot read properties of null) whenever the logged-in check answered before the parser reached that element — which it always did without a stored token. It now runs on DOM ready.
  • logInRedirect on the legacy /login/<path> form kept only the path. The OIDC provider routes an unauthenticated authorization request through /login/oauth/authorize?client_id=…&state=…; dropping the query there loses the entire authorization request. The suffix form now preserves its query string.

Added

  • .group-required { display: none } in public/css/styles.css, the base rule the shared gating model reveals against.
  • #spa-shell dropped its inline margin-top; styles.css already sets it and the shared shell adjusts it when a banner is shown.

Verified

  • Browser-verified against a full theta-env stack (sso-manager + proxy + jump-host): every top-level page renders with a clean console; nav gating is correct for admin and non-admin; forceLogin's onboarding and group gates fire; val.js blocks a weak password and accepts a strong one through a real form submit; the DELETE-method forms work; and the OIDC login round trip (authorize with PKCE -> login -> consent -> callback -> token fragment) completes on both OIDC clients.