diff --git a/nodejs/routes/api_conf.js b/nodejs/routes/api_conf.js index 3492889..9a2d33d 100644 --- a/nodejs/routes/api_conf.js +++ b/nodejs/routes/api_conf.js @@ -90,5 +90,42 @@ router.post('/', async (req, res, next) => { next(err); } }); +router.get('/proxy', async (req, res, next) => { + try { + const proxyConf = await baoConf.get('proxy/conf') || {}; + const editable = JSON.parse(JSON.stringify(proxyConf)); + if (editable.oidc && editable.oidc.clientSecret) editable.oidc.clientSecret = MASK; + if (editable.ldap && editable.ldap.bindPassword) editable.ldap.bindPassword = MASK; + res.json(editable); + } catch(err) { + next(err); + } +}); + +router.post('/proxy', async (req, res, next) => { + try { + const existing = await baoConf.get('proxy/conf') || {}; + const incoming = req.body || {}; + + if (incoming.oidc && incoming.oidc.clientSecret !== undefined) { + if (incoming.oidc.clientSecret === '' || incoming.oidc.clientSecret === MASK) delete incoming.oidc.clientSecret; + } + if (incoming.ldap && incoming.ldap.bindPassword !== undefined) { + if (incoming.ldap.bindPassword === '' || incoming.ldap.bindPassword === MASK) delete incoming.ldap.bindPassword; + } + + for (const key of Object.keys(incoming)) { + if (typeof incoming[key] === 'object' && incoming[key] !== null && !Array.isArray(incoming[key])) { + existing[key] = { ...(existing[key] || {}), ...incoming[key] }; + } else { + existing[key] = incoming[key]; + } + } + await baoConf.set('proxy/conf', existing); + res.json({ success: true }); + } catch(err) { + next(err); + } +}); module.exports = router; \ No newline at end of file diff --git a/nodejs/views/conf.ejs b/nodejs/views/conf.ejs index a3563d5..b2dae87 100644 --- a/nodejs/views/conf.ejs +++ b/nodejs/views/conf.ejs @@ -4,6 +4,7 @@ $(document).ready(function() { loadConf(); + loadProxyConf(); loadTos(); }); @@ -88,6 +89,47 @@ } } + async function loadProxyConf() { + try { + const data = await app.api.get('conf/proxy'); + if (data.oidc) { + $('#proxy-issuer').val(data.oidc.issuer || ''); + $('#proxy-client-id').val(data.oidc.clientId || ''); + $('#proxy-client-secret').val(data.oidc.clientSecret || ''); + } + if (data.ldap) { + $('#proxy-ldap-bindpass').val(data.ldap.bindPassword || ''); + } + } catch (error) { + console.error('Failed to load Proxy conf:', error); + } + } + + async function saveProxyConf() { + const btn = $('#btn-save-proxy'); + btn.prop('disabled', true).html(' Saving...'); + + const payload = { + oidc: { + issuer: $('#proxy-issuer').val(), + clientId: $('#proxy-client-id').val(), + clientSecret: $('#proxy-client-secret').val() + }, + ldap: { + bindPassword: $('#proxy-ldap-bindpass').val() + } + }; + + try { + await app.api.post('conf/proxy', payload); + app.messages.toast('Proxy configuration saved securely to OpenBao!', 'success'); + } catch (error) { + app.messages.toast('Failed to save Proxy configuration: ' + error.message, 'danger'); + } finally { + btn.prop('disabled', false).html(' Save Proxy Secrets'); + } + } + // ── Terms of Service editor ────────────────────────────────────────── // Moved here from the admin Overview dashboard — it's a configuration // control, so it belongs on the System Configuration page. The API is @@ -165,6 +207,9 @@ +
@@ -266,6 +311,47 @@
+ +
+
+
+
Proxy Secrets (OpenBao)
+
+
+

These secrets are stored directly in OpenBao (`secret/proxy/conf`) and read by the Proxy at boot.

+ +
OAuth / OIDC Integration
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ +
LDAP Integration
+
+ +
+ + +
+
Password for the Proxy's LDAP service account.
+
+ + +
+
+
+