Import postgresql plugin

This commit is contained in:
Marek Miklewicz
2026-07-04 16:00:04 +02:00
commit ddc971567f
74 changed files with 18735 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
return static function (AppContext $ctx): void {
$ok = [];
$errors = [];
$daUser = $ctx->daUser->username();
$prefix = $ctx->daUser->prefix();
$showRemoteHosts = $ctx->settings->allowRemoteHosts();
$databases = $ctx->pg->listDatabasesForUser($daUser);
$dbNames = array_map(static fn(array $db): string => $db['name'], $databases);
if ($ctx->isPost()) {
try {
$ctx->requireCsrf('create_user_submit');
$role = NamePolicy::normalizeRoleName($ctx->postString('role_name'), $prefix);
if ($ctx->pg->roleExists($role)) {
throw new RuntimeException('Użytkownik PostgreSQL już istnieje: ' . $role);
}
$password = $ctx->postString('password');
if (strlen($password) < 12) {
throw new RuntimeException('Hasło użytkownika musi mieć co najmniej 12 znaków.');
}
$selectedDbsRaw = $ctx->postArray('databases');
$selectedDbs = [];
foreach ($selectedDbsRaw as $db) {
$normalizedDb = NamePolicy::normalizeDatabaseName($db, $prefix);
if (!in_array($normalizedDb, $dbNames, true)) {
throw new RuntimeException('Nieprawidłowa baza do przypisania: ' . $normalizedDb);
}
$selectedDbs[] = $normalizedDb;
}
$selectedDbs = array_values(array_unique($selectedDbs));
$privileges = NamePolicy::sanitizePrivileges($ctx->postArray('privileges'));
if (empty($privileges)) {
$privileges = NamePolicy::defaultPrivileges();
}
$globalHostEntries = [['host' => 'localhost', 'note' => '']];
$remoteHostEntry = null;
if ($showRemoteHosts) {
$remoteHostRaw = trim($ctx->postString('remote_host'));
if ($remoteHostRaw !== '') {
$remoteHost = NamePolicy::normalizeRemoteIpv4AccessPattern($remoteHostRaw);
$remoteComment = NamePolicy::normalizeHostComment($ctx->postString('remote_comment'));
$remoteHostEntry = ['host' => $remoteHost, 'note' => $remoteComment];
}
}
if ($remoteHostEntry !== null && empty($selectedDbs)) {
throw new RuntimeException('Host zdalny można dodać tylko dla wybranej bazy.');
}
$ctx->pg->createRole($role, $password);
try {
$ctx->pg->replaceRoleHostsWithNotes($daUser, $role, $globalHostEntries);
foreach ($selectedDbs as $dbName) {
$ctx->pg->grantPrivileges($dbName, $role, $privileges);
if ($remoteHostEntry !== null) {
$ctx->pg->replaceRoleHostsWithNotes($daUser, $role, [$remoteHostEntry], $dbName);
}
}
$sync = $ctx->pg->syncHbaRules();
if (!$sync['ok']) {
$errors[] = 'Użytkownik został utworzony, ale synchronizacja pg_hba nie powiodła się: ' . $sync['output'];
}
$ok[] = 'Utworzono użytkownika ' . $role . ' i przypisano ' . count($selectedDbs) . ' baz(y).';
} catch (Throwable $inner) {
try {
foreach ($selectedDbs as $dbName) {
$ctx->pg->revokePrivileges($dbName, $role);
}
$ctx->pg->deleteRoleHosts($daUser, $role);
$ctx->pg->dropRole($role);
} catch (Throwable $ignored) {
}
throw $inner;
}
} catch (Throwable $e) {
$errors[] = $e->getMessage();
}
}
$dbCheckboxes = '';
foreach ($dbNames as $dbName) {
$dbCheckboxes .= '<label class="inline"><input type="checkbox" name="databases[]" value="' . AppContext::e($dbName) . '"> ' . AppContext::e($dbName) . '</label>';
}
if ($dbCheckboxes === '') {
$dbCheckboxes = '<p class="muted">Brak baz do przypisania.</p>';
}
$privCheckboxes = '';
foreach (NamePolicy::PRIVILEGE_LABELS as $code => $label) {
$checked = ($code === 'ALL') ? ' checked' : '';
$privCheckboxes .= '<label class="inline"><input type="checkbox" name="privileges[]" value="' . AppContext::e($code) . '"' . $checked . '> ' . AppContext::e($label) . '</label>';
}
$remoteFields = '';
if ($showRemoteHosts) {
$remoteFields .= '<div class="row">';
$remoteFields .= '<div><label>Dodatkowy host IPv4/CIDR</label><input type="text" name="remote_host" placeholder="203.0.113.10 lub 203.0.113.0/24"></div>';
$remoteFields .= '<div><label>Komentarz hosta</label><input type="text" name="remote_comment" placeholder="np. Laptop administratora"></div>';
$remoteFields .= '</div>';
} else {
$remoteFields .= '<p class="muted">Hosty zdalne są wyłączone w konfiguracji pluginu (`allow_remote_hosts=0`).</p>';
}
$body = '';
$body .= '<form method="post">';
$body .= $ctx->csrfField('create_user_submit');
$body .= '<div class="row">';
$body .= '<div><label>Nazwa użytkownika PostgreSQL</label><input type="text" name="role_name" placeholder="' . AppContext::e($prefix) . 'app_user" required></div>';
$body .= '<div><label>Hasło</label><input type="password" name="password" placeholder="min. 12 znaków" required></div>';
$body .= '</div>';
$body .= '<div class="row">';
$body .= '<div><label>Domyślny dostęp</label><p class="muted">`localhost` jest dodawany automatycznie.</p></div>';
$body .= '<div><label>Przypisz do baz</label><div>' . $dbCheckboxes . '</div></div>';
$body .= '</div>';
$body .= $remoteFields;
$body .= '<div class="panel"><h3>Uprawnienia dla przypisywanych baz</h3><div class="privileges-group" data-privileges-group>' . $privCheckboxes . '</div></div>';
$body .= '<div class="actions"><button class="primary" type="submit">Utwórz użytkownika</button></div>';
$body .= '</form>';
$ctx->renderPage('Tworzenie Użytkownika PostgreSQL', $body, $ok, $errors);
};