From be666f5b2f4d5524d6955588e32741e94a766ae9 Mon Sep 17 00:00:00 2001 From: William Mantly Date: Thu, 30 Jul 2026 11:57:44 -0400 Subject: [PATCH] Recognize app_super_admin; add-user/add-permission as modal buttons; persist LE key - app_super_admin is a new cross-app LDAP group (also recognized by sso-manager-node and jump-host): added to conf.auth.adminGroups so members are always global admins here, same as the existing anti-lockout adminUsers/adminGroups mechanism. - Users and Permissions pages: the always-visible sidebar "Add" forms are now an "Add User"/"Add Permission" button in the list header that opens an app.modal dialog, matching the hosts.ejs convention. - The Let's Encrypt ACME account key now defaults to the already-persisted /data volume (models/host.js) instead of a CWD-relative path (./le_key.cert -> /app/le_key.cert in the container), which was lost on every image rebuild. Falls back to the old relative path when /data isn't present (e.g. local dev outside docker). --- nodejs/conf/base.js | 3 +- nodejs/models/host.js | 13 +++++ nodejs/views/permissions.ejs | 110 +++++++++++++++++------------------ nodejs/views/users.ejs | 76 ++++++++++-------------- 4 files changed, 98 insertions(+), 104 deletions(-) diff --git a/nodejs/conf/base.js b/nodejs/conf/base.js index cd92e08..bc0b0a9 100644 --- a/nodejs/conf/base.js +++ b/nodejs/conf/base.js @@ -49,7 +49,8 @@ module.exports = { // Per-user overrides are Grant records managed in the app. auth: { // Members of these SSO/LDAP groups are always global admins. - adminGroups: [], + // app_super_admin is the cross-app super admin group (sso, proxy, jump-host). + adminGroups: ['app_super_admin'], // Optional default role mapping for groups, e.g. // { 'dns-team': { role: 'manager', scope: 'domain', domain: 'foo.com' } } // { 'proxy-viewers': { role: 'viewer', scope: 'global' } } diff --git a/nodejs/models/host.js b/nodejs/models/host.js index fd88a5f..69826c3 100755 --- a/nodejs/models/host.js +++ b/nodejs/models/host.js @@ -9,10 +9,23 @@ const tldExtract = require('tld-extract').parse_host; const LetsEncrypt = require('../utils/letsencrypt'); const conf = require('@simpleworkjs/conf'); +const fs = require('fs'); +const path = require('path'); + +// Defaults to the same persisted volume Redis uses (/data, see +// docker-entrypoint.sh's REDIS_DATA_DIR) instead of the old CWD-relative +// default (./le_key.cert -> /app/le_key.cert), which lives in the +// container's writable layer and was lost on every rebuild. Falls back to +// the old relative path when /data isn't present (e.g. local dev outside +// docker), so it stays writable there too. +const dataDir = process.env.REDIS_DATA_DIR || '/data'; +const accountKeyPath = fs.existsSync(dataDir) ? path.join(dataDir, 'le_key.cert') : './le_key.cert'; + const letsEncrypt = new LetsEncrypt({ directoryUrl: conf.environment === "production" ? LetsEncrypt.AcmeClient.directory.letsencrypt.production : LetsEncrypt.AcmeClient.directory.letsencrypt.staging, + accountKeyPath, }); class Host extends Table{ diff --git a/nodejs/views/permissions.ejs b/nodejs/views/permissions.ejs index 0652bd2..e674106 100644 --- a/nodejs/views/permissions.ejs +++ b/nodejs/views/permissions.ejs @@ -40,6 +40,50 @@ } } + function permissionAddOpen(){ + app.modal.open({title: 'Add Permission', bodyHtml: + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '
' + + 'Wildcards: *.example.com matches one label, ' + + '**.example.com matches any depth (incl. the apex), ' + + '** matches every domain.' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '
', + }); + } + function removePermission(id){ app.permission.remove(id, function(error, data){ if(error) return app.messages.action(error, $.scope.Permission.$this, 'danger'); @@ -79,63 +123,7 @@