Files
reflector-dashboard/auth/node_admins_api.php
T
bjorneandClaude Fable 5.1 25b481e2ae Public web-only release: SvxPortal front-end + auth backend, install templates and INSTALL.md
Stripped from the internal repo: per-box deploy trees, Go services (nodelist/astmeta/proxy/svxdr),
deploy/sync scripts and all secrets. Config templates for nginx, PHP-FPM, Redis and tmpfiles
live in install/ with CHANGEME placeholders.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 23:07:05 +02:00

55 lines
1.8 KiB
PHP

<?php
declare(strict_types=1);
// Manage a node's admin list (Redis set node_admins:<CALL>). The node's sysop is
// always the implicit default admin; this stores ADDITIONAL admins.
//
// Owners (user_owns_node) may LIST the admins of their nodes; only site admins
// (admin_level >= 2) may add or remove entries — users must not grant
// themselves or others admin rights.
require __DIR__ . '/lib.php';
$uuid = require_login();
csrf_check();
$in = json_in();
$action = strtolower(trim((string)($in['action'] ?? 'list')));
function na_valid_call(string $cs): bool {
return (bool)preg_match('/^[A-Z0-9]{1,3}[0-9][A-Z0-9]{0,3}[A-Z](-[A-Z0-9]{1,10})?$/', $cs);
}
$node = strtoupper(trim((string)($in['node'] ?? '')));
$admin = strtoupper(trim((string)($in['admin'] ?? '')));
if ($node === '' || !node_callsign_valid($node)) {
json_out(['ok' => false, 'error' => 'invalid_node'], 400);
}
$r = redis();
$key = rkey("node_admins:$node");
// Listing requires ownership (callsign/email/node-admin — user_owns_node).
if (!user_owns_node($uuid, $node)) {
json_out(['ok' => false, 'error' => 'forbidden'], 403);
}
$stored = $r->sMembers($key) ?: [];
if ($action === 'add' || $action === 'remove') {
// Changing the list is site-admin only.
if (acct_admin_level($uuid) < 2) {
json_out(['ok' => false, 'error' => 'forbidden'], 403);
}
if ($admin === '' || !na_valid_call($admin)) {
json_out(['ok' => false, 'error' => 'invalid_admin'], 400);
}
if ($action === 'add') {
$r->sAdd($key, $admin);
} else {
$r->sRem($key, $admin);
}
$stored = $r->sMembers($key) ?: [];
} elseif ($action !== 'list') {
json_out(['ok' => false, 'error' => 'invalid_action'], 400);
}
sort($stored);
json_out(['ok' => true, 'node' => $node, 'admins' => $stored]);