diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 785c99f..124c84e 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,19 +1,19 @@ { "name": "proxy-api", - "version": "1.5.3", + "version": "1.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proxy-api", - "version": "1.5.3", + "version": "1.7.0", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-free": "^7.3.0", "@popperjs/core": "^2.11.8", "@simpleworkjs/app-stack": "^1.0.0", "@simpleworkjs/conf": "^1.2.0", - "@simpleworkjs/frontend": "^0.2.6", + "@simpleworkjs/frontend": "^0.2.7", "@simpleworkjs/ldap": "^1.0.0", "@simpleworkjs/oidc-client": "^1.0.0", "acme-client": "^5.4.0", @@ -310,9 +310,9 @@ } }, "node_modules/@simpleworkjs/frontend": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@simpleworkjs/frontend/-/frontend-0.2.6.tgz", - "integrity": "sha512-2uqvEjxyZ2LE+sfhP6rJcEMmqdViazJ3ZkitWJXInPMWF6DiEZuP5MYqBqJvfDko63CCHEt1/ChFQd7Ry85Pzg==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@simpleworkjs/frontend/-/frontend-0.2.7.tgz", + "integrity": "sha512-s5oBc9dKLjd1bVhOQWR6+97faqQsbVKi0QYn5sNqOP6pGkUYUg2mY88ruHHg4Fp710owrzO/F3of/7tteFiGCw==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/nodejs/package.json b/nodejs/package.json index 18b18a1..a860baa 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -23,7 +23,7 @@ "@popperjs/core": "^2.11.8", "@simpleworkjs/app-stack": "^1.0.0", "@simpleworkjs/conf": "^1.2.0", - "@simpleworkjs/frontend": "^0.2.6", + "@simpleworkjs/frontend": "^0.2.7", "@simpleworkjs/ldap": "^1.0.0", "@simpleworkjs/oidc-client": "^1.0.0", "acme-client": "^5.4.0", diff --git a/nodejs/public/css/styles.css b/nodejs/public/css/styles.css index a34ad78..7065eae 100755 --- a/nodejs/public/css/styles.css +++ b/nodejs/public/css/styles.css @@ -7,6 +7,12 @@ body { display: flex; flex-direction: column; min-height: 100vh; + /* Height of the fixed navbar (plus the update banner, while shown -- + see top.ejs's showUpdateBanner/dismissUpdateBanner). Lets an in-page + sticky element offset itself below both fixed elements via + `top: var(--sw-content-offset)` instead of colliding with them at the + viewport's true top:0. */ + --sw-content-offset: 4.5rem; } #spa-shell { diff --git a/nodejs/public/lib/js/app-base.js b/nodejs/public/lib/js/app-base.js index 3d4398d..88f4290 100644 --- a/nodejs/public/lib/js/app-base.js +++ b/nodejs/public/lib/js/app-base.js @@ -584,10 +584,31 @@ app.util = (function(app){ document.body.removeChild(element); } + // Scroll a just-added/-edited element into view and flash its + // background, so the user's eye lands on the row that changed instead of + // it silently appearing/updating somewhere off-screen. Takes a jQuery + // object or a raw DOM node (e.g. jq-repeat's `item.__jq_$el`). + function revealItem(el){ + var node = el && el.jquery ? el[0] : el; + if (!node) return; + if (typeof node.scrollIntoView === 'function') { + node.scrollIntoView({behavior: 'smooth', block: 'center'}); + } + var prevTransition = node.style.transition; + var prevBg = node.style.backgroundColor; + node.style.transition = 'background-color 1.5s ease'; + node.style.backgroundColor = 'var(--bs-success-bg-subtle, #d1e7dd)'; + setTimeout(function(){ + node.style.backgroundColor = prevBg; + setTimeout(function(){ node.style.transition = prevTransition; }, 1500); + }, 300); + } + return { downloadFile: downloadFile, getUrlParameter: getUrlParameter, escapeHtml: escapeHtml, + revealItem: revealItem, } })(app); diff --git a/nodejs/routes/user.js b/nodejs/routes/user.js index d89e4d1..526d943 100755 --- a/nodejs/routes/user.js +++ b/nodejs/routes/user.js @@ -81,11 +81,20 @@ router.put('/password', async function(req, res, next){ } }); -// Admin: reset another user's password. +// Admin: reset another user's password. Blocked for SSO/OIDC-provisioned +// accounts (backing === 'oidc') -- they authenticate through the IdP, not a +// local password, so resetting one here would be a no-op at best and a +// false sense of control at worst. Only applies to the redis user backend; +// LDAP/PAM-backed deployments have no per-record marker for this. router.put('/password/:username', authz.requireAdmin, async function(req, res, next){ try{ validatePassword(req.body.password); let user = await User.get(req.params.username); + if(user.backing === 'oidc'){ + let e = new Error('Cannot set a password for an SSO-authenticated user.'); + e.status = 403; + throw e; + } return res.json({results: await user.setPassword(req.body)}); }catch(error){ next(error); diff --git a/nodejs/views/dns.ejs b/nodejs/views/dns.ejs index 126462e..4cfeab1 100644 --- a/nodejs/views/dns.ejs +++ b/nodejs/views/dns.ejs @@ -113,6 +113,7 @@ }); +
| Type | -Subject | -Scope | -Domain | -Role | -Delete | - - - - -
|---|