*/ public const PRIVILEGE_LABELS = [ 'ALL' => 'Wszystkie uprawnienia', 'CONNECT' => 'CONNECT', 'CREATE' => 'CREATE (baza/schemat)', 'TEMPORARY' => 'TEMPORARY', 'SCHEMA_USAGE' => 'USAGE (schema public)', 'TABLE_SELECT' => 'SELECT (tabele)', 'TABLE_INSERT' => 'INSERT (tabele)', 'TABLE_UPDATE' => 'UPDATE (tabele)', 'TABLE_DELETE' => 'DELETE (tabele)', 'TABLE_TRUNCATE' => 'TRUNCATE (tabele)', 'TABLE_REFERENCES' => 'REFERENCES (tabele)', 'TABLE_TRIGGER' => 'TRIGGER (tabele)', 'SEQUENCE_USAGE' => 'USAGE (sekwencje)', 'SEQUENCE_SELECT' => 'SELECT (sekwencje)', 'SEQUENCE_UPDATE' => 'UPDATE (sekwencje)', 'FUNCTION_EXECUTE' => 'EXECUTE (funkcje)', ]; public static function defaultPrivileges(): array { return ['ALL']; } public static function normalizeDatabaseName(string $input, string $prefix): string { return self::normalizeIdentifier($input, $prefix, 'Nazwa bazy'); } public static function normalizeRoleName(string $input, string $prefix): string { return self::normalizeIdentifier($input, $prefix, 'Nazwa użytkownika'); } public static function assertBelongsToUser(string $name, string $prefix, string $entityLabel = 'Obiekt'): void { if (strpos($name, $prefix) !== 0) { throw new InvalidArgumentException($entityLabel . ' musi zaczynać się od prefiksu: ' . $prefix); } } public static function normalizeIdentifier(string $input, string $prefix, string $label): string { $value = strtolower(trim($input)); if ($value === '') { throw new InvalidArgumentException($label . ' jest wymagana.'); } if (!preg_match('/^[a-z0-9_]+$/', $value)) { throw new InvalidArgumentException($label . ' może zawierać wyłącznie znaki a-z, 0-9 oraz _.'); } if (strpos($value, $prefix) !== 0) { $value = $prefix . $value; } if (strpos($value, $prefix) !== 0) { throw new InvalidArgumentException($label . ' musi zaczynać się od prefiksu: ' . $prefix); } if (strlen($value) > self::MAX_IDENTIFIER_LEN) { throw new InvalidArgumentException($label . ' przekracza limit 63 znaków PostgreSQL.'); } if ($value === $prefix) { throw new InvalidArgumentException($label . ' nie może być równa samemu prefiksowi.'); } return $value; } /** * @return string[] */ public static function parseHosts(string $raw): array { $raw = str_replace(["\r\n", "\r", ';'], ["\n", "\n", "\n"], $raw); $raw = str_replace(',', "\n", $raw); $parts = explode("\n", $raw); $hosts = []; foreach ($parts as $part) { $part = trim($part); if ($part === '') { continue; } $hosts[] = $part; } return array_values(array_unique($hosts)); } public static function normalizeHost(string $input, bool $allowWildcard): string { $host = strtolower(trim($input)); if ($host === '') { throw new InvalidArgumentException('Pusty host.'); } if (in_array($host, ['localhost', '127.0.0.1', '::1'], true)) { return $host; } if (self::isStrictIpv4($host)) { return $host; } throw new InvalidArgumentException('Nieobsługiwany format hosta: ' . $input); } public static function normalizeRemoteIpv4Host(string $input): string { $host = trim($input); if ($host === '') { throw new InvalidArgumentException('Adres IPv4 jest wymagany.'); } if (!self::isStrictIpv4($host)) { throw new InvalidArgumentException('Dozwolone są tylko pojedyncze adresy IPv4, np. 203.0.113.10.'); } return $host; } public static function normalizeRemoteIpv4AccessPattern(string $input): string { $host = trim(strtolower($input)); if ($host === '') { throw new InvalidArgumentException('Adres IPv4 albo CIDR jest wymagany.'); } if (self::isStrictIpv4($host)) { return $host; } if (preg_match('#^([^/]+)/([0-9]{1,2})$#', $host, $m)) { $ip = $m[1]; $prefix = (int)$m[2]; if (!self::isStrictIpv4($ip) || $prefix < 0 || $prefix > 32) { throw new InvalidArgumentException('Nieprawidłowy CIDR IPv4: ' . $input); } return self::normalizeIpv4Cidr($ip, $prefix); } throw new InvalidArgumentException('Dozwolone są pojedyncze adresy IPv4 albo CIDR, np. 203.0.113.10 lub 203.0.113.0/24.'); } public static function normalizeHostComment(string $input): string { $comment = trim(preg_replace('/[\r\n\t]+/', ' ', $input) ?? ''); $comment = preg_replace('/\s+/', ' ', $comment) ?? ''; $comment = trim($comment); if (strlen($comment) > self::MAX_HOST_COMMENT_LEN) { throw new InvalidArgumentException('Komentarz hosta przekracza limit ' . self::MAX_HOST_COMMENT_LEN . ' znaków.'); } return $comment; } /** * @param array $input * @return string[] */ public static function sanitizePrivileges(array $input): array { $allowed = array_keys(self::PRIVILEGE_LABELS); $result = []; foreach ($input as $priv) { $priv = strtoupper(trim((string)$priv)); if ($priv === '') { continue; } if (!in_array($priv, $allowed, true)) { continue; } $result[] = $priv; } $result = array_values(array_unique($result)); if (in_array('ALL', $result, true)) { return ['ALL']; } return $result; } private static function isStrictIpv4(string $value): bool { if (!preg_match('/^(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}$/', $value)) { return false; } return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false; } private static function normalizeIpv4Cidr(string $ip, int $prefix): string { $long = ip2long($ip); if ($long === false) { throw new InvalidArgumentException('Nieprawidłowy adres IPv4: ' . $ip); } $unsigned = (int)sprintf('%u', $long); $mask = $prefix === 0 ? 0 : ((0xFFFFFFFF << (32 - $prefix)) & 0xFFFFFFFF); $network = $unsigned & $mask; $networkIp = long2ip($network); if ($networkIp === false) { throw new InvalidArgumentException('Nieprawidłowy CIDR IPv4: ' . $ip . '/' . $prefix); } return $networkIp . '/' . $prefix; } }