Managed Instances) - adding and changing them, their connection * codes, asking them to update, read-only and suspended, signing in to them as support, and * removing them. The workings are in functions/managed.php. */ defined('FROM_POST_HANDLER') || die("Direct file access is not allowed"); // Off unless config.php turns it on (managerEnabled()) - then none of this answers if (!managerEnabled()) { return; } /* The instance a request is about, not removed - or back with why */ $managed_instance_for_request = function ($id, bool $connected = false) { $row = managedInstanceRow(intval($id)); if (!$row) { flashAlert("That instance is gone - it may have been deleted", 'error'); redirect('managed_instances.php'); } if (!empty($row['managed_instance_archived_at'])) { flashAlert(escapeHtml($row['managed_instance_name']) . " has been removed", 'error'); redirect("managed_instance.php?id=" . intval($row['managed_instance_id'])); } if ($connected && (empty($row['managed_instance_secret_hash']) || trim((string) $row['managed_instance_url']) === '')) { flashAlert(escapeHtml($row['managed_instance_name']) . " isn't connected yet", 'error'); redirect("managed_instance.php?id=" . intval($row['managed_instance_id'])); } return $row; }; if (isset($_POST['add_managed_instance'])) { validateCSRFToken(); [$fields, $error] = managedInstanceFromInput($_POST); if ($error !== '') { flashAlert(escapeHtml($error), 'error'); redirect(); } // The signing key is made now, so a server that can't make one says so before anything connects try { managerKeys(); } catch (Throwable $e) { flashAlert(escapeHtml($e->getMessage()), 'error'); redirect(); } mysqli_query($mysqli, "INSERT INTO managed_instances SET managed_instance_uuid = '" . managedUuid() . "', " . managedInstanceSetSql($fields)); $instance_id = intval(mysqli_insert_id($mysqli)); $_SESSION['managed_instance_code'][$instance_id] = managedInstanceNewCode($instance_id); logAudit("Managed Instance", "Create", "$session_name added managed instance " . escapeSql($fields['name']), $fields['client_id'], $instance_id); flashAlert("" . escapeHtml($fields['name']) . " added - now connect it with the code below"); redirect("managed_instance.php?id=$instance_id"); } if (isset($_POST['edit_managed_instance'])) { validateCSRFToken(); $row = $managed_instance_for_request($_POST['managed_instance_id'] ?? 0); $instance_id = intval($row['managed_instance_id']); [$fields, $error] = managedInstanceFromInput($_POST); if ($error !== '') { flashAlert(escapeHtml($error), 'error'); redirect(); } // Once it has connected, its address is the one it says it has if (!empty($row['managed_instance_secret_hash'])) { $fields['url'] = null; } mysqli_query($mysqli, "UPDATE managed_instances SET " . managedInstanceSetSql($fields) . " WHERE managed_instance_id = $instance_id"); // What changed, for the audit log - what it's set to, in words $changes = []; if ($row['managed_instance_name'] !== $fields['name']) { $changes[] = 'renamed from ' . $row['managed_instance_name']; } if (intval($row['managed_instance_seat_limit']) !== $fields['seat_limit']) { $changes[] = 'seat limit ' . ($fields['seat_limit'] ?: 'none') . ' (was ' . (intval($row['managed_instance_seat_limit']) ?: 'none') . ')'; } if ($row['managed_instance_update_policy'] !== $fields['update_policy'] || intval($row['managed_instance_update_delay_days']) !== $fields['update_delay_days']) { $changes[] = 'updates ' . ($fields['update_policy'] === 'auto' ? 'automatic after ' . $fields['update_delay_days'] . ' days' : 'by hand'); } if ((string) $row['managed_instance_white_label_key'] !== $fields['white_label_key']) { $changes[] = $fields['white_label_key'] === '' ? 'white-label key taken off' : 'white-label key set'; } if ($row['managed_instance_mode'] !== $fields['mode']) { $changes[] = strtolower(MANAGED_MODES[$fields['mode']]) . ' (was ' . strtolower(MANAGED_MODES[$row['managed_instance_mode']] ?? $row['managed_instance_mode']) . ')'; } if (intval($row['managed_instance_client_id']) !== $fields['client_id']) { $changes[] = 'client changed'; } if ($fields['url'] !== null && (string) $row['managed_instance_url'] !== $fields['url']) { $changes[] = $fields['url'] === '' ? 'address taken off' : 'address ' . $fields['url']; } if ($changes) { logAudit("Managed Instance", "Edit", "$session_name edited managed instance " . escapeSql($fields['name']) . " - " . escapeSql(implode(', ', $changes)), $fields['client_id'], $instance_id); } flashAlert("" . escapeHtml($fields['name']) . " " . ($changes ? 'saved' . (!empty($row['managed_instance_secret_hash']) ? ' - it takes the change at its next check-in' : '') : 'unchanged')); redirect(); } if (isset($_GET['managed_instance_new_code'])) { validateCSRFToken(); $row = $managed_instance_for_request($_GET['managed_instance_new_code']); $instance_id = intval($row['managed_instance_id']); $_SESSION['managed_instance_code'][$instance_id] = managedInstanceNewCode($instance_id); logAudit("Managed Instance", "Code", "$session_name made a new connection code for " . escapeSql($row['managed_instance_name']), intval($row['managed_instance_client_id']), $instance_id); redirect("managed_instance.php?id=$instance_id"); } if (isset($_GET['managed_command'])) { validateCSRFToken(); $command = (string) $_GET['managed_command']; if (!in_array($command, ['update_now', 'check_updates'], true)) { flashAlert("That isn't something an instance can be asked", 'error'); redirect(); } $row = $managed_instance_for_request($_GET['id'] ?? 0, true); $instance_id = intval($row['managed_instance_id']); $by = (string) (mysqli_fetch_row(mysqli_query($mysqli, "SELECT user_name FROM users WHERE user_id = " . intval($session_user_id)))[0] ?? 'An admin'); managedQueueCommand($instance_id, $command, $by); logAudit("Managed Instance", $command === 'update_now' ? "Update" : "Check", "$session_name asked " . escapeSql($row['managed_instance_name']) . " to " . strtolower(managedCommandLabel($command)), intval($row['managed_instance_client_id']), $instance_id); flashAlert("Asked - " . escapeHtml($row['managed_instance_name']) . " picks it up at its next check-in, within a minute"); redirect(); } if (isset($_GET['managed_instance_mode'])) { validateCSRFToken(); $mode = (string) $_GET['managed_instance_mode']; if (!isset(MANAGED_MODES[$mode])) { flashAlert("That isn't a mode", 'error'); redirect(); } $row = $managed_instance_for_request($_GET['id'] ?? 0); $instance_id = intval($row['managed_instance_id']); mysqli_query($mysqli, "UPDATE managed_instances SET managed_instance_mode = '$mode' WHERE managed_instance_id = $instance_id"); logAudit("Managed Instance", "Mode", "$session_name made " . escapeSql($row['managed_instance_name']) . " " . strtolower(MANAGED_MODES[$mode]) . " (was " . strtolower(MANAGED_MODES[$row['managed_instance_mode']] ?? $row['managed_instance_mode']) . ")", intval($row['managed_instance_client_id']), $instance_id); flashAlert("" . escapeHtml($row['managed_instance_name']) . " is " . strtolower(MANAGED_MODES[$mode]) . (!empty($row['managed_instance_secret_hash']) ? ' from its next check-in' : ' - it takes it when it connects')); redirect(); } if (isset($_GET['managed_support_sign_in'])) { validateCSRFToken(); $row = $managed_instance_for_request($_GET['managed_support_sign_in'], true); $instance_id = intval($row['managed_instance_id']); $me = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT user_name, user_email FROM users WHERE user_id = " . intval($session_user_id))) ?: []; try { $link = managedSupportLink($row, (string) ($me['user_name'] ?? ''), (string) ($me['user_email'] ?? '')); } catch (Throwable $e) { flashAlert(escapeHtml($e->getMessage()), 'error'); redirect("managed_instance.php?id=$instance_id"); } logAudit("Managed Instance", "Support", "$session_name signed in to " . escapeSql($row['managed_instance_name']) . " as support", intval($row['managed_instance_client_id']), $instance_id); redirect($link); } if (isset($_GET['remove_managed_instance'])) { validateCSRFToken(); $row = $managed_instance_for_request($_GET['remove_managed_instance']); $instance_id = intval($row['managed_instance_id']); mysqli_query($mysqli, "UPDATE managed_instances SET managed_instance_archived_at = NOW(), managed_instance_enroll_hash = NULL, managed_instance_enroll_expires_at = NULL WHERE managed_instance_id = $instance_id"); // What it hasn't picked up, it won't now mysqli_query($mysqli, "UPDATE managed_instance_commands SET managed_command_status = 'cancelled', managed_command_result = 'The instance was removed', managed_command_done_at = NOW() WHERE managed_command_instance_id = $instance_id AND managed_command_status = 'queued'"); logAudit("Managed Instance", "Remove", "$session_name removed managed instance " . escapeSql($row['managed_instance_name']), intval($row['managed_instance_client_id']), $instance_id); flashAlert("" . escapeHtml($row['managed_instance_name']) . " removed" . (!empty($row['managed_instance_secret_hash']) ? ' - it lets go at its next check-in' : ''), 'error'); redirect('managed_instances.php'); } if (isset($_GET['delete_managed_instance'])) { validateCSRFToken(); $row = managedInstanceRow(intval($_GET['delete_managed_instance'])); if (!$row || empty($row['managed_instance_archived_at'])) { flashAlert("Only a removed instance can be deleted", 'error'); redirect('managed_instances.php'); } $instance_id = intval($row['managed_instance_id']); mysqli_query($mysqli, "DELETE FROM managed_instance_commands WHERE managed_command_instance_id = $instance_id"); mysqli_query($mysqli, "DELETE FROM managed_instances WHERE managed_instance_id = $instance_id"); logAudit("Managed Instance", "Delete", "$session_name deleted managed instance " . escapeSql($row['managed_instance_name']), intval($row['managed_instance_client_id']), $instance_id); flashAlert("" . escapeHtml($row['managed_instance_name']) . " deleted", 'error'); redirect('managed_instances.php?show=removed'); }