diff --git a/lib/adminUi.js b/lib/adminUi.js index abe5d28..8206f80 100644 --- a/lib/adminUi.js +++ b/lib/adminUi.js @@ -85,37 +85,48 @@ const csrfField = (req) => { }; -const layout = (title, body) => { - return ` - - -${escapeHtml(title)} - - - +${navPills(req, links)} ${body} -

${escapeHtml(constants.PLUGIN_NAME)} v${escapeHtml(constants.PLUGIN_VERSION)}

-`; +

${escapeHtml(constants.PLUGIN_NAME)} v${escapeHtml(constants.PLUGIN_VERSION)}

`; }; @@ -133,7 +144,7 @@ const dashboard = async (req, res) => { const active = await keys.getActiveKeyMeta(); const issuer = issuerForReq(req); const body = ` - +
@@ -148,7 +159,7 @@ const dashboard = async (req, res) => {

Signing key rotation

Generates a new active signing key (new kid). The previous key keeps verifying issued tokens (stays in JWKS as retiring) until its grace window elapses, then drops out.

${csrfField(req)}`; - res.type("text/html").send(layout("saltcorn-idp dashboard", body)); + res.sendWrap("saltcorn-idp dashboard", layout(req, body)); } catch (e) { // eslint-disable-next-line no-console console.error(`[${constants.PLUGIN_NAME}] dashboard failed:`, e); @@ -198,12 +209,12 @@ const groupsPage = async (req, res) => { const body = `

Groups

The OIDC groups claim = each user's Saltcorn role (as role:<name>) plus these custom groups (as group:<name>).

-
Env ID${escapeHtml(env ? env.env_id : "?")}
Bootstrapped at${escapeHtml(env ? env.bootstrapped_at : "")}
Issuer${escapeHtml(issuer)}
${rows || ''}
GroupMembers
no groups yet
+ ${rows || ''}
GroupMembers
no groups yet

Create group

${csrfField(req)}
`; - res.type("text/html").send(layout("saltcorn-idp groups", body)); + res.sendWrap("saltcorn-idp groups", layout(req, body)); } catch (e) { // eslint-disable-next-line no-console console.error(`[${constants.PLUGIN_NAME}] groups page failed:`, e); @@ -306,7 +317,7 @@ const clientsPage = async (req, res) => { } const body = `

Clients (relying parties)

- ${rows || ''}
client_idlabelauthredirect URIsscope
no clients yet
+ ${rows || ''}
client_idlabelauthredirect URIsscope
no clients yet

Register client

${csrfField(req)}

client_id

@@ -322,7 +333,7 @@ const clientsPage = async (req, res) => {

scope

`; - res.type("text/html").send(layout("saltcorn-idp clients", body)); + res.sendWrap("saltcorn-idp clients", layout(req, body)); } catch (e) { // eslint-disable-next-line no-console console.error(`[${constants.PLUGIN_NAME}] clients page failed:`, e); @@ -368,7 +379,7 @@ const createClientHandler = async (req, res) => {

Client secret (shown once - copy it now):

${escapeHtml(created.secret)}

Back to clients

`; - res.type("text/html").send(layout("client secret", body)); + res.sendWrap("client secret", layout(req, body)); } else { res.redirect(constants.ADMIN_BASE_PATH + "/clients"); } @@ -409,7 +420,7 @@ const samlSpsPage = async (req, res) => { const body = `

SAML service providers

Only registered SPs receive assertions, and only at an allow-listed ACS URL. A signing cert enables (and "require signed" enforces) AuthnRequest signature verification.

- ${rows || ''}
entityIDlabelACS URLsreq signedcert
no SPs yet
+ ${rows || ''}
entityIDlabelACS URLsreq signedcert
no SPs yet

Register SP

${csrfField(req)}

entityID

@@ -419,7 +430,7 @@ const samlSpsPage = async (req, res) => {

`; - res.type("text/html").send(layout("saltcorn-idp saml sps", body)); + res.sendWrap("saltcorn-idp saml sps", layout(req, body)); } catch (e) { // eslint-disable-next-line no-console console.error(`[${constants.PLUGIN_NAME}] saml sps page failed:`, e); @@ -512,7 +523,7 @@ const ldapListenerSection = async (req) => { const applied = await ldapSettings.getApplied(); const effective = runtime.enabled ? `${escapeHtml(runtime.host)}:${escapeHtml(String(runtime.port))}` : "disabled"; const running = (applied && applied.enabled) ? `${escapeHtml(applied.host)}:${escapeHtml(String(applied.port))}` : "disabled"; - const statusTable = ` + const statusTable = `
Currently running${running}
Effective after restart${effective}
`; @@ -557,7 +568,7 @@ const ldapServicePage = async (req, res) => { ${listener}

LDAP service account

A service DN + password for the search-then-bind flow (an application binds as this DN, searches for a user, then re-binds as that user to validate the password). The password is sealed at rest and never displayed.

-
Configured service DN${dn ? `${escapeHtml(dn)}` : '(none)'}
+
Configured service DN${dn ? `${escapeHtml(dn)}` : '(none)'}

Set service account

${csrfField(req)}

service DN

@@ -566,7 +577,7 @@ const ldapServicePage = async (req, res) => {

Clear

${csrfField(req)}
`; - res.type("text/html").send(layout("saltcorn-idp ldap", body)); + res.sendWrap("saltcorn-idp ldap", layout(req, body)); } catch (e) { // eslint-disable-next-line no-console console.error(`[${constants.PLUGIN_NAME}] ldap service page failed:`, e);