From cd6bb56bb621aa16f56ac6faa1f64e778437e3fa Mon Sep 17 00:00:00 2001 From: Alexis Gousseau Date: Mon, 22 Jun 2026 12:04:04 +0200 Subject: [PATCH] perf: :rotating_light: resolve linters errors --- composer.json | 20 +++-- config/admin-secure.php | 6 +- src/AdminSecure.php | 81 +++++++------------ src/AdminSecureServiceProvider.php | 6 +- src/Exceptions/AdminSecureException.php | 2 + .../AdminSecureMiddleware.php | 21 +++-- src/Options.php | 76 ++++++++--------- 7 files changed, 97 insertions(+), 115 deletions(-) rename src/{Http/Middleware => Middlewares}/AdminSecureMiddleware.php (61%) diff --git a/composer.json b/composer.json index 898d9eb..286f91b 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,20 @@ "phpunit/php-code-coverage": "^11", "larastan/larastan": "^2.9" }, + "scripts": { + "post-autoload-dump": [ + "@php ./vendor/bin/testbench package:discover --ansi" + ], + "phpcs": [ + "./vendor/bin/phpcs" + ], + "phpstan": [ + "./vendor/bin/phpstan" + ], + "rector": [ + "./vendor/bin/rector" + ] + }, "autoload": { "psr-4": { "Uwil\\LaravelAdminSecure\\": "src/" @@ -40,12 +54,6 @@ ] } }, - "scripts": { - "post-autoload-dump": [ - "@php ./vendor/bin/testbench package:discover --ansi" - ], - "test": "vendor/bin/phpunit" - }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true diff --git a/config/admin-secure.php b/config/admin-secure.php index e94639a..419e3b0 100644 --- a/config/admin-secure.php +++ b/config/admin-secure.php @@ -1,6 +1,8 @@ env('UWIL_ADMIN_SECURE_CIDR_LIST'), - 'cidrProjectAllowedList' => env('UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST') + 'cidrList' => env('UWIL_ADMIN_SECURE_CIDR_LIST'), + 'cidrProjectAllowedList' => env('UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST'), ]; diff --git a/src/AdminSecure.php b/src/AdminSecure.php index 0fff3d4..592406f 100644 --- a/src/AdminSecure.php +++ b/src/AdminSecure.php @@ -1,5 +1,7 @@ Private ipV4 CIDR list. */ private static array $private_ip_v4_cidr_list = [ - '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16' + '10.0.0.0/8', + '172.16.0.0/12', + '192.168.0.0/16', ]; - /** - * Private ipV4 CIDR loopback - * - * @var string - */ + /** @var string Private ipV4 CIDR loopback. */ private static string $private_ip_v4_loopback_cidr = '127.0.0.0/8'; - /** - * Private ipV6 CIDR list - * - * @var array - */ + /** @var array Private ipV6 CIDR list. */ private static array $private_ip_v6_cidr_list = [ - 'fd00::/8' + 'fd00::/8', ]; /** * Private ipV6 CIDR loopback - * - * @var string */ private static string $private_ip_v6_loopback_cidr = '::1/128'; - /** - * Private options - * - * @var \Uwil\AdminSecure\Laravel\Options|null - */ + /** @var \Uwil\LaravelAdminSecure\Options|null Private options. */ private static $options = null; /** - * Check if ip is private - * - * @param \Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address $ip_address - * @return boolean + * Check if ip is private. */ - public static function ipIsPrivate(IPv4Address|IPv6Address $ip_address): bool + public static function ipIsPrivate(IPv4Address | IPv6Address $ip_address): bool { - return \get_class($ip_address) === IPv4Address::class ? + return $ip_address instanceof IPv4Address ? self::ipV4IsPrivate($ip_address) : self::ipV6IsPrivate($ip_address); } /** - * Fetch client IP + * Fetch client IP. * - * @return \Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address - * @throws \Uwil\AdminSecure\Laravel\Exceptions\AdminSecureException If the ip is invalid. + * @throws \Uwil\LaravelAdminSecure\Exceptions\AdminSecureException If the ip is invalid. */ - public static function fetchClientIp(): IPv4Address|IPv6Address + public static function fetchClientIp(): IPv4Address | IPv6Address { // ! REVERSE PROXY HACK. - $ip = request()->server('HTTP_X_FORWARDED_FOR') ?? request()->getClientIp(); - $ip = IPAddress::Make($ip); + $forwarded = request()->server('HTTP_X_FORWARDED_FOR'); + $ip = is_string($forwarded) && $forwarded !== '' + ? trim(explode(',', $forwarded)[0]) + : request()->getClientIp() ?? '0.0.0.0'; + $ip = IPAddress::Make($ip); if ($ip->isErr()) { throw new AdminSecureException(\trans('Client IP not valid ') . $ip->getErr()); } + return $ip->unwrap(); } /** - * Check if ipV4 is private - * - * @param \Dxw\CIDR\IPv4Address $ipv4_address - * @return boolean + * Check if ipV4 is private. */ private static function ipV4IsPrivate(IPv4Address $ipv4_address): bool { return \collect(self::$private_ip_v4_cidr_list) ->concat([self::$private_ip_v4_loopback_cidr]) ->map(fn (string $ipv4_cidr) => IPv4Range::Make($ipv4_cidr)->unwrap()) - ->reduce(fn (bool|null $initial, IPv4Range $ipv4_range) => - $initial || $ipv4_range->containsAddress($ipv4_address)); + ->reduce(fn (?bool $initial, IPv4Range $ipv4_range) => $initial || $ipv4_range->containsAddress($ipv4_address)); } /** - * Check if ipV6 is private - * - * @param \Dxw\CIDR\IPv6Address $ipv6_address - * @return boolean + * Check if ipV6 is private. */ private static function ipV6IsPrivate(IPv6Address $ipv6_address): bool { return \collect(self::$private_ip_v6_cidr_list) ->concat([self::$private_ip_v6_loopback_cidr]) ->map(fn (string $ipv6_cidr) => IPv6Range::Make($ipv6_cidr)->unwrap()) - ->reduce(fn (bool|null $initial, IPv6Range $ipv6_range) => - $initial || $ipv6_range->containsAddress($ipv6_address)); + ->reduce(fn (?bool $initial, IPv6Range $ipv6_range) => $initial || $ipv6_range->containsAddress($ipv6_address)); } /** - * Get Admin secure options - * - * @return \Uwil\AdminSecure\Laravel\Options + * Get Admin secure options. */ public static function getOptions(): Options { - if (!self::$options) { + if (! self::$options) { self::$options = new Options(\config('admin-secure')); } + return self::$options; } } diff --git a/src/AdminSecureServiceProvider.php b/src/AdminSecureServiceProvider.php index ac68745..c26ff4a 100644 --- a/src/AdminSecureServiceProvider.php +++ b/src/AdminSecureServiceProvider.php @@ -1,8 +1,11 @@ config_path(), ], 'config'); - // * Set filter condition + // * Set filter condition. $this->app->singleton('hasToFilterIp', fn (Application $app) => $app->environment('production', 'staging')); /** @var \Illuminate\Routing\Router */ @@ -32,6 +35,7 @@ class AdminSecureServiceProvider extends ServiceProvider * * @return void */ + #[\Override] public function register() { $this->mergeConfigFrom( diff --git a/src/Exceptions/AdminSecureException.php b/src/Exceptions/AdminSecureException.php index 8807959..ad12149 100644 --- a/src/Exceptions/AdminSecureException.php +++ b/src/Exceptions/AdminSecureException.php @@ -1,5 +1,7 @@ make('hasToFilterIp')) { + if (! \app()->make('hasToFilterIp')) { return $next($request); } $client_ip = AdminSecure::fetchClientIp(); - + $ip_is_allowed = \collect(AdminSecure::getOptions()->getCidrList()) - ->reduce(fn (null|bool $initial, IPv4Range|IPv6Range $ip_range) => $initial || IP::contains( - $ip_range, + ->reduce(fn (?bool $initial, IPv4Range | IPv6Range $ip_range) => $initial || $ip_range->containsAddress( $client_ip - )->unwrap()); + )); $ip_is_allowed_by_config = \collect(AdminSecure::getOptions()->getCidrAllowedList()) - ->reduce(fn (null|bool $initial, IPv4Range|IPv6Range $ip_range) => $initial || IP::contains( - $ip_range, + ->reduce(fn (?bool $initial, IPv4Range | IPv6Range $ip_range) => $initial || $ip_range->containsAddress( $client_ip - )->unwrap()); + )); if ($ip_is_allowed or $ip_is_allowed_by_config or AdminSecure::ipIsPrivate($client_ip)) { return $next($request); diff --git a/src/Options.php b/src/Options.php index d5fa2ed..6cb3245 100644 --- a/src/Options.php +++ b/src/Options.php @@ -15,27 +15,19 @@ use Uwil\LaravelAdminSecure\Exceptions\AdminSecureException; */ final class Options { - /** - * @var array The configuration options - */ + /** @var array The configuration options. */ private $options; - /** - * @var \Symfony\Component\OptionsResolver\OptionsResolver The options resolver - */ + /** @var \Symfony\Component\OptionsResolver\OptionsResolver The options resolver. */ private $resolver; - /** - * The cache key where to store config cidr list. - * - * @var string - */ + /** @var string The cache key where to store config cidr list. */ private static string $cache_config_key = 'uwil_ip_cidr_config_list'; /** * Class constructor. * - * @param array $options The configuration options. + * @param array $options The configuration options. */ public function __construct(array $options = []) { @@ -47,7 +39,7 @@ final class Options } /** - * Get cidr list + * Get cidr list. * * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> */ @@ -57,7 +49,7 @@ final class Options } /** - * Get cidr project allowed list + * Get cidr project allowed list. * * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> */ @@ -69,26 +61,24 @@ final class Options /** * Configures the options of the client. * - * @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver The resolver for the options. - * @return void + * @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver The resolver for the options. */ private function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'cidrList' => [], - 'cidrProjectAllowedList' => [] + 'cidrList' => [], + 'cidrProjectAllowedList' => [], ]); - $resolver->setNormalizer('cidrList', \Closure::fromCallable([$this, 'normalizeCidrList'])); - $resolver->setNormalizer('cidrProjectAllowedList', \Closure::fromCallable([$this, 'normalizeCidrProjectList'])); + $resolver->setNormalizer('cidrList', $this->normalizeCidrList(...)); + $resolver->setNormalizer('cidrProjectAllowedList', $this->normalizeCidrProjectList(...)); } /** - * Normalise Cidr list for cidrList option + * Normalise Cidr list for cidrList option. + * + * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> * - * @param \Symfony\Component\OptionsResolver\Options $options - * @param string|null $value - * @return array * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed */ private function normalizeCidrList( @@ -100,11 +90,10 @@ final class Options } /** - * Normalise Cidr list for cidrProjectAllowedList option + * Normalise Cidr list for cidrProjectAllowedList option. + * + * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> * - * @param \Symfony\Component\OptionsResolver\Options $options - * @param string|null $value - * @return array * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed */ private function normalizeCidrProjectList( @@ -118,40 +107,43 @@ final class Options /** * Normalize CIDR list with specific cache key. * - * @param string|null $value - * @param string $optionName * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> */ private function doNormalizeCidrList(?string $value, string $optionName): array { - $cacheConfigKey = self::$cache_config_key . '_' . $optionName; - $allowed_cidr_config_list = collect(explode(',', strval($value)))->filter(); + $cacheConfigKey = self::$cache_config_key . '_' . $optionName; + $allowed_cidr_config_list = collect(explode(',', strval($value)))->filter(); $allowed_cidr_config_list_hash = \md5($allowed_cidr_config_list->implode(',')); + /** @var array{list: array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>, hash: string}|null $cached */ + $cached = Cache::get($cacheConfigKey); + // Flush cache if config hash changed - if ($allowed_cidr_config_list_hash !== \collect(Cache::get($cacheConfigKey))->get('hash')) { + if ($cached === null || $cached['hash'] !== $allowed_cidr_config_list_hash) { Cache::forget($cacheConfigKey); /** @var array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> $allowed_cidr_list */ $allowed_cidr_list = $allowed_cidr_config_list ->map(function (string $cidr) { - $ip_range = !IPv4Range::Make($cidr)->isErr() ? IPv4Range::Make($cidr) : IPv6Range::Make($cidr); + $ip_range = ! IPv4Range::Make($cidr)->isErr() ? IPv4Range::Make($cidr) : IPv6Range::Make($cidr); if ($ip_range->isErr()) { throw new AdminSecureException(\trans( 'CIDR LIST not valid (ipv4 or ipv6 cidr) :cidr', - [ - 'cidr' => $cidr - ] + ['cidr' => $cidr] )); } + return $ip_range->unwrap(); })->all(); + + Cache::forever($cacheConfigKey, [ + 'list' => $allowed_cidr_list, + 'hash' => $allowed_cidr_config_list_hash, + ]); + + return $allowed_cidr_list; } - /** @var array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> $allowed_cidr_list */ - return \collect(Cache::rememberForever($cacheConfigKey, fn() => [ - 'list' => $allowed_cidr_list, - 'hash' => $allowed_cidr_config_list_hash - ]))->get('list'); + return $cached['list']; } }