59d4b65195
- Boot: bao-conf.init('sso-manager') replaces conf_manager; deep-merges
secret/sso-manager/conf over file config (fail-soft). Scoped VAULT_TOKEN
(policy sso-broker), never root.
- /api/vault reworked: middleware.auth -> scopeGuard -> token-injecting
proxy. vault_broker.js mints Redis-cached per-user (user-<uid>) /
per-admin (sso-admin) tokens via the sso-broker role; scopeGuard enforces
path prefix on top of the OpenBao policy. Client auth-token stripped.
- vault UI renamed (vaultwarden.ejs -> vault.ejs), /vault route auth-gated,
role-scoped: users see only secret/users/<uid>/, admins get free-form +
Apps mint tab (secret/apps/<name>/*, token shown once).
- api_conf.js writes via bao-conf.set('sso-manager', ...).
- Remediation: config/*-secrets.js untracked+gitignored, test_plugins.js
deleted, proxy-secrets.js.example placeholder added. Secrets remain in
git history; provider-side rotation is the real fix.
Co-Authored-By: Claude <noreply@anthropic.com>
60 lines
1.9 KiB
JavaScript
60 lines
1.9 KiB
JavaScript
const router = require('express').Router();
|
|
const baoConf = require('@simpleworkjs/bao-conf');
|
|
const permission = require('../utils/permission');
|
|
const conf = require('@simpleworkjs/conf');
|
|
|
|
router.use(async (req, res, next) => {
|
|
try {
|
|
await permission.byGroup(req.user, ['app_sso_admin']);
|
|
next();
|
|
} catch(err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
router.get('/', async (req, res) => {
|
|
const editable = {
|
|
smtp: conf.smtp || {},
|
|
discovery: conf.discovery || {},
|
|
oauth: conf.oauth || {}
|
|
};
|
|
res.json(editable);
|
|
});
|
|
|
|
// Shallow-per-key merge of `src` into the live conf object (matches the old
|
|
// conf_manager.applyConf behaviour: nested objects are spread, not deep-merged,
|
|
// so call-time conf readers see saved values without a restart).
|
|
function applyToLiveConf(src) {
|
|
if (!src) return;
|
|
for (const key of Object.keys(src)) {
|
|
if (typeof src[key] === 'object' && src[key] !== null && !Array.isArray(src[key])) {
|
|
conf[key] = { ...(conf[key] || {}), ...src[key] };
|
|
} else {
|
|
conf[key] = src[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
router.post('/', async (req, res, next) => {
|
|
try {
|
|
const existing = await baoConf.get('sso-manager/conf') || {};
|
|
// Deep merge req.body into existing
|
|
for (const key of Object.keys(req.body)) {
|
|
if (typeof req.body[key] === 'object' && req.body[key] !== null && !Array.isArray(req.body[key])) {
|
|
existing[key] = { ...(existing[key] || {}), ...req.body[key] };
|
|
} else {
|
|
existing[key] = req.body[key];
|
|
}
|
|
}
|
|
await baoConf.set('sso-manager/conf', existing);
|
|
// Reflect the saved values in the live conf immediately (the next boot's
|
|
// bao-conf.init() would pick them up too, but this keeps running readers
|
|
// current without a restart, as the old conf_manager did).
|
|
applyToLiveConf(existing);
|
|
res.json({ success: true });
|
|
} catch(err) {
|
|
next(err);
|
|
}
|
|
});
|
|
|
|
module.exports = router; |