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>
55 lines
1.8 KiB
PHP
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]);
|