From 285cc4fbef67d918121e2952a76ed5a3757fa223 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Sun, 26 Jul 2026 00:32:37 -0400 Subject: [PATCH] Release 1.5.0: bump submodules to the unified-UI-shell tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sso-manager-node v1.5.0, proxy v1.4.0, jump-host v1.3.0 — the three apps now share one byte-identical front-end shell (top.ejs, bottom.ejs, app-base.js) with per-app values in each repo's utils/ui.js, one nav-gating model driven by /api/user/me, and jQuery 4 / EJS 3 across the board. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 100 +++++++++++++++++++++++++++++++++++++++++++++++ jump-host | 2 +- proxy | 2 +- sso-manager-node | 2 +- 4 files changed, 103 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b9b9db..bcd46d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,106 @@ for what changed inside the apps it composes. ## [Unreleased] +## [1.5.0] - 2026-07-26 + +### Bumped +- sso-manager-node -> [v1.5.0](https://github.com/theta42/sso-manager-node/releases/tag/v1.5.0) +- proxy -> [v1.4.0](https://github.com/theta42/proxy/releases/tag/v1.4.0) +- jump-host -> [v1.3.0](https://github.com/theta42/jump-host/releases/tag/v1.3.0) + +This release finishes the UI half of the unification that 1.4.0 deferred: the +three apps now share one front-end shell. `views/top.ejs`, `views/bottom.ejs` +and `public/lib/js/app-base.js` are byte-identical across sso-manager-node, +proxy and jump-host, and everything per-app moved into each repo's new +`nodejs/utils/ui.js` (nav items and the groups that may see them, footer links, +favicon, profile/logout targets, update-banner on/off). Nav gating is one model +everywhere — the shell reveals `.group-required-` from `GET /api/user/me`, +normalising sso's LDAP DNs and the OIDC clients' group CNs to the same shape, +with the clients' `isAdmin` flag exposed as a synthetic `admin` group. jQuery is +4.0.0 and EJS 3.1.10 in all three. + +Five client-side bugs were fixed along the way, including two that broke real +flows: `app.api.delete` ignored the callback that `formAJAX` passes (so +DELETE-method forms — the proxy's host and DNS delete buttons — never refreshed), +and the login page threw on every logged-out visit while revealing its card. + +No `setup.sh`, compose or config change: this is app-internal UI work. Verified +by driving a full stack of all three apps in a browser — every page renders +console-clean, nav gating is correct per role, and the OIDC login round trip +completes on both OIDC clients. + +sso-manager-node 1.5.0: + +### 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-` 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=`; 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/` 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. + +### Fixed (sso-manager-node) +- `public/lib/js/val.js` shadowed `message` with `let` inside `validateField`, so a custom rule's return value never reached `validateMessage` and the caller always saw the generic length message. Resolved by adopting the shared validator, which also brings the `target`/`hostname` rules and the real password policy (>= 8 chars, and either 12+ or 3 of 4 character classes) to this app. +- `public/js/app.js` used `$.isFunction`, removed in jQuery 4. + +### Added (sso-manager-node) +- `GET /api/user/me` now also reports `isAdmin` (membership in `app_sso_admin`), the single effective-rights flag the shared UI shell gates the update banner on. Group-level gating still reads `memberOf`. + +### 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. + +proxy 1.4.0: + +### 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-` 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=`; 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/` 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. +- Admin-only nav items lost their inline `display: none` in favour of that class, and the brand link points at `/` instead of `#`. + +### 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. + +jump-host 1.3.0: + +### 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-` 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=`; 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/` 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. + ## [1.4.0] - 2026-07-25 ### Bumped diff --git a/jump-host b/jump-host index 67e2fc5..9db5305 160000 --- a/jump-host +++ b/jump-host @@ -1 +1 @@ -Subproject commit 67e2fc54c27abc31efb7eda53911b047ba5eaeb2 +Subproject commit 9db530565dfdf23b6f276c0bf27999519091dfcb diff --git a/proxy b/proxy index f4efdfb..75045b4 160000 --- a/proxy +++ b/proxy @@ -1 +1 @@ -Subproject commit f4efdfb9577a5f7c1c34f8a1c7c25d36b41f11a5 +Subproject commit 75045b4b335a0a8619ad9d2061f5a13ea8765e1d diff --git a/sso-manager-node b/sso-manager-node index fc0d910..59ae308 160000 --- a/sso-manager-node +++ b/sso-manager-node @@ -1 +1 @@ -Subproject commit fc0d9104d0c4803c7d710cc459c33430c85dec84 +Subproject commit 59ae30897ba5bfed9b6cb643ff32ec1e21a67e58