230 lines
7.1 KiB
PHP
230 lines
7.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
final class NamePolicy
|
|
{
|
|
public const MAX_IDENTIFIER_LEN = 63;
|
|
public const MAX_HOST_COMMENT_LEN = 180;
|
|
public const ALL_IPV4_HOST_PATTERN = '0.0.0.0/0';
|
|
|
|
/**
|
|
* @var array<string,string>
|
|
*/
|
|
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<int,string> $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;
|
|
}
|
|
}
|