From a4e0fafa16e3ead9175b89d0270c0d00f70a81c8 Mon Sep 17 00:00:00 2001 From: Vincent BRIDIER Date: Fri, 9 Jan 2026 15:24:46 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20Remove=20LDC=20Web=20API=20?= =?UTF-8?q?dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 + README.md | 27 +--- composer.json | 4 +- config/admin-secure.php | 5 +- license.md | 2 +- src/AdminSecure.php | 107 +--------------- src/AdminSecureServiceProvider.php | 14 --- src/Console/SyncFromLdcWeb.php | 43 ------- .../AdminSecureInvalidAdressException.php | 7 -- src/Http/Middleware/AdminSecureMiddleware.php | 9 +- src/Options.php | 118 +++++++++--------- 11 files changed, 82 insertions(+), 260 deletions(-) delete mode 100644 src/Console/SyncFromLdcWeb.php delete mode 100644 src/Exceptions/AdminSecureInvalidAdressException.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c2538..0387578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ All notable changes to `admin-secure-laravel` will be documented in this file. - This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), - Commits respect [Conventionnal commits](https://www.conventionalcommits.org/en/v1.0.0/) & use [Gitmoji](https://gitmoji.dev/). +## **[v5.0.0] - 09/01/2026** + +### Added + +- feat: ✨ Remove LDC Web API dependency + ## **[v4.0.0] - 07/04/2025** ### Added diff --git a/README.md b/README.md index d5983eb..fcad06a 100644 --- a/README.md +++ b/README.md @@ -7,36 +7,19 @@ composer req uwil/admin-secure-laravel .env.example : ```dotenv -# ADMIN SECURE -# - API ENDPOINT (Optional) -LDC_WEB_API_ENDPOINT="https://ldc-web.ldc.fr/api/ldc-networks" -# - API KEY -LDC_WEB_API_KEY="" +# UWIL ADMIN SECURE +# - LIST SEPARATED WITH ',' (Required) +UWIL_ADMIN_SECURE_CIDR_LIST="" # - LIST SEPARATED WITH ',' (Optional) -LDC_WEB_CIDR_MANUAL_ALLOWED_LIST="" +UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST="" ``` -README.md : -``` -Put every min in PROD : -php artisan schedule:run -``` - -## Available Commands -``` -uwil:admin-secure:sync -``` ## Usage Put 'admin-secure' middleware on route to protect it. -## Cron configure - -Nothing to do command is already configureed to run everyday. -You just need to install this package. -**!! Runs only in production !!** # Changelog -Latest version v4.0.0. +Latest version v5.0.0. See the [CHANGELOG.md](CHANGELOG.md) file for details. diff --git a/composer.json b/composer.json index 8b6b45c..01a4501 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,10 @@ "name": "uwil/admin-secure-laravel", "description": "Restrict access to LDC Network", "license": "MIT", - "version": "4.0.0", + "version": "5.0.0", "readme": "https://lab.s-web.fr/ldc-web/projets/ldc-web-admin-secure-laravel/-/blob/main/README.md", "support": { - "email": "equipe.web@ldc.fr", + "email": "digital@ldc.fr", "issues": "https://lab.s-web.fr/ldc-web/projets/ldc-web-admin-secure-laravel/-/issues" }, "require": { diff --git a/config/admin-secure.php b/config/admin-secure.php index a845951..e94639a 100644 --- a/config/admin-secure.php +++ b/config/admin-secure.php @@ -1,7 +1,6 @@ env('LDC_WEB_API_ENDPOINT', 'https://ldc-web.ldc.fr/api/ldc-networks'), - 'apiKey' => env('LDC_WEB_API_KEY'), - 'cidrManualAllowedList' => env('LDC_WEB_CIDR_MANUAL_ALLOWED_LIST') + 'cidrList' => env('UWIL_ADMIN_SECURE_CIDR_LIST'), + 'cidrProjectAllowedList' => env('UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST') ]; diff --git a/license.md b/license.md index 856d209..1b52fbd 100644 --- a/license.md +++ b/license.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 LDC Service Web +Copyright (c) 2022 LDC Digital Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/AdminSecure.php b/src/AdminSecure.php index f87a18a..e8401f2 100644 --- a/src/AdminSecure.php +++ b/src/AdminSecure.php @@ -7,29 +7,10 @@ use Dxw\CIDR\IPv4Address; use Dxw\CIDR\IPv4Range; use Dxw\CIDR\IPv6Address; use Dxw\CIDR\IPv6Range; -use Illuminate\Http\Client\Response; -use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Http; -use Illuminate\Support\Facades\Log; use Uwil\AdminSecure\Laravel\Exceptions\AdminSecureException; -use Uwil\AdminSecure\Laravel\Exceptions\AdminSecureInvalidAdressException; abstract class AdminSecure { - /** - * The cache key where to store ip list. - * - * @var string - */ - private static string $cache_key = 'ldcweb_ip_list'; - - /** - * The cache time to live. - * - * @var string - */ - private static string $cache_ttl = '180 minutes'; - /** * Private ipV4 CIDR list * @@ -89,7 +70,7 @@ abstract class AdminSecure */ public static function fetchClientIp(): IPv4Address|IPv6Address { - // ! HA PROXY HACK. + // ! REVERSE PROXY HACK. $ip = request()->server('HTTP_X_FORWARDED_FOR') ?? request()->getClientIp(); $ip = IPAddress::Make($ip); @@ -99,64 +80,6 @@ abstract class AdminSecure return $ip->unwrap(); } - /** - * Get ip list from cache - * - * @return array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address> - */ - public static function getCacheList(): array - { - try { - /** @var array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address>|null $ip_list */ - $ip_list = Cache::get(self::$cache_key); - if (!$ip_list) { - $ip_list = self::fetchIpList(); - self::validateIpList($ip_list); - self::setCacheList($ip_list); - return $ip_list; - } - return $ip_list; - } catch (AdminSecureInvalidAdressException) { - $ip_list = self::fetchIpList(); - self::validateIpList($ip_list); - self::setCacheList($ip_list); - return $ip_list; - } - } - - /** - * Set ip list to cache - * - * @param array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address> $ip_list - * @return void - */ - public static function setCacheList(array $ip_list): void - { - Cache::put(self::$cache_key, $ip_list, \DateInterval::createFromDateString(self::$cache_ttl)); - } - - /** - * Fetch Ip Adress list from Uwil Web - * - * @return array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address> - * @throws \Uwil\AdminSecure\Laravel\Exceptions\AdminSecureException If Wrong env value. - * @throws \Illuminate\Http\Client\RequestException If calling admin secure fails. - * @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber - */ - public static function fetchIpList(): array - { - // phpcs:enable - $endpoint = self::getOptions()->getEndpoint(); - $key = self::getOptions()->getApiKey(); - - $response = Http::get($endpoint, ['token' => $key])->throw(); - $adresses = self::parseAdresses($response); - - Log::info(\sprintf('ADMINSECURE : Fetched %d ip(s) from LDC WEB', \count($adresses))); - - return $adresses; - } - /** * Check if ipV4 is private * @@ -187,34 +110,6 @@ abstract class AdminSecure $initial || $ipv6_range->containsAddress($ipv6_address)); } - /** - * Validate ip list - * - * @param array $ip_list - * @return void - * @throws \Uwil\AdminSecure\Laravel\Exceptions\AdminSecureInvalidAdressException If an IP is invalid. - */ - private static function validateIpList(array $ip_list): void - { - \collect($ip_list)->each(function ($ip) { - if (!\is_object($ip) or !\in_array(\get_class($ip), [IPv4Address::class, IPv6Address::class])) { - throw new AdminSecureInvalidAdressException('Invalid adresse ' . \strval(\json_encode($ip))); - } - }); - } - - /** - * Parse adresses - * - * @param \Illuminate\Http\Client\Response $response - * @return array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address> - */ - private static function parseAdresses(Response $response): array - { - return \collect($response->json()) - ->map(fn (string $ip) => IPAddress::Make($ip)->unwrap())->all(); - } - /** * Get Admin secure options * diff --git a/src/AdminSecureServiceProvider.php b/src/AdminSecureServiceProvider.php index 4089c37..c43a754 100644 --- a/src/AdminSecureServiceProvider.php +++ b/src/AdminSecureServiceProvider.php @@ -2,11 +2,9 @@ namespace Uwil\AdminSecure\Laravel; -use Illuminate\Console\Scheduling\Schedule; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\ServiceProvider; use Illuminate\Routing\Router; -use Uwil\AdminSecure\Laravel\Console\SyncFromLdcWeb; use Uwil\AdminSecure\Laravel\Http\Middleware\AdminSecureMiddleware; class AdminSecureServiceProvider extends ServiceProvider @@ -22,21 +20,9 @@ class AdminSecureServiceProvider extends ServiceProvider __DIR__ . '/../config' => config_path(), ], 'config'); - // * Register the command if we are using the application via the CLI . - $this->commands([SyncFromLdcWeb::class]); - // * Set filter condition $this->app->singleton('hasToFilterIp', fn (Application $app) => $app->environment('production', 'staging')); - // * Cron Report everyday - $this->app->booted(function () { - if ($this->app->make('hasToFilterIp')) { - /** @var \Illuminate\Console\Scheduling\Schedule */ - $schedule = $this->app->make(Schedule::class); - $schedule->command('uwil:admin-secure:sync')->daily(); - } - }); - /** @var \Illuminate\Routing\Router */ $router = $this->app->make(Router::class); $router->aliasMiddleware('admin-secure', AdminSecureMiddleware::class); diff --git a/src/Console/SyncFromLdcWeb.php b/src/Console/SyncFromLdcWeb.php deleted file mode 100644 index d9f6366..0000000 --- a/src/Console/SyncFromLdcWeb.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class SyncFromLdcWeb extends Command -{ - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'uwil:admin-secure:sync'; - - /** - * The console command description. - * - * @var string - */ - protected $description = 'Sync IP list from LDC Web'; - - /** - * Execute the console command. - * - * @return integer - * @throws \Uwil\AdminSecure\Laravel\Exceptions\AdminSecureException If missing LDC_WEB_API_KEY key. - */ - public function handle(): int - { - $this->info($this->description); - AdminSecure::setCacheList(AdminSecure::fetchIpList()); - $this->info('Done.'); - - return 0; - } -} diff --git a/src/Exceptions/AdminSecureInvalidAdressException.php b/src/Exceptions/AdminSecureInvalidAdressException.php deleted file mode 100644 index d2d4bb8..0000000 --- a/src/Exceptions/AdminSecureInvalidAdressException.php +++ /dev/null @@ -1,7 +0,0 @@ -reduce(fn (null|bool $initial, IPv4Address|IPv6Address $ip_address) => $initial || IP::contains( - $ip_address, + + $ip_is_allowed = \collect(AdminSecure::getOptions()->getCidrList()) + ->reduce(fn (null|bool $initial, IPv4Range|IPv6Range $ip_range) => $initial || IP::contains( + $ip_range, $client_ip )->unwrap()); diff --git a/src/Options.php b/src/Options.php index 8c42bdb..3ec11e1 100644 --- a/src/Options.php +++ b/src/Options.php @@ -30,7 +30,7 @@ final class Options * * @var string */ - private static string $cache_config_key = 'ldcweb_ip_cidr_config_list'; + private static string $cache_config_key = 'uwil_ip_cidr_config_list'; /** * Class constructor. @@ -47,33 +47,23 @@ final class Options } /** - * Get end point url + * Get cidr list * - * @return string + * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> */ - public function getEndpoint(): string + public function getCidrList(): array { - return $this->options['endpoint']; + return $this->options['cidrList']; } /** - * Get api key - * - * @return string - */ - public function getApiKey(): string - { - return $this->options['apiKey']; - } - - /** - * Get cidr allowed list + * Get cidr project allowed list * * @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> */ public function getCidrAllowedList(): array { - return $this->options['cidrManualAllowedList']; + return $this->options['cidrProjectAllowedList']; } /** @@ -85,34 +75,19 @@ final class Options private function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'endpoint' => '', - 'apiKey' => '', - 'cidrManualAllowedList' => [] + 'cidrList' => [], + 'cidrProjectAllowedList' => [] ]); - $resolver->setAllowedTypes('endpoint', [ - 'string', \Closure::fromCallable([$this, 'validateEndpoint']) - ]); - $resolver->setAllowedTypes('apiKey', 'string'); - $resolver->setNormalizer('cidrManualAllowedList', \Closure::fromCallable([$this, 'normalizeCidrList'])); + $resolver->setNormalizer('cidrList', \Closure::fromCallable([$this, 'normalizeCidrList'])); + $resolver->setNormalizer('cidrProjectAllowedList', \Closure::fromCallable([$this, 'normalizeCidrProjectList'])); } /** - * Validate endpoint - * - * @param null|string $value - * @return boolean - */ - private function validateEndpoint(?string $value): bool - { - return !(!$value or !\filter_var($value, \FILTER_VALIDATE_URL)); - } - - /** - * Normalise Cidr list + * Normalise Cidr list for cidrList option * * @param \Symfony\Component\OptionsResolver\Options $options - * @param string $value + * @param string|null $value * @return array * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed */ @@ -121,31 +96,60 @@ final class Options ?string $value ): array { // phpcs:enable + return $this->doNormalizeCidrList($value, 'cidrList'); + } - $allowed_cidr_config_list = collect(explode(',', strval($value)))->filter(); + /** + * Normalise Cidr list for cidrProjectAllowedList option + * + * @param \Symfony\Component\OptionsResolver\Options $options + * @param string|null $value + * @return array + * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed + */ + private function normalizeCidrProjectList( + \Symfony\Component\OptionsResolver\Options $options, + ?string $value + ): array { + // phpcs:enable + return $this->doNormalizeCidrList($value, 'cidrProjectAllowedList'); + } + + /** + * 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(); $allowed_cidr_config_list_hash = \md5($allowed_cidr_config_list->implode(',')); - /** @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); - if ($ip_range->isErr()) { - throw new AdminSecureException(\trans( - 'LDC_WEB_CIDR_MANUAL_ALLOWED_LIST not valid (ipv4 or ipv6 cidr) :cidr', - [ - 'cidr' => $cidr - ] - )); - } - return $ip_range->unwrap(); - })->all(); - // * Flush cache if hash config hash changed - if ($allowed_cidr_config_list_hash !== \collect(Cache::get(self::$cache_config_key))->get('hash')) { - Cache::forget(self::$cache_config_key); + // Flush cache if config hash changed + if ($allowed_cidr_config_list_hash !== \collect(Cache::get($cacheConfigKey))->get('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); + if ($ip_range->isErr()) { + throw new AdminSecureException(\trans( + 'CIDR LIST not valid (ipv4 or ipv6 cidr) :cidr', + [ + 'cidr' => $cidr + ] + )); + } + return $ip_range->unwrap(); + })->all(); } /** @var array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> $allowed_cidr_list */ - return \collect(Cache::rememberForever(self::$cache_config_key, fn () => [ + return \collect(Cache::rememberForever($cacheConfigKey, fn() => [ 'list' => $allowed_cidr_list, 'hash' => $allowed_cidr_config_list_hash ]))->get('list');