Import postgresql plugin
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return static function (AppContext $ctx): void {
|
||||
if (!$ctx->settings->allowRemoteHosts()) {
|
||||
$ctx->renderFatal('Zarządzanie hostami zdalnymi jest wyłączone (`allow_remote_hosts=0`).', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
$ok = [];
|
||||
$errors = [];
|
||||
$lastSyncOutput = '';
|
||||
|
||||
$daUser = $ctx->daUser->username();
|
||||
$prefix = $ctx->daUser->prefix();
|
||||
$postgresPort = $ctx->pg->serverPort();
|
||||
|
||||
$dbNames = array_map(static fn(array $db): string => $db['name'], $ctx->pg->listDatabasesForUser($daUser));
|
||||
if (empty($dbNames)) {
|
||||
$ctx->renderPage('Hosty Zdalne Użytkownika', '<div class="panel"><p class="muted">Brak baz danych do zarządzania hostami.</p></div>', $ok, $errors);
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedDb = $ctx->postString('db_name');
|
||||
if ($selectedDb === '') {
|
||||
$selectedDb = $ctx->queryString('db');
|
||||
}
|
||||
if ($selectedDb === '') {
|
||||
$selectedDb = $dbNames[0];
|
||||
}
|
||||
try {
|
||||
$selectedDb = NamePolicy::normalizeDatabaseName($selectedDb, $prefix);
|
||||
} catch (Throwable $e) {
|
||||
$selectedDb = $dbNames[0];
|
||||
}
|
||||
if (!in_array($selectedDb, $dbNames, true)) {
|
||||
$selectedDb = $dbNames[0];
|
||||
}
|
||||
|
||||
$roleNames = $ctx->pg->listDatabaseUsers($daUser, $selectedDb);
|
||||
$selectedRole = $ctx->queryString('role');
|
||||
|
||||
if ($ctx->isPost()) {
|
||||
$selectedRole = $ctx->postString('role_name');
|
||||
|
||||
try {
|
||||
$ctx->requireCsrf('manage_hosts_submit');
|
||||
|
||||
$selectedDb = NamePolicy::normalizeDatabaseName($ctx->postString('db_name', $selectedDb), $prefix);
|
||||
if (!in_array($selectedDb, $dbNames, true)) {
|
||||
throw new RuntimeException('Baza nie należy do konta DA: ' . $selectedDb);
|
||||
}
|
||||
|
||||
$roleNames = $ctx->pg->listDatabaseUsers($daUser, $selectedDb);
|
||||
$selectedRole = NamePolicy::normalizeRoleName($selectedRole, $prefix);
|
||||
if (!in_array($selectedRole, $roleNames, true)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL nie ma dostępu do wybranej bazy: ' . $selectedRole);
|
||||
}
|
||||
|
||||
$currentEntries = $ctx->pg->getRoleHostEntries($daUser, $selectedRole, $selectedDb);
|
||||
$hostMap = [];
|
||||
foreach ($currentEntries as $entry) {
|
||||
$hostMap[$entry['host']] = $entry['note'];
|
||||
}
|
||||
|
||||
$removeHosts = $ctx->postArray('remove_hosts');
|
||||
$removeHosts = array_values(array_unique($removeHosts));
|
||||
foreach ($removeHosts as $rawHost) {
|
||||
$host = trim($rawHost);
|
||||
if ($host === '') {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($host, $hostMap)) {
|
||||
unset($hostMap[$host]);
|
||||
}
|
||||
}
|
||||
|
||||
$addHostRaw = trim($ctx->postString('add_host'));
|
||||
if ($addHostRaw !== '') {
|
||||
$addHost = NamePolicy::normalizeRemoteIpv4AccessPattern($addHostRaw);
|
||||
$addComment = NamePolicy::normalizeHostComment($ctx->postString('add_comment'));
|
||||
$hostMap[$addHost] = $addComment;
|
||||
}
|
||||
|
||||
$allowAllHosts = $ctx->postString('allow_all_hosts') === '1';
|
||||
if ($allowAllHosts) {
|
||||
$allHostsComment = NamePolicy::normalizeHostComment($ctx->postString('allow_all_comment'));
|
||||
$hostMap[NamePolicy::ALL_IPV4_HOST_PATTERN] = $allHostsComment !== '' ? $allHostsComment : 'Dostęp z każdego adresu IPv4';
|
||||
} else {
|
||||
unset($hostMap[NamePolicy::ALL_IPV4_HOST_PATTERN]);
|
||||
}
|
||||
|
||||
if (count($hostMap) > $ctx->settings->maxHostsPerUser()) {
|
||||
throw new RuntimeException('Przekroczono limit hostów dla wybranej bazy i użytkownika PostgreSQL.');
|
||||
}
|
||||
|
||||
$finalEntries = [];
|
||||
foreach ($hostMap as $host => $note) {
|
||||
$finalEntries[] = ['host' => $host, 'note' => $note];
|
||||
}
|
||||
|
||||
$ctx->pg->replaceRoleHostsWithNotes($daUser, $selectedRole, $finalEntries, $selectedDb);
|
||||
$restartPostgresql = $ctx->postString('restart_postgresql') === '1';
|
||||
$sync = $ctx->pg->syncHbaRules($restartPostgresql);
|
||||
$lastSyncOutput = trim((string)($sync['output'] ?? ''));
|
||||
if (!$sync['ok']) {
|
||||
$errors[] = 'Hosty zapisane, ale synchronizacja konfiguracji hostów zdalnych nie powiodła się: ' . $sync['output'];
|
||||
} elseif ($restartPostgresql) {
|
||||
$ok[] = 'Synchronizacja konfiguracji PostgreSQL zakończona i usługa PostgreSQL została zrestartowana.';
|
||||
} elseif (array_key_exists(NamePolicy::ALL_IPV4_HOST_PATTERN, $hostMap)) {
|
||||
$ok[] = 'Synchronizacja PostgreSQL zakończona. Dla dostępu z każdego hosta port PostgreSQL do otwarcia w CSF: ' . $postgresPort . '.';
|
||||
} else {
|
||||
$ok[] = 'Synchronizacja konfiguracji PostgreSQL i CSF zakończona.';
|
||||
}
|
||||
|
||||
$ok[] = 'Zaktualizowano hosty dostępu dla użytkownika ' . $selectedRole . '.';
|
||||
} catch (Throwable $e) {
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$roleNames = $ctx->pg->listDatabaseUsers($daUser, $selectedDb);
|
||||
if ($selectedRole !== '') {
|
||||
try {
|
||||
$selectedRole = NamePolicy::normalizeRoleName($selectedRole, $prefix);
|
||||
} catch (Throwable $e) {
|
||||
$selectedRole = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ($selectedRole === '' && !empty($roleNames)) {
|
||||
$selectedRole = $roleNames[0];
|
||||
}
|
||||
|
||||
$currentEntries = [];
|
||||
if ($selectedRole !== '') {
|
||||
$currentEntries = $ctx->pg->getRoleHostEntries($daUser, $selectedRole, $selectedDb);
|
||||
}
|
||||
$allHostsEnabled = false;
|
||||
$allHostsComment = '';
|
||||
foreach ($currentEntries as $entry) {
|
||||
if ($entry['host'] === NamePolicy::ALL_IPV4_HOST_PATTERN) {
|
||||
$allHostsEnabled = true;
|
||||
$allHostsComment = $entry['note'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$dbOptions = '';
|
||||
foreach ($dbNames as $dbName) {
|
||||
$sel = ($dbName === $selectedDb) ? ' selected' : '';
|
||||
$dbOptions .= '<option value="' . AppContext::e($dbName) . '"' . $sel . '>' . AppContext::e($dbName) . '</option>';
|
||||
}
|
||||
|
||||
$roleOptions = '<option value="">-- wybierz użytkownika --</option>';
|
||||
foreach ($roleNames as $roleName) {
|
||||
$sel = ($roleName === $selectedRole) ? ' selected' : '';
|
||||
$roleOptions .= '<option value="' . AppContext::e($roleName) . '"' . $sel . '>' . AppContext::e($roleName) . '</option>';
|
||||
}
|
||||
|
||||
$hostRows = '';
|
||||
if (empty($currentEntries)) {
|
||||
$hostRows = '<tr><td colspan="3" class="muted">Brak zapisanych hostów.</td></tr>';
|
||||
} else {
|
||||
foreach ($currentEntries as $entry) {
|
||||
$host = $entry['host'];
|
||||
$note = $entry['note'];
|
||||
|
||||
$hostRows .= '<tr>';
|
||||
$hostRows .= '<td>' . AppContext::e($host) . '</td>';
|
||||
$hostRows .= '<td>' . AppContext::e($note) . '</td>';
|
||||
$hostRows .= '<td><label class="inline"><input type="checkbox" name="remove_hosts[]" value="' . AppContext::e($host) . '"> usuń</label></td>';
|
||||
$hostRows .= '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if ($allHostsEnabled) {
|
||||
$body .= '<div class="alert alert-err" style="border:1px solid #b42318;background:#fff1f0;color:#7a1b13;margin-bottom:12px">';
|
||||
$body .= '<strong>Uwaga:</strong> dla bazy <code>' . AppContext::e($selectedDb) . '</code> i użytkownika <code>' . AppContext::e($selectedRole) . '</code> włączono dostęp z każdego adresu IPv4. Plugin nie otwiera portu PostgreSQL w CSF dla tego trybu. Port PostgreSQL do otwarcia w CSF: <code>' . AppContext::e((string)$postgresPort) . '</code>.';
|
||||
$body .= '</div>';
|
||||
}
|
||||
|
||||
$body .= '<div class="panel">';
|
||||
$body .= '<h3>Status konfiguracji hostów zdalnych</h3>';
|
||||
$body .= '<p class="muted">Hosty zdalne są włączone przez <code>allow_remote_hosts=1</code>. Zapis hostów uruchamia synchronizację <code>pg_hba</code>, <code>postgresql.conf</code> oraz CSF.</p>';
|
||||
if ($lastSyncOutput !== '') {
|
||||
$body .= '<p><strong>Ostatni wynik synchronizacji:</strong></p>';
|
||||
$body .= '<pre class="muted">' . AppContext::e($lastSyncOutput) . '</pre>';
|
||||
} else {
|
||||
$body .= '<p class="muted">Ostatni wynik synchronizacji będzie widoczny po zapisaniu zmian hostów.</p>';
|
||||
}
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= '<form method="post">';
|
||||
$body .= $ctx->csrfField('manage_hosts_submit');
|
||||
|
||||
$body .= '<div class="row">';
|
||||
$body .= '<div><label>Baza danych</label><select name="db_name" required>' . $dbOptions . '</select></div>';
|
||||
$body .= '<div><label>Użytkownik PostgreSQL</label><select name="role_name" required>' . $roleOptions . '</select></div>';
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= '<div class="row">';
|
||||
$body .= '<div><label>Dodaj IPv4/CIDR</label><input type="text" name="add_host" placeholder="198.51.100.12 lub 198.51.100.0/24"></div>';
|
||||
$body .= '<div><label>Komentarz do nowego hosta</label><input type="text" name="add_comment" placeholder="np. serwer aplikacyjny PROD"></div>';
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= '<div class="panel" style="margin-top:12px;border-color:#f0b4a7;background:#fff8f6">';
|
||||
$body .= '<div class="actions" style="justify-content:space-between;align-items:center;margin-top:0">';
|
||||
$body .= '<label class="inline" style="margin:0"><input type="checkbox" name="allow_all_hosts" value="1"' . ($allHostsEnabled ? ' checked' : '') . '> Dostęp dla wszystkich hostów IPv4 (<code>0.0.0.0/0</code>)</label>';
|
||||
$body .= '<button class="danger" type="submit" name="restart_postgresql" value="1">Zapisz i restartuj PostgreSQL</button>';
|
||||
$body .= '</div>';
|
||||
$body .= '<p class="muted">Ta opcja dotyczy tylko wybranej bazy i użytkownika. Plugin skonfiguruje PostgreSQL, ale nie otworzy portu PostgreSQL w CSF. Port PostgreSQL do otwarcia w CSF: <code>' . AppContext::e((string)$postgresPort) . '</code>.</p>';
|
||||
$body .= '<label>Komentarz dla trybu wszystkich hostów</label><input type="text" name="allow_all_comment" value="' . AppContext::e($allHostsComment) . '" placeholder="np. VPS klienta - port ' . AppContext::e((string)$postgresPort) . ' otwierany ręcznie w CSF">';
|
||||
$body .= '</div>';
|
||||
|
||||
$body .= '<div class="panel"><h3>Aktualne hosty dla wybranej bazy</h3>';
|
||||
$body .= '<table><thead><tr><th>Host/CIDR</th><th>Komentarz</th><th>Akcja</th></tr></thead><tbody>' . $hostRows . '</tbody></table>';
|
||||
$body .= '</div>';
|
||||
$body .= '<div class="actions"><button class="primary" type="submit">Zapisz hosty</button></div>';
|
||||
$body .= '</form>';
|
||||
|
||||
$ctx->renderPage('Hosty Zdalne Użytkownika', $body, $ok, $errors);
|
||||
};
|
||||
Reference in New Issue
Block a user