coolify/app/Rules/ValidIpOrCidr.php
Andras Bacsai 91f538e171 fix(server): handle limit edge case and IPv6 allowlist dedupe
Update server limit enforcement to re-enable force-disabled servers when the
team is at or under its limit (`<= 0` condition).

Improve allowlist validation and matching by:
- supporting IPv6 CIDR mask ranges up to `/128`
- adding IPv6-aware CIDR matching in `checkIPAgainstAllowlist`
- normalizing/deduplicating redundant allowlist entries before saving

Add feature tests for `ServerLimitCheckJob` covering under-limit, at-limit,
over-limit, and no-op scenarios.
2026-03-03 17:03:46 +01:00

66 lines
1.8 KiB
PHP

<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class ValidIpOrCidr implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (empty($value)) {
// Empty is allowed (means allow from anywhere)
return;
}
// Special case: 0.0.0.0 is allowed
if ($value === '0.0.0.0') {
return;
}
$entries = explode(',', $value);
$invalidEntries = [];
foreach ($entries as $entry) {
$entry = trim($entry);
if (empty($entry)) {
continue;
}
// Special case: 0.0.0.0 with or without subnet
if (str_starts_with($entry, '0.0.0.0')) {
continue;
}
// Check if it's CIDR notation
if (str_contains($entry, '/')) {
$parts = explode('/', $entry);
if (count($parts) !== 2) {
$invalidEntries[] = $entry;
continue;
}
[$ip, $mask] = $parts;
$isIpv6 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
$maxMask = $isIpv6 ? 128 : 32;
if (! filter_var($ip, FILTER_VALIDATE_IP) || ! is_numeric($mask) || $mask < 0 || $mask > $maxMask) {
$invalidEntries[] = $entry;
}
} else {
// Check if it's a valid IP
if (! filter_var($entry, FILTER_VALIDATE_IP)) {
$invalidEntries[] = $entry;
}
}
}
if (! empty($invalidEntries)) {
$fail('The following entries are not valid IP addresses or CIDR notations: '.implode(', ', $invalidEntries));
}
}
}