Files
sso-manager-node/nodejs/routes/service_account.js
T
wmantly 0b701dfc6f Merge OAuth Apps + LDAP Info into one tabbed page; add Service Accounts
- OAuth Apps and LDAP Info are both "how do other apps/hosts plug into
  this SSO" concerns -- merged into a single /integrations page with
  tabs, replacing the two separate nav items with one. /oauth-clients
  and /ldap-info 301-redirect there for compat.
- Add a Service Accounts section under the LDAP tab: bind-only LDAP
  identities (organizationalRole + simpleSecurityObject, no
  posixAccount) for apps/hosts, as opposed to real people. Create,
  rotate password, and delete, all from the UI -- previously the only
  such account (theta-env's bootstrap-created cn=ldapclient) was
  invisible to the Users page entirely (filtered out by
  conf.ldap.userFilter) and had no GUI way to see or rotate it; the new
  ServiceAccount model uses the exact same objectClasses bootstrap.js
  already creates cn=ldapclient with, so it recognizes and manages that
  account too, not just ones created through this UI.
- The ldap-client bash snippet now points at "create one under Service
  Accounts above" instead of a bare textual example.

Verified against a real LDAP server (not just the dev sandbox's usual
unreachable one): created a service account, confirmed it binds
successfully with the generated password, rotated its password, and
deleted it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 19:57:32 -04:00

55 lines
1.5 KiB
JavaScript

'use strict';
const router = require('express').Router();
const {ServiceAccount} = require('../models/service_account');
const permission = require('../utils/permission');
const ADMIN_GROUP = 'app_sso_admin';
router.get('/', async function(req, res, next) {
try {
await permission.byGroup(req.user, [ADMIN_GROUP]);
return res.json({results: await ServiceAccount.list()});
} catch(error) {
next(error);
}
});
router.post('/', async function(req, res, next) {
try {
await permission.byGroup(req.user, [ADMIN_GROUP]);
const result = await ServiceAccount.create({cn: req.body.cn, description: req.body.description});
return res.json({
results: result,
message: `Service account "${result.cn}" created. Save the password now — it will not be shown again.`,
});
} catch(error) {
next(error);
}
});
router.put('/:cn/password', async function(req, res, next) {
try {
await permission.byGroup(req.user, [ADMIN_GROUP]);
const result = await ServiceAccount.setPassword(req.params.cn, req.body.password);
return res.json({
results: result,
message: `Password rotated for "${req.params.cn}". Save it now — it will not be shown again.`,
});
} catch(error) {
next(error);
}
});
router.delete('/:cn', async function(req, res, next) {
try {
await permission.byGroup(req.user, [ADMIN_GROUP]);
await ServiceAccount.remove(req.params.cn);
return res.json({message: `Service account "${req.params.cn}" deleted.`});
} catch(error) {
next(error);
}
});
module.exports = router;