Implement email support with per-user permission model (closes #2)
New modules: - server/mailer.mjs: nodemailer transport wrapper - server/mail_perms.mjs: runtime permission store, persisted to disk New actions: - send-email: checks (caller, to, topic) permission before sending - set-mail-permission: grant/revoke permissions, gated by canApprove - get-mail-permissions: list current permissions Handler signature extended to handler(params, ctx) where ctx carries caller, users, mail_perm_store and mailer_send. Existing handlers ignore ctx so the change is backwards-compatible. SMTP config lives in secrets.json under optional 'smtp' key. Mail permissions path via --mail-perms or CONDUIT_MAIL_PERMS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@ import { actions } from './actions.mjs';
|
||||
import { enqueue, get_entry, list_pending, resolve } from './queue.mjs';
|
||||
import { load_secrets } from './secrets.mjs';
|
||||
import { create_auth_middleware, check_can_approve } from './auth.mjs';
|
||||
import { create_mailer } from './mailer.mjs';
|
||||
import { load_mail_perms } from './mail_perms.mjs';
|
||||
|
||||
function get_arg(argv, flag) {
|
||||
const i = argv.indexOf(flag);
|
||||
@@ -17,7 +19,9 @@ const PORT = process.env.CONDUIT_PORT || 3015;
|
||||
const BIND = get_arg(process.argv, '--bind') || process.env.CONDUIT_BIND || '127.0.0.1';
|
||||
const VERBOSE = process.argv.includes('--verbose');
|
||||
|
||||
const secrets_path = get_arg(process.argv, '--secrets');
|
||||
const secrets_path = get_arg(process.argv, '--secrets');
|
||||
const mail_perms_path = get_arg(process.argv, '--mail-perms') || process.env.CONDUIT_MAIL_PERMS || null;
|
||||
|
||||
let secrets;
|
||||
try {
|
||||
secrets = load_secrets(secrets_path);
|
||||
@@ -25,7 +29,21 @@ try {
|
||||
console.error(`Fatal: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const { users } = secrets;
|
||||
const { users, smtp } = secrets;
|
||||
|
||||
let mail_perm_store;
|
||||
try {
|
||||
mail_perm_store = load_mail_perms(mail_perms_path);
|
||||
} catch (err) {
|
||||
console.error(`Fatal: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!mail_perms_path) {
|
||||
console.warn('Warning: --mail-perms not set; mail permissions will not persist across restarts');
|
||||
}
|
||||
|
||||
const mailer_send = create_mailer(smtp);
|
||||
|
||||
const app = express();
|
||||
app.use(express.json({
|
||||
@@ -75,9 +93,11 @@ app.post('/action', async (req, res) => {
|
||||
return res.status(403).json({ status: 'denied', reason: 'Policy: auto-deny' });
|
||||
}
|
||||
|
||||
const ctx = { caller: req.conduit_user, users, mail_perm_store, mailer_send };
|
||||
|
||||
if (def.policy === 'auto-accept') {
|
||||
try {
|
||||
const result = await def.handler(params);
|
||||
const result = await def.handler(params, ctx);
|
||||
return res.json({ status: 'accepted', result });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ status: 'error', error: err.message });
|
||||
@@ -111,9 +131,10 @@ app.post('/queue/:id/approve', async (req, res) => {
|
||||
entry.resolved_by = req.conduit_user;
|
||||
resolve(req.params.id, 'approved');
|
||||
|
||||
const ctx = { caller: entry.submitted_by, users, mail_perm_store, mailer_send };
|
||||
const def = actions[entry.action];
|
||||
try {
|
||||
const result = await def.handler(entry.params);
|
||||
const result = await def.handler(entry.params, ctx);
|
||||
res.json({ status: 'approved', result });
|
||||
} catch (err) {
|
||||
res.status(500).json({ status: 'error', error: err.message });
|
||||
|
||||
Reference in New Issue
Block a user