Import postgresql plugin
This commit is contained in:
@@ -0,0 +1,586 @@
|
||||
<?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();
|
||||
$postgresPort = $ctx->pg->serverPort();
|
||||
$passwordChangedFor = '';
|
||||
$changedPassword = '';
|
||||
$createdRoleName = '';
|
||||
$createdRolePassword = '';
|
||||
$createdRoleAssignedDbs = [];
|
||||
$showCreateUserForm = (strtolower(trim($ctx->queryString('add_user'))) === '1');
|
||||
$deletePromptRoleRaw = trim($ctx->postString('delete_role'));
|
||||
if ($deletePromptRoleRaw === '') {
|
||||
$deletePromptRoleRaw = trim($ctx->queryString('delete_role'));
|
||||
}
|
||||
$deletePromptRole = '';
|
||||
$deletePromptAssignedDbs = [];
|
||||
|
||||
$createRoleSuffixInput = trim($ctx->postString('create_role_name'));
|
||||
if (strpos($createRoleSuffixInput, $prefix) === 0) {
|
||||
$createRoleSuffixInput = substr($createRoleSuffixInput, strlen($prefix));
|
||||
}
|
||||
$createPasswordInput = $ctx->postString('create_password');
|
||||
$createSelectedDbsRaw = $ctx->postArray('create_user_databases');
|
||||
$createSelectedDbs = array_values(array_unique(array_filter($createSelectedDbsRaw, static fn(string $db): bool => $db !== '')));
|
||||
|
||||
$databases = $ctx->pg->listDatabasesForUser($daUser);
|
||||
$allDbs = array_map(static fn(array $db): string => $db['name'], $databases);
|
||||
$roles = $ctx->pg->listRolesForUser($daUser);
|
||||
$roleNames = array_map(static fn(array $r): string => $r['name'], $roles);
|
||||
$preferredRoleRaw = $ctx->postString('role_name');
|
||||
if ($preferredRoleRaw === '') {
|
||||
$preferredRoleRaw = $ctx->queryString('role');
|
||||
}
|
||||
|
||||
if ($ctx->isPost()) {
|
||||
$postAction = $ctx->postString('form_action');
|
||||
try {
|
||||
if ($postAction === 'create_user_inline') {
|
||||
$ctx->requireCsrf('create_user_inline_submit');
|
||||
$showCreateUserForm = true;
|
||||
|
||||
$newRole = NamePolicy::normalizeRoleName($ctx->postString('create_role_name'), $prefix);
|
||||
if ($ctx->pg->roleExists($newRole)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL już istnieje: ' . $newRole);
|
||||
}
|
||||
|
||||
$newPassword = $ctx->postString('create_password');
|
||||
if (strlen($newPassword) < 12) {
|
||||
throw new RuntimeException('Hasło użytkownika musi mieć co najmniej 12 znaków.');
|
||||
}
|
||||
|
||||
$selectedDbs = [];
|
||||
foreach ($createSelectedDbs as $rawDbName) {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($rawDbName, $prefix);
|
||||
if (!in_array($dbName, $allDbs, true)) {
|
||||
throw new RuntimeException('Baza nie należy do konta DA: ' . $dbName);
|
||||
}
|
||||
if (!in_array($dbName, $selectedDbs, true)) {
|
||||
$selectedDbs[] = $dbName;
|
||||
}
|
||||
}
|
||||
|
||||
$ctx->pg->createRole($newRole, $newPassword);
|
||||
try {
|
||||
$ctx->pg->replaceRoleHostsWithNotes($daUser, $newRole, [['host' => 'localhost', 'note' => '']]);
|
||||
|
||||
foreach ($selectedDbs as $dbName) {
|
||||
$ctx->pg->grantPrivileges($dbName, $newRole, NamePolicy::defaultPrivileges());
|
||||
}
|
||||
|
||||
$sync = $ctx->pg->syncHbaRules();
|
||||
if (!$sync['ok']) {
|
||||
$errors[] = 'Użytkownik został utworzony, ale synchronizacja pg_hba nie powiodła się: ' . $sync['output'];
|
||||
}
|
||||
|
||||
$createdRoleName = $newRole;
|
||||
$createdRolePassword = $newPassword;
|
||||
$createdRoleAssignedDbs = $selectedDbs;
|
||||
|
||||
if (empty($selectedDbs)) {
|
||||
$ok[] = 'Utworzono użytkownika ' . $newRole . ' bez przypisanych baz danych.';
|
||||
} else {
|
||||
$ok[] = 'Utworzono użytkownika ' . $newRole . ' i przypisano do ' . count($selectedDbs) . ' baz(y).';
|
||||
}
|
||||
|
||||
$preferredRoleRaw = $newRole;
|
||||
$showCreateUserForm = false;
|
||||
$createRoleSuffixInput = '';
|
||||
$createPasswordInput = '';
|
||||
$createSelectedDbs = [];
|
||||
} catch (Throwable $inner) {
|
||||
try {
|
||||
$ctx->pg->deleteRoleHosts($daUser, $newRole);
|
||||
} catch (Throwable $ignored) {
|
||||
}
|
||||
try {
|
||||
$ctx->pg->dropRole($newRole);
|
||||
} catch (Throwable $ignored) {
|
||||
}
|
||||
throw $inner;
|
||||
}
|
||||
} elseif ($postAction === 'delete_user_confirm') {
|
||||
$ctx->requireCsrf('delete_user_submit');
|
||||
|
||||
$roleToDelete = NamePolicy::normalizeRoleName($ctx->postString('delete_role'), $prefix);
|
||||
if (!in_array($roleToDelete, $roleNames, true)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL nie należy do konta DA: ' . $roleToDelete);
|
||||
}
|
||||
|
||||
$owned = array_values(array_unique($ctx->pg->roleOwnedDatabases($roleToDelete, $daUser)));
|
||||
if (!empty($owned)) {
|
||||
throw new RuntimeException(
|
||||
'Nie można usunąć użytkownika, ponieważ jest właścicielem baz: ' . implode(', ', $owned)
|
||||
);
|
||||
}
|
||||
|
||||
$assigned = array_values(array_unique($ctx->pg->roleAssignedDatabases($roleToDelete, $daUser)));
|
||||
foreach ($assigned as $dbName) {
|
||||
$owner = $ctx->pg->getDatabaseOwner($dbName);
|
||||
if ($owner !== null && $owner !== $roleToDelete) {
|
||||
$ctx->pg->reassignOwnedObjects($dbName, $roleToDelete, $owner);
|
||||
}
|
||||
$ctx->pg->revokePrivileges($dbName, $roleToDelete);
|
||||
}
|
||||
|
||||
$ctx->pg->deleteRoleHosts($daUser, $roleToDelete);
|
||||
$ctx->pg->dropRole($roleToDelete);
|
||||
|
||||
$sync = $ctx->pg->syncHbaRules();
|
||||
if (!$sync['ok']) {
|
||||
$errors[] = 'Usuwanie wykonane, ale synchronizacja pg_hba nie powiodła się: ' . $sync['output'];
|
||||
}
|
||||
|
||||
$ok[] = 'Usunięto użytkownika ' . $roleToDelete . '.';
|
||||
if ($preferredRoleRaw === $roleToDelete) {
|
||||
$preferredRoleRaw = '';
|
||||
}
|
||||
$deletePromptRoleRaw = '';
|
||||
} else {
|
||||
$ctx->requireCsrf('user_manage_submit');
|
||||
if (empty($roleNames)) {
|
||||
throw new RuntimeException('Brak użytkowników PostgreSQL do zarządzania.');
|
||||
}
|
||||
|
||||
$selectedRole = NamePolicy::normalizeRoleName($ctx->postString('role_name', $preferredRoleRaw), $prefix);
|
||||
if (!in_array($selectedRole, $roleNames, true)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL nie należy do konta DA: ' . $selectedRole);
|
||||
}
|
||||
$preferredRoleRaw = $selectedRole;
|
||||
|
||||
if ($postAction === 'change_password') {
|
||||
$password = $ctx->postString('password');
|
||||
if (strlen($password) < 12) {
|
||||
throw new RuntimeException('Nowe hasło musi mieć minimum 12 znaków.');
|
||||
}
|
||||
$ctx->pg->changeRolePassword($selectedRole, $password);
|
||||
$passwordChangedFor = $selectedRole;
|
||||
$changedPassword = $password;
|
||||
} elseif ($postAction === 'grant_db') {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($ctx->postString('db_name'), $prefix);
|
||||
if (!in_array($dbName, $allDbs, true)) {
|
||||
throw new RuntimeException('Baza nie należy do konta DA: ' . $dbName);
|
||||
}
|
||||
$privileges = NamePolicy::sanitizePrivileges($ctx->postArray('privileges'));
|
||||
if (empty($privileges)) {
|
||||
$privileges = NamePolicy::defaultPrivileges();
|
||||
}
|
||||
$ctx->pg->grantPrivileges($dbName, $selectedRole, $privileges);
|
||||
$ok[] = 'Przyznano dostęp do bazy ' . $dbName . ' użytkownikowi ' . $selectedRole . '.';
|
||||
} elseif ($postAction === 'revoke_db') {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($ctx->postString('db_name'), $prefix);
|
||||
$ctx->pg->revokePrivileges($dbName, $selectedRole);
|
||||
$ctx->pg->deleteRoleHosts($daUser, $selectedRole, $dbName);
|
||||
$ok[] = 'Odebrano dostęp do bazy ' . $dbName . ' użytkownikowi ' . $selectedRole . '.';
|
||||
} elseif ($postAction === 'add_host' && $showRemoteHosts) {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($ctx->postString('db_name'), $prefix);
|
||||
if (!in_array($dbName, $allDbs, true)) {
|
||||
throw new RuntimeException('Baza nie należy do konta DA: ' . $dbName);
|
||||
}
|
||||
if (!in_array($selectedRole, $ctx->pg->listDatabaseUsers($daUser, $dbName), true)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL nie ma dostępu do wybranej bazy: ' . $selectedRole);
|
||||
}
|
||||
|
||||
$allowAllHosts = $ctx->postString('allow_all_hosts') === '1';
|
||||
$host = $allowAllHosts
|
||||
? NamePolicy::ALL_IPV4_HOST_PATTERN
|
||||
: NamePolicy::normalizeRemoteIpv4AccessPattern($ctx->postString('host'));
|
||||
$note = NamePolicy::normalizeHostComment($ctx->postString('host_note'));
|
||||
if ($allowAllHosts && $note === '') {
|
||||
$note = 'Dostęp z każdego adresu IPv4';
|
||||
}
|
||||
|
||||
$entries = $ctx->pg->getRoleHostEntries($daUser, $selectedRole, $dbName);
|
||||
$map = [];
|
||||
foreach ($entries as $entry) {
|
||||
$map[$entry['host']] = $entry['note'];
|
||||
}
|
||||
$map[$host] = $note;
|
||||
if (count($map) > $ctx->settings->maxHostsPerUser()) {
|
||||
throw new RuntimeException('Przekroczono limit hostów dla wybranej bazy i użytkownika PostgreSQL.');
|
||||
}
|
||||
$save = [];
|
||||
foreach ($map as $h => $n) {
|
||||
$save[] = ['host' => $h, 'note' => $n];
|
||||
}
|
||||
$ctx->pg->replaceRoleHostsWithNotes($daUser, $selectedRole, $save, $dbName);
|
||||
$restartPostgresql = $ctx->postString('restart_postgresql') === '1';
|
||||
$sync = $ctx->pg->syncHbaRules($restartPostgresql);
|
||||
if (!$sync['ok']) {
|
||||
$errors[] = 'Host dodany, ale synchronizacja pg_hba nie powiodła się: ' . $sync['output'];
|
||||
} elseif ($restartPostgresql) {
|
||||
$ok[] = 'Synchronizacja konfiguracji PostgreSQL zakończona i usługa PostgreSQL została zrestartowana.';
|
||||
}
|
||||
$ok[] = 'Dodano host ' . $host . ' dla użytkownika ' . $selectedRole . ' i bazy ' . $dbName . '.';
|
||||
} elseif ($postAction === 'remove_host' && $showRemoteHosts) {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($ctx->postString('db_name'), $prefix);
|
||||
if (!in_array($dbName, $allDbs, true)) {
|
||||
throw new RuntimeException('Baza nie należy do konta DA: ' . $dbName);
|
||||
}
|
||||
$host = trim($ctx->postString('host'));
|
||||
$entries = $ctx->pg->getRoleHostEntries($daUser, $selectedRole, $dbName);
|
||||
$save = [];
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry['host'] === $host) {
|
||||
continue;
|
||||
}
|
||||
$save[] = $entry;
|
||||
}
|
||||
$ctx->pg->replaceRoleHostsWithNotes($daUser, $selectedRole, $save, $dbName);
|
||||
$sync = $ctx->pg->syncHbaRules();
|
||||
if (!$sync['ok']) {
|
||||
$errors[] = 'Host usunięty, ale synchronizacja pg_hba nie powiodła się: ' . $sync['output'];
|
||||
}
|
||||
$ok[] = 'Usunięto host ' . $host . ' dla użytkownika ' . $selectedRole . ' i bazy ' . $dbName . '.';
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$errors[] = $e->getMessage();
|
||||
if ($postAction === 'create_user_inline') {
|
||||
$showCreateUserForm = true;
|
||||
}
|
||||
if ($postAction === 'delete_user_confirm') {
|
||||
$deletePromptRoleRaw = trim($ctx->postString('delete_role'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$databases = $ctx->pg->listDatabasesForUser($daUser);
|
||||
$allDbs = array_map(static fn(array $db): string => $db['name'], $databases);
|
||||
$roles = $ctx->pg->listRolesForUser($daUser);
|
||||
$roleNames = array_map(static fn(array $r): string => $r['name'], $roles);
|
||||
$selectedRole = '';
|
||||
if (!empty($roleNames)) {
|
||||
$candidateRaw = $preferredRoleRaw !== '' ? $preferredRoleRaw : $roleNames[0];
|
||||
try {
|
||||
$candidate = NamePolicy::normalizeRoleName($candidateRaw, $prefix);
|
||||
} catch (Throwable $e) {
|
||||
$candidate = $roleNames[0];
|
||||
}
|
||||
if (!in_array($candidate, $roleNames, true)) {
|
||||
$candidate = $roleNames[0];
|
||||
}
|
||||
$selectedRole = $candidate;
|
||||
}
|
||||
|
||||
if ($deletePromptRoleRaw !== '') {
|
||||
try {
|
||||
$deletePromptRole = NamePolicy::normalizeRoleName($deletePromptRoleRaw, $prefix);
|
||||
if (!in_array($deletePromptRole, $roleNames, true)) {
|
||||
throw new RuntimeException('Użytkownik PostgreSQL nie należy do konta DA: ' . $deletePromptRole);
|
||||
}
|
||||
$deletePromptAssignedDbs = array_values(array_unique($ctx->pg->roleAssignedDatabases($deletePromptRole, $daUser)));
|
||||
} catch (Throwable $e) {
|
||||
$errors[] = $e->getMessage();
|
||||
$deletePromptRole = '';
|
||||
$deletePromptAssignedDbs = [];
|
||||
}
|
||||
}
|
||||
|
||||
$createSelectedDbSet = [];
|
||||
foreach ($createSelectedDbs as $rawDbName) {
|
||||
try {
|
||||
$dbName = NamePolicy::normalizeDatabaseName($rawDbName, $prefix);
|
||||
} catch (Throwable $e) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($dbName, $allDbs, true)) {
|
||||
$createSelectedDbSet[$dbName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$roleOptions = '';
|
||||
foreach ($roleNames as $roleName) {
|
||||
$sel = ($roleName === $selectedRole) ? ' selected' : '';
|
||||
$roleOptions .= '<option value="' . AppContext::e($roleName) . '"' . $sel . '>' . AppContext::e($roleName) . '</option>';
|
||||
}
|
||||
|
||||
$assignedDbs = [];
|
||||
$grantableDbs = $allDbs;
|
||||
if ($selectedRole !== '') {
|
||||
$assignedDbs = $ctx->pg->roleAssignedDatabases($selectedRole, $daUser);
|
||||
$grantableDbs = array_values(array_diff($allDbs, $assignedDbs));
|
||||
}
|
||||
|
||||
$dbOptions = '<option value="">-- wybierz bazę danych --</option>';
|
||||
foreach ($grantableDbs as $dbName) {
|
||||
$dbOptions .= '<option value="' . AppContext::e($dbName) . '">' . AppContext::e($dbName) . '</option>';
|
||||
}
|
||||
|
||||
$assignedDbOptions = '';
|
||||
foreach ($assignedDbs as $dbName) {
|
||||
$assignedDbOptions .= '<option value="' . AppContext::e($dbName) . '">' . AppContext::e($dbName) . '</option>';
|
||||
}
|
||||
|
||||
$dbRows = '';
|
||||
foreach ($assignedDbs as $dbName) {
|
||||
$profile = $ctx->pg->getGrantProfile($dbName, $selectedRole);
|
||||
$label = (empty($profile) || in_array('ALL', $profile, true))
|
||||
? 'Pełny dostęp'
|
||||
: implode(', ', $profile);
|
||||
|
||||
$dbRows .= '<tr>';
|
||||
$dbRows .= '<td>' . AppContext::e($dbName) . '</td>';
|
||||
$dbRows .= '<td><span class="badge">' . AppContext::e($label) . '</span></td>';
|
||||
$dbRows .= '<td class="actions">';
|
||||
$dbRows .= '<a class="btn" href="' . AppContext::e($ctx->url('modify_privileges.html', ['db' => $dbName, 'role' => $selectedRole])) . '">Zarządzaj</a>';
|
||||
$dbRows .= '<a class="btn" href="' . AppContext::e($ctx->url('modify_privileges.html', ['db' => $dbName, 'role' => $selectedRole])) . '#przywileje">Przywileje</a>';
|
||||
$dbRows .= '<form method="post" style="display:inline-flex;gap:6px;margin:0">';
|
||||
$dbRows .= $ctx->csrfField('user_manage_submit');
|
||||
$dbRows .= '<input type="hidden" name="role_name" value="' . AppContext::e($selectedRole) . '">';
|
||||
$dbRows .= '<input type="hidden" name="db_name" value="' . AppContext::e($dbName) . '">';
|
||||
$dbRows .= '<input type="hidden" name="form_action" value="revoke_db">';
|
||||
$dbRows .= '<button class="btn danger" type="submit">Odbierz dostęp</button>';
|
||||
$dbRows .= '</form>';
|
||||
$dbRows .= '</td>';
|
||||
$dbRows .= '</tr>';
|
||||
}
|
||||
if ($dbRows === '') {
|
||||
$dbRows = '<tr><td colspan="3" class="muted">Brak przypisanych baz danych.</td></tr>';
|
||||
}
|
||||
|
||||
$hostsRows = '';
|
||||
$hostsCount = 0;
|
||||
$hasAllHostsEntry = false;
|
||||
if ($showRemoteHosts && $selectedRole !== '') {
|
||||
foreach ($assignedDbs as $dbName) {
|
||||
$hostEntries = $ctx->pg->getRoleHostEntries($daUser, $selectedRole, $dbName);
|
||||
$hostsCount += count($hostEntries);
|
||||
foreach ($hostEntries as $entry) {
|
||||
$host = $entry['host'];
|
||||
$note = $entry['note'];
|
||||
if ($host === NamePolicy::ALL_IPV4_HOST_PATTERN) {
|
||||
$hasAllHostsEntry = true;
|
||||
}
|
||||
$hostsRows .= '<tr>';
|
||||
$hostsRows .= '<td>' . AppContext::e($dbName) . '</td>';
|
||||
$hostsRows .= '<td>' . AppContext::e($host) . '</td>';
|
||||
$hostsRows .= '<td>' . AppContext::e($note) . '</td>';
|
||||
$hostsRows .= '<td class="actions">';
|
||||
$hostsRows .= '<form method="post" style="display:inline-flex;gap:6px;margin:0">';
|
||||
$hostsRows .= $ctx->csrfField('user_manage_submit');
|
||||
$hostsRows .= '<input type="hidden" name="role_name" value="' . AppContext::e($selectedRole) . '">';
|
||||
$hostsRows .= '<input type="hidden" name="db_name" value="' . AppContext::e($dbName) . '">';
|
||||
$hostsRows .= '<input type="hidden" name="host" value="' . AppContext::e($host) . '">';
|
||||
$hostsRows .= '<input type="hidden" name="form_action" value="remove_host">';
|
||||
$hostsRows .= '<button class="btn danger" type="submit">Usuń</button>';
|
||||
$hostsRows .= '</form>';
|
||||
$hostsRows .= '</td>';
|
||||
$hostsRows .= '</tr>';
|
||||
}
|
||||
}
|
||||
if ($hostsRows === '') {
|
||||
$hostsRows = '<tr><td colspan="4" class="muted">Brak dodatkowych hostów dla przypisanych baz.</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
$endpoint = $ctx->pg->connectionEndpoint();
|
||||
$endpointHost = trim($endpoint['host']) !== '' ? $endpoint['host'] : 'localhost';
|
||||
$endpointPort = $endpoint['port'] > 0 ? (string)$endpoint['port'] : '5432';
|
||||
$createdUserCard = '';
|
||||
if ($createdRoleName !== '' && $createdRolePassword !== '') {
|
||||
$assignedDbsHtml = '';
|
||||
if (!empty($createdRoleAssignedDbs)) {
|
||||
$assignedItems = '';
|
||||
foreach ($createdRoleAssignedDbs as $assignedDbName) {
|
||||
$assignedItems .= '<li><code>' . AppContext::e($assignedDbName) . '</code></li>';
|
||||
}
|
||||
$assignedDbsHtml .= '<div><strong>Przypisane bazy danych:</strong></div>';
|
||||
$assignedDbsHtml .= '<div><ul class="list-clean">' . $assignedItems . '</ul></div>';
|
||||
}
|
||||
|
||||
$createdUserCard .= '<section class="success-credentials">';
|
||||
$createdUserCard .= '<div class="left">✓</div>';
|
||||
$createdUserCard .= '<div class="right">';
|
||||
$createdUserCard .= '<h4>Dane dostępowe użytkownika PostgreSQL</h4>';
|
||||
$createdUserCard .= '<div class="kv">';
|
||||
$createdUserCard .= '<div><strong>Nazwa hosta:</strong></div><div><code>' . AppContext::e($endpointHost) . ' (Port: ' . AppContext::e($endpointPort) . ')</code></div>';
|
||||
$createdUserCard .= '<div><strong>Nazwa użytkownika:</strong></div><div><code>' . AppContext::e($createdRoleName) . '</code></div>';
|
||||
$createdUserCard .= '<div><strong>Hasło:</strong></div><div><code>' . AppContext::e($createdRolePassword) . '</code></div>';
|
||||
$createdUserCard .= $assignedDbsHtml;
|
||||
$createdUserCard .= '</div></div></section>';
|
||||
}
|
||||
|
||||
$passwordChangedCard = '';
|
||||
if ($passwordChangedFor !== '' && $changedPassword !== '') {
|
||||
$passwordChangedCard .= '<section class="success-credentials">';
|
||||
$passwordChangedCard .= '<div class="left">✓</div>';
|
||||
$passwordChangedCard .= '<div class="right">';
|
||||
$passwordChangedCard .= '<h4>Hasło zostało zmienione</h4>';
|
||||
$passwordChangedCard .= '<div class="kv">';
|
||||
$passwordChangedCard .= '<div><strong>Nazwa hosta:</strong></div><div><code>' . AppContext::e($endpointHost) . ' (Port: ' . AppContext::e($endpointPort) . ')</code></div>';
|
||||
$passwordChangedCard .= '<div><strong>Nazwa użytkownika:</strong></div><div><code>' . AppContext::e($passwordChangedFor) . '</code></div>';
|
||||
$passwordChangedCard .= '<div><strong>Hasło:</strong></div><div><code>' . AppContext::e($changedPassword) . '</code></div>';
|
||||
$passwordChangedCard .= '</div></div></section>';
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$body .= $createdUserCard;
|
||||
$body .= $passwordChangedCard;
|
||||
$body .= '<section class="panel">';
|
||||
$body .= '<h3>Zarządzaj użytkownikami</h3>';
|
||||
$body .= '<div class="actions" style="justify-content:space-between;align-items:center">';
|
||||
$body .= '<div class="actions" style="margin-top:0">';
|
||||
$body .= '<a class="btn primary" href="' . AppContext::e($ctx->url('change_password.html', ['add_user' => 1])) . '#add-user-form">Dodaj użytkownika</a>';
|
||||
if ($showCreateUserForm) {
|
||||
$body .= '<a class="btn" href="' . AppContext::e($ctx->url('change_password.html')) . '">Anuluj</a>';
|
||||
}
|
||||
$body .= '</div>';
|
||||
if ($selectedRole !== '') {
|
||||
$body .= '<div class="actions" style="margin-top:0">';
|
||||
$body .= '<form method="get" action="' . AppContext::e($ctx->url('change_password.html')) . '" class="actions" style="margin-top:0">';
|
||||
$body .= '<label style="margin:0">Użytkownik: <select name="role" style="width:auto;display:inline-block;margin-left:8px">' . $roleOptions . '</select></label>';
|
||||
$body .= '<button class="btn" type="submit">Przełącz</button>';
|
||||
$body .= '</form>';
|
||||
$body .= '<a class="btn danger-fill" href="' . AppContext::e($ctx->url('change_password.html', ['role' => $selectedRole, 'delete_role' => $selectedRole])) . '">Usuń użytkownika</a>';
|
||||
$body .= '</div>';
|
||||
} else {
|
||||
$body .= '<span class="muted">Brak użytkowników PostgreSQL. Dodaj pierwszego użytkownika.</span>';
|
||||
}
|
||||
$body .= '</div>';
|
||||
$body .= '<p class="section-text">Szczegółowe informacje o użytkowniku.</p>';
|
||||
if ($selectedRole !== '') {
|
||||
$body .= '<table><tbody>';
|
||||
$body .= '<tr><td><strong>Nazwa użytkownika</strong><br>' . AppContext::e($selectedRole) . '</td><td><strong>Bazy danych</strong><br>' . AppContext::e((string)count($assignedDbs)) . '</td>';
|
||||
if ($showRemoteHosts) {
|
||||
$body .= '<td><strong>Dozwolone hosty</strong><br>' . AppContext::e((string)$hostsCount) . '</td>';
|
||||
} else {
|
||||
$body .= '<td><strong>Dozwolone hosty</strong><br>localhost</td>';
|
||||
}
|
||||
$body .= '</tr></tbody></table>';
|
||||
} else {
|
||||
$body .= '<p class="muted">Aby zarządzać użytkownikami najpierw utwórz użytkownika PostgreSQL.</p>';
|
||||
}
|
||||
$body .= '</section>';
|
||||
|
||||
if ($deletePromptRole !== '') {
|
||||
$cancelUrl = $ctx->url('change_password.html', $selectedRole !== '' ? ['role' => $selectedRole] : []);
|
||||
$body .= '<section class="panel" style="margin-bottom:14px;border-color:#f0b4a7;background:#fff5f3">';
|
||||
$body .= '<div class="alert alert-err" style="margin:0 0 10px">Czy na pewno chcesz usunąć użytkownika <strong>' . AppContext::e($deletePromptRole) . '</strong>?</div>';
|
||||
|
||||
if (count($deletePromptAssignedDbs) === 1) {
|
||||
$body .= '<div class="alert alert-err" style="margin:0 0 10px">Uwaga użytkownik <strong>' . AppContext::e($deletePromptRole) . '</strong> jest przypisany do następującej bazy danych <strong>' . AppContext::e($deletePromptAssignedDbs[0]) . '</strong>.</div>';
|
||||
} elseif (count($deletePromptAssignedDbs) > 1) {
|
||||
$body .= '<div class="alert alert-err" style="margin:0 0 10px">Uwaga użytkownik <strong>' . AppContext::e($deletePromptRole) . '</strong> jest przypisany do następujących baz danych:</div>';
|
||||
$body .= '<ul class="list-clean" style="margin:0 0 12px 18px">';
|
||||
foreach ($deletePromptAssignedDbs as $assignedDbName) {
|
||||
$body .= '<li>' . AppContext::e($assignedDbName) . '</li>';
|
||||
}
|
||||
$body .= '</ul>';
|
||||
}
|
||||
|
||||
$body .= '<form method="post">';
|
||||
$body .= $ctx->csrfField('delete_user_submit');
|
||||
$body .= '<input type="hidden" name="form_action" value="delete_user_confirm">';
|
||||
$body .= '<input type="hidden" name="delete_role" value="' . AppContext::e($deletePromptRole) . '">';
|
||||
$body .= '<div class="actions">';
|
||||
$body .= '<button class="danger-fill" type="submit">Potwierdź usunięcie użytkownika</button>';
|
||||
$body .= '<a class="btn" href="' . AppContext::e($cancelUrl) . '">Anuluj</a>';
|
||||
$body .= '</div>';
|
||||
$body .= '</form>';
|
||||
$body .= '</section>';
|
||||
}
|
||||
|
||||
if ($showCreateUserForm) {
|
||||
$body .= '<section class="panel" id="add-user-form" style="margin-top:14px">';
|
||||
$body .= '<h3>Utwórz nowego użytkownika PostgreSQL</h3>';
|
||||
$body .= '<form method="post">';
|
||||
$body .= $ctx->csrfField('create_user_inline_submit');
|
||||
$body .= '<input type="hidden" name="form_action" value="create_user_inline">';
|
||||
$body .= '<div class="row">';
|
||||
$body .= '<div><label>Nazwa użytkownika PostgreSQL</label><div class="input-prefix"><span class="prefix">' . AppContext::e($prefix) . '</span><input type="text" name="create_role_name" value="' . AppContext::e($createRoleSuffixInput) . '" autocomplete="off" required></div></div>';
|
||||
$body .= '<div><label>Hasło użytkownika</label>';
|
||||
$body .= '<div class="input-with-tools"><input type="password" id="create-role-password" name="create_password" value="' . AppContext::e($createPasswordInput) . '" autocomplete="new-password" required>';
|
||||
$body .= '<span class="field-tools">';
|
||||
$body .= '<button type="button" class="tool-btn" data-generate-target="create-role-password" title="Generuj hasło" aria-label="Generuj hasło"><svg viewBox="0 0 24 24"><path d="M20 7v5h-5"></path><path d="M20 12a8 8 0 1 1-2.34-5.66L20 7"></path></svg></button>';
|
||||
$body .= '<button type="button" class="tool-btn" data-toggle-target="create-role-password" title="Pokaż lub ukryj hasło" aria-label="Pokaż lub ukryj hasło"><svg viewBox="0 0 24 24"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z"></path><circle cx="12" cy="12" r="3"></circle></svg></button>';
|
||||
$body .= '</span></div>';
|
||||
$body .= '</div>';
|
||||
$body .= '</div>';
|
||||
$body .= '<details class="details-advanced">';
|
||||
$body .= '<summary>Przypisz do baz danych (opcjonalnie)</summary>';
|
||||
if (empty($allDbs)) {
|
||||
$body .= '<p class="muted">Brak baz do przypisania.</p>';
|
||||
} else {
|
||||
$body .= '<div class="db-choice-grid">';
|
||||
foreach ($allDbs as $dbName) {
|
||||
$checked = isset($createSelectedDbSet[$dbName]) ? ' checked' : '';
|
||||
$body .= '<label class="db-choice-card"><input type="checkbox" name="create_user_databases[]" value="' . AppContext::e($dbName) . '"' . $checked . '><span>' . AppContext::e($dbName) . '</span></label>';
|
||||
}
|
||||
$body .= '</div>';
|
||||
}
|
||||
$body .= '<p class="muted" style="margin-top:8px">Możesz pozostawić użytkownika bez przypisania do baz i nadać dostęp później.</p>';
|
||||
$body .= '</details>';
|
||||
$body .= '<div class="actions"><button class="primary" type="submit">Utwórz użytkownika</button></div>';
|
||||
$body .= '</form>';
|
||||
$body .= '</section>';
|
||||
}
|
||||
|
||||
if ($selectedRole !== '') {
|
||||
$body .= '<section class="panel" style="margin-top:14px">';
|
||||
$body .= '<h3>Zarządzanie hasłami</h3>';
|
||||
$body .= '<form method="post">';
|
||||
$body .= $ctx->csrfField('user_manage_submit');
|
||||
$body .= '<input type="hidden" name="role_name" value="' . AppContext::e($selectedRole) . '">';
|
||||
$body .= '<input type="hidden" name="form_action" value="change_password">';
|
||||
$body .= '<label>Nowe hasło</label>';
|
||||
$body .= '<div class="input-with-tools"><input type="password" id="change-role-password" name="password" placeholder="Wpisz nowe hasło" autocomplete="new-password" required>';
|
||||
$body .= '<span class="field-tools">';
|
||||
$body .= '<button type="button" class="tool-btn" data-generate-target="change-role-password" title="Generuj hasło" aria-label="Generuj hasło"><svg viewBox="0 0 24 24"><path d="M20 7v5h-5"></path><path d="M20 12a8 8 0 1 1-2.34-5.66L20 7"></path></svg></button>';
|
||||
$body .= '<button type="button" class="tool-btn" data-toggle-target="change-role-password" title="Pokaż lub ukryj hasło" aria-label="Pokaż lub ukryj hasło"><svg viewBox="0 0 24 24"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6z"></path><circle cx="12" cy="12" r="3"></circle></svg></button>';
|
||||
$body .= '</span></div>';
|
||||
$body .= '<div class="actions"><button class="primary" type="submit">Zmień hasło</button></div>';
|
||||
$body .= '</form>';
|
||||
$body .= '</section>';
|
||||
|
||||
$body .= '<section class="panel" style="margin-top:14px">';
|
||||
$body .= '<h3>Dostęp do baz danych</h3>';
|
||||
$body .= '<table><thead><tr><th>Nazwa bazy danych</th><th>Przywileje</th><th>Akcje</th></tr></thead><tbody>' . $dbRows . '</tbody></table>';
|
||||
$body .= '<form method="post" style="margin-top:10px">';
|
||||
$body .= $ctx->csrfField('user_manage_submit');
|
||||
$body .= '<input type="hidden" name="role_name" value="' . AppContext::e($selectedRole) . '">';
|
||||
$body .= '<input type="hidden" name="form_action" value="grant_db">';
|
||||
$body .= '<label>Przyznaj dostęp do kolejnej bazy danych</label>';
|
||||
$body .= '<select name="db_name">' . $dbOptions . '</select>';
|
||||
$body .= '<input type="hidden" name="privileges[]" value="ALL">';
|
||||
$body .= '<div class="actions"><button class="btn" type="submit">+ Przyznaj pełny dostęp</button></div>';
|
||||
$body .= '</form>';
|
||||
$body .= '</section>';
|
||||
|
||||
if ($showRemoteHosts) {
|
||||
$body .= '<section class="panel" style="margin-top:14px">';
|
||||
$body .= '<h3>Dozwolone hosty</h3>';
|
||||
if ($hasAllHostsEntry) {
|
||||
$body .= '<div class="alert alert-err" style="border:1px solid #b42318;background:#fff1f0;color:#7a1b13">Dla co najmniej jednej bazy 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>.</div>';
|
||||
}
|
||||
$body .= '<p class="section-text">Lista źródłowych adresów IP/CIDR, które mogą łączyć się z konkretną bazą przy użyciu poświadczeń użytkownika.</p>';
|
||||
$body .= '<table><thead><tr><th>Baza</th><th>Host/CIDR</th><th>Komentarz</th><th>Akcje</th></tr></thead><tbody>' . $hostsRows . '</tbody></table>';
|
||||
$body .= '<form method="post" style="margin-top:10px">';
|
||||
$body .= $ctx->csrfField('user_manage_submit');
|
||||
$body .= '<input type="hidden" name="role_name" value="' . AppContext::e($selectedRole) . '">';
|
||||
$body .= '<input type="hidden" name="form_action" value="add_host">';
|
||||
$body .= '<div class="row">';
|
||||
$body .= '<div><label>Baza danych</label><select name="db_name">' . $assignedDbOptions . '</select></div>';
|
||||
$body .= '<div><label>Zezwalaj na dostęp z IPv4/CIDR</label><input type="text" name="host" placeholder="203.0.113.10 lub 203.0.113.0/24"></div>';
|
||||
$body .= '<div><label>Komentarz</label><input type="text" name="host_note" placeholder="np. biuro / serwer aplikacji"></div>';
|
||||
$body .= '</div>';
|
||||
$body .= '<div class="actions" style="justify-content:space-between;align-items:center;margin-top:10px">';
|
||||
$body .= '<label class="inline" style="margin:0"><input type="checkbox" name="allow_all_hosts" value="1"> 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">Ten checkbox ignoruje pole IPv4/CIDR i wymaga ręcznego otwarcia portu PostgreSQL w CSF. Port PostgreSQL do otwarcia w CSF: <code>' . AppContext::e((string)$postgresPort) . '</code>.</p>';
|
||||
$body .= '<div class="actions"><button class="btn" type="submit">+ Dodaj host</button></div>';
|
||||
$body .= '</form>';
|
||||
$body .= '</section>';
|
||||
}
|
||||
}
|
||||
|
||||
$ctx->renderPage('Zarządzaj użytkownikami', $body, $ok, $errors);
|
||||
};
|
||||
Reference in New Issue
Block a user