├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── filament-firewall.php ├── database └── migrations │ └── update_firewall_ips_table.php.stub ├── resources └── lang │ ├── en │ ├── .gitkeep │ └── filament-firewall.php │ ├── hu │ └── filament-firewall.php │ ├── pt_BR │ └── filament-firewall.php │ └── pt_PT │ └── filament-firewall.php └── src ├── Facades └── FilamentFirewall.php ├── Filament └── Resources │ ├── FirewallIpResource.php │ └── FirewallIpResource │ └── Pages │ └── ManageFirewallIps.php ├── FilamentFirewall.php ├── FilamentFirewallPanel.php ├── FilamentFirewallServiceProvider.php ├── Middleware └── WhitelistRangeMiddleware.php └── Models └── Ip.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | node_modules/ 3 | npm-debug.log 4 | yarn-error.log 5 | 6 | # Laravel 4 specific 7 | bootstrap/compiled.php 8 | app/storage/ 9 | 10 | # Laravel 5 & Lumen specific 11 | public/storage 12 | public/hot 13 | 14 | # Laravel 5 & Lumen specific with changed public path 15 | public_html/storage 16 | public_html/hot 17 | 18 | storage/*.key 19 | .env 20 | Homestead.yaml 21 | Homestead.json 22 | /.vagrant 23 | .phpunit.result.cache 24 | composer.lock 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Solution Forest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > Please note that we will only be updating to version 2.x, excluding any bug fixes. 3 | 4 | 5 | ## About Solution Forest 6 | 7 | [Solution Forest](https://solutionforest.com) Web development agency based in Hong Kong. We help customers to solve their problems. We Love Open Soruces. 8 | 9 | We have built a collection of best-in-class products: 10 | 11 | - [VantagoAds](https://vantagoads.com): A self manage Ads Server, Simplify Your Advertising Strategy. 12 | - [GatherPro.events](https://gatherpro.events): A Event Photos management tools, Streamline Your Event Photos. 13 | - [Website CMS Management](https://filamentphp.com/plugins/solution-forest-cms-website): Website CMS Management - Filament CMS Plugin 14 | - [Filaletter](https://filaletter.solutionforest.net): Filaletter - Filament Newsletter Plugin 15 | 16 | # Filament Firewall 17 | 18 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/solution-forest/filament-firewall.svg?style=flat-square)](https://packagist.org/packages/solution-forest/filament-firewall) 19 | [![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/solution-forest/filament-firewall/run-tests?label=tests)](https://github.com/solution-forest/filament-firewall/actions?query=workflow%3Arun-tests+branch%3Amain) 20 | [![GitHub Code Style Action Status](https://img.shields.io/github/workflow/status/solution-forest/filament-firewall/Check%20&%20fix%20styling?label=code%20style)](https://github.com/solution-forest/filament-firewall/actions?query=workflow%3A"Check+%26+fix+styling"+branch%3Amain) 21 | [![Total Downloads](https://img.shields.io/packagist/dt/solution-forest/filament-firewall.svg?style=flat-square)](https://packagist.org/packages/solution-forest/filament-firewall) 22 | 23 | This package provides a whitelist and blacklist feature to restrict access to your Laravel application and Filament admin panel. For additional functionality, please refer to the [Laravel Firewall package](https://github.com/akaunting/laravel-firewall). 24 | 25 | ![filament-firewall-package-1](https://github.com/solutionforest/filament-firewall/assets/68525320/153b1478-003f-4ef9-bebc-8ed249647e9f) 26 | ![filament-firewall-package-2](https://github.com/solutionforest/filament-firewall/assets/68525320/1cde3993-77e1-4e64-8e4c-9727f1a40801) 27 | 28 | 29 | ## Getting Started 30 | 31 | 1. Install the package using the `composer require` command: 32 | 33 | ```php 34 | composer require solution-forest/filament-firewall 35 | ``` 36 | 2. To publish the configuration files and migrations files for this plugin, as well as automatically run migration, enter the following command: 37 | 38 | ```php 39 | php artisan filament-firewall:install 40 | ``` 41 | 3. This package comes with `WhitelistRangeMiddleware`. You need to register it in `$middleware` in the `app\Http\Kernel.php` file: 42 | 43 | ```bash 44 | protected $middleware = [ 45 | ... 46 | \SolutionForest\FilamentFirewall\Middleware\WhitelistRangeMiddleware::class, 47 | ]; 48 | ``` 49 | 4. You can change the setting in the `config/filament-firewall.php` file to skip the middleware `WhitelistRangeMiddleware` check. 50 | 5. Register the plugin in your Panel provider: 51 | > **Important: Register the plugin in your Panel provider after version 2.x** 52 | ``` bash 53 | use SolutionForest\FilamentFirewall\FilamentFirewallPanel; 54 | 55 | public function panel(Panel $panel): Panel 56 | { 57 | return $panel 58 | ->plugin(FilamentFirewallPanel::make()); 59 | } 60 | ``` 61 | 62 | ## Usage 63 | - On the IP Firewall page, you have the ability to add IPs to either a whitelist or a blocklist. The `WhitelistRangeMiddleware` middleware will automatically handle whitelist IP access. If you need to block a range of IPs while allowing a specific IP, such as blocking *172.19.0.0/24* except for *172.19.0.1*, you'll need to create a new **Deny** access record and specify the IP and prefix as *'172.19.0.0'* and *'24'*. Additionally, you'll need to add an **Allow** access record for the specific IP *'172.19.0.1'*. 64 | 65 | ![filament-firewall-package-2](https://github.com/solutionforest/filament-firewall/assets/68525320/1cde3993-77e1-4e64-8e4c-9727f1a40801) 66 | 67 | - Get whitelist / blacklist records 68 | 69 | ```php 70 | // whitelist 71 | \SolutionForest\FilamentFirewall\Facade\FilamentFirewall::getWhiteList(); 72 | 73 | // blacklist 74 | \SolutionForest\FilamentFirewall\Facade\FilamentFirewall::getBlackList(); 75 | ``` 76 | 77 | - Determine whether an IP address is included in a whitelist or blacklist 78 | 79 | ```php 80 | // whitelist 81 | \SolutionForest\FilamentFirewall\Facade\FilamentFirewall::withinWhiteList($ip); 82 | 83 | // blacklist 84 | \SolutionForest\FilamentFirewall\Facade\FilamentFirewall::withinBlackList($ip); 85 | ``` 86 | 87 | ## Publishing translations 88 | ```bash 89 | php artisan vendor:publish --tag=filament-firewall-translations 90 | ``` 91 | 92 | ## Security Vulnerabilities 93 | If you discover any security related issues, please email info+package@solutionforest.net instead of using the issue tracker. 94 | 95 | 96 | ## License 97 | Please see [License File](LICENSE.md) for more information. 98 | 99 | 100 | ## Credits 101 | - [Laravel Firewall](https://github.com/akaunting/laravel-firewall) 102 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solution-forest/filament-firewall", 3 | "description": "This is a middleware for whitelisting/blacklisting for Filament Admin", 4 | "keywords": [ 5 | "Solution Forest", 6 | "laravel", 7 | "filament-firewall" 8 | ], 9 | "homepage": "https://github.com/solution-forest/filament-firewall", 10 | "support": { 11 | "issues": "https://github.com/solution-forest/filament-firewall/issues", 12 | "source": "https://github.com/solution-forest/filament-firewall" 13 | }, 14 | "license": "MIT", 15 | "authors": [{ 16 | "name": "Carly", 17 | "email": "info@solutionforest.net", 18 | "role": "Developer" 19 | }], 20 | "require": { 21 | "php": "^8.0", 22 | "akaunting/laravel-firewall": "^2.2", 23 | "filament/filament": "^3.0", 24 | "filament/support": "^3.0", 25 | "spatie/laravel-package-tools": "^1.15.0" 26 | }, 27 | "require-dev": { 28 | "laravel/pint": "^1.0", 29 | "nunomaduro/collision": "^7.9", 30 | "nunomaduro/larastan": "^2.0.1", 31 | "orchestra/testbench": "^8.0", 32 | "pestphp/pest": "^2.0", 33 | "pestphp/pest-plugin-arch": "^2.0", 34 | "pestphp/pest-plugin-laravel": "^2.0", 35 | "phpstan/extension-installer": "^1.1", 36 | "phpstan/phpstan-deprecation-rules": "^1.0", 37 | "phpstan/phpstan-phpunit": "^1.0", 38 | "spatie/laravel-ray": "^1.26" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "SolutionForest\\FilamentFirewall\\": "src", 43 | "SolutionForest\\FilamentFirewall\\Database\\Factories\\": "database/factories", 44 | "SolutionForest\\FilamentFirewall\\Database\\Seeders\\": "database/seeders" 45 | } 46 | }, 47 | "autoload-dev": { 48 | 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "providers": [ 53 | "SolutionForest\\FilamentFirewall\\FilamentFirewallServiceProvider" 54 | ], 55 | "aliases": { 56 | "FilamentFirewall": "SolutionForest\\FilamentFirewall\\Facades\\FilamentFirewall" 57 | } 58 | } 59 | }, 60 | "minimum-stability": "dev", 61 | "prefer-stable": true, 62 | "config": { 63 | "allow-plugins": { 64 | "pestphp/pest-plugin": true, 65 | "phpstan/extension-installer": true 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/filament-firewall.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'ip' => Ip::class, 9 | ], 10 | 'resources' => [ 11 | FirewallIpResource::class, 12 | ], 13 | 'skip_whitelist_range' => [ 14 | '127.0.0.1', 15 | ], 16 | ]; -------------------------------------------------------------------------------- /database/migrations/update_firewall_ips_table.php.stub: -------------------------------------------------------------------------------- 1 | integer('prefix_size')->nullable()->after('ip'); 13 | }); 14 | } 15 | 16 | public function down(): void 17 | { 18 | Schema::table('firewall_ips', function (Blueprint $table) { 19 | $table->dropColumn('prefix_size'); 20 | }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /resources/lang/en/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solutionforest/filament-firewall/c051ff56da7db1888a741cc9678f349f5944cda8/resources/lang/en/.gitkeep -------------------------------------------------------------------------------- /resources/lang/en/filament-firewall.php: -------------------------------------------------------------------------------- 1 | 'IP Firewall', 6 | 'filament.resource.ip.getNavigationGroup' => 'System', 7 | 'filament.resource.ip.label' => 'IP Firewall', 8 | 'filament.resource.ip.modalLabel' => 'IP Firewall', 9 | 'filament.resource.ip.pluralLabel' => 'IP Firewall', 10 | 'filament.resource.ip.pluralModelLabel' => 'IP Firewall', 11 | 12 | 'table.column.ip' => 'IP', 13 | 'table.column.prefix_size' => 'Prefix', 14 | 'table.column.blocked' => 'Blocked?', 15 | 'table.column.is_allow' => 'Allow / Deny', 16 | 'table.column.created_at' => 'Created at', 17 | 'table.column.updated_at' => 'Updated at', 18 | 19 | 'form.field.ip' => 'IP', 20 | 'form.field.prefix_size' => 'Prefix', 21 | 'form.field.blocked' => 'Blocked?', 22 | 'form.field.is_allow' => 'Allow / Deny', 23 | 24 | 'action.addMyIp' => 'Add My IP', 25 | 'action.addMyIp.alreadyAdded' => 'Already added.', 26 | 'action.fillMyIp' => 'Fill My IP', 27 | 28 | 'labels.allow' => 'Allow', 29 | 'labels.deny' => 'Deny', 30 | ]; -------------------------------------------------------------------------------- /resources/lang/hu/filament-firewall.php: -------------------------------------------------------------------------------- 1 | 'IP tűzfal', 5 | 'filament.resource.ip.getNavigationGroup' => 'Rendszer', 6 | 'filament.resource.ip.label' => 'IP cím', 7 | 'filament.resource.ip.modalLabel' => 'IP cím', 8 | 'filament.resource.ip.pluralLabel' => 'IP cím', 9 | 'filament.resource.ip.pluralModelLabel' => 'IP tűzfal', 10 | 11 | 'table.column.ip' => 'IP cím', 12 | 'table.column.prefix_size' => 'Előtag', 13 | 'table.column.blocked' => 'Blokkolva?', 14 | 'table.column.is_allow' => 'Művelet', 15 | 'table.column.created_at' => 'Létrehozva', 16 | 'table.column.updated_at' => 'Módosítva', 17 | 18 | 'form.field.ip' => 'IP cím', 19 | 'form.field.prefix_size' => 'Előtag', 20 | 'form.field.blocked' => 'Blokkolva?', 21 | 'form.field.is_allow' => 'Művelet', 22 | 23 | 'action.addMyIp' => 'IP címem hozzáadása', 24 | 'action.addMyIp.alreadyAdded' => 'Sikeresen hozzáadva.', 25 | 'action.fillMyIp' => 'IP címem kitöltése', 26 | 27 | 'labels.allow' => 'Engedélyezés', 28 | 'labels.deny' => 'Tiltás', 29 | ]; 30 | -------------------------------------------------------------------------------- /resources/lang/pt_BR/filament-firewall.php: -------------------------------------------------------------------------------- 1 | 'Firewall IP', 5 | 'filament.resource.ip.getNavigationGroup' => 'Sistema', 6 | 'filament.resource.ip.label' => 'Endereço IP', 7 | 'filament.resource.ip.modalLabel' => 'Endereço IP', 8 | 'filament.resource.ip.pluralLabel' => 'Endereços IP', 9 | 'filament.resource.ip.pluralModelLabel' => 'Firewall IP', 10 | 11 | 'table.column.ip' => 'Endereço IP', 12 | 'table.column.prefix_size' => 'Prefixo', 13 | 'table.column.blocked' => 'Bloqueado?', 14 | 'table.column.is_allow' => 'Ação', 15 | 'table.column.created_at' => 'Criado em', 16 | 'table.column.updated_at' => 'Atualizado em', 17 | 18 | 'form.field.ip' => 'Endereço IP', 19 | 'form.field.prefix_size' => 'Prefixo', 20 | 'form.field.blocked' => 'Bloqueado?', 21 | 'form.field.is_allow' => 'Ação', 22 | 23 | 'action.addMyIp' => 'Adicionar meu IP', 24 | 'action.addMyIp.alreadyAdded' => 'Adicionado com sucesso.', 25 | 'action.fillMyIp' => 'Preencher meu IP', 26 | 27 | 'labels.allow' => 'Permitir', 28 | 'labels.deny' => 'Negar', 29 | ]; 30 | -------------------------------------------------------------------------------- /resources/lang/pt_PT/filament-firewall.php: -------------------------------------------------------------------------------- 1 | 'Firewall IP', 5 | 'filament.resource.ip.getNavigationGroup' => 'Sistema', 6 | 'filament.resource.ip.label' => 'Endereço IP', 7 | 'filament.resource.ip.modalLabel' => 'Endereço IP', 8 | 'filament.resource.ip.pluralLabel' => 'Endereços IP', 9 | 'filament.resource.ip.pluralModelLabel' => 'Firewall IP', 10 | 11 | 'table.column.ip' => 'Endereço IP', 12 | 'table.column.prefix_size' => 'Prefixo', 13 | 'table.column.blocked' => 'Bloqueado?', 14 | 'table.column.is_allow' => 'Ação', 15 | 'table.column.created_at' => 'Criado em', 16 | 'table.column.updated_at' => 'Atualizado em', 17 | 18 | 'form.field.ip' => 'Endereço IP', 19 | 'form.field.prefix_size' => 'Prefixo', 20 | 'form.field.blocked' => 'Bloqueado?', 21 | 'form.field.is_allow' => 'Ação', 22 | 23 | 'action.addMyIp' => 'Adicionar o meu IP', 24 | 'action.addMyIp.alreadyAdded' => 'Adicionado com sucesso.', 25 | 'action.fillMyIp' => 'Preencher o meu IP', 26 | 27 | 'labels.allow' => 'Permitir', 28 | 'labels.deny' => 'Negar', 29 | ]; 30 | -------------------------------------------------------------------------------- /src/Facades/FilamentFirewall.php: -------------------------------------------------------------------------------- 1 | schema([ 23 | Forms\Components\TextInput::make('ip') 24 | ->label(__('filament-firewall::filament-firewall.form.field.ip')) 25 | ->default(fn () => Request::getClientIp()) 26 | ->regex('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/') 27 | ->validationAttribute(Str::upper(__('filament-firewall::filament-firewall.form.field.ip'))) 28 | ->suffixAction(Forms\Components\Actions\Action::make('fillMyIp') 29 | ->label(__('filament-firewall::filament-firewall.action.fillMyIp')) 30 | ->icon('heroicon-o-pencil') 31 | ->action(fn (Set $set) => $set('ip', Request::getClientIp())) 32 | ) 33 | ->required(), 34 | 35 | Forms\Components\TextInput::make('prefix_size') 36 | ->label(__('filament-firewall::filament-firewall.form.field.prefix_size')) 37 | ->numeric() 38 | ->minValue(0) 39 | ->maxValue(32) 40 | ->prefix('/'), 41 | 42 | Forms\Components\Radio::make('blocked') 43 | ->label(__('filament-firewall::filament-firewall.form.field.is_allow')) 44 | ->options([ 45 | 0 => __('filament-firewall::filament-firewall.labels.allow'), 46 | 1 => __('filament-firewall::filament-firewall.labels.deny'), 47 | ]) 48 | ->default(1), 49 | ]) 50 | ->columns(1); 51 | } 52 | 53 | public static function table(Table $table): Table 54 | { 55 | return $table 56 | ->columns([ 57 | Tables\Columns\TextColumn::make('ip') 58 | ->label(__('filament-firewall::filament-firewall.table.column.ip')) 59 | ->searchable(isIndividual: true) 60 | ->sortable(), 61 | Tables\Columns\TextColumn::make('prefix_size') 62 | ->label(__('filament-firewall::filament-firewall.table.column.prefix_size')) 63 | ->formatStateUsing(fn (?string $state): ?string => $state ? str($state)->prepend('/') : null) 64 | ->searchable(isIndividual: true) 65 | ->sortable(), 66 | Tables\Columns\IconColumn::make('blocked') 67 | ->label(__('filament-firewall::filament-firewall.table.column.is_allow')) 68 | ->boolean() 69 | ->falseIcon('heroicon-o-check-circle') 70 | ->falseColor('success') 71 | ->trueIcon('heroicon-o-x-circle') 72 | ->trueColor('danger'), 73 | Tables\Columns\TextColumn::make('created_at') 74 | ->label(__('filament-firewall::filament-firewall.table.column.created_at')) 75 | ->sortable(), 76 | Tables\Columns\TextColumn::make('updated_at') 77 | ->label(__('filament-firewall::filament-firewall.table.column.updated_at')) 78 | ->sortable(), 79 | ]) 80 | ->filters([ 81 | Tables\Filters\TrashedFilter::make(), 82 | ]) 83 | ->actions([ 84 | Tables\Actions\EditAction::make(), 85 | Tables\Actions\DeleteAction::make(), 86 | Tables\Actions\ForceDeleteAction::make(), 87 | Tables\Actions\RestoreAction::make(), 88 | ]) 89 | ->bulkActions([ 90 | Tables\Actions\DeleteBulkAction::make(), 91 | Tables\Actions\ForceDeleteBulkAction::make(), 92 | Tables\Actions\RestoreBulkAction::make(), 93 | ]); 94 | } 95 | 96 | public static function getPages(): array 97 | { 98 | return [ 99 | 'index' => Pages\ManageFirewallIps::route('/'), 100 | ]; 101 | } 102 | 103 | public static function getModel(): string 104 | { 105 | return config('filament-firewall.models.ip', \SolutionForest\FilamentFirewall\Models\Ip::class); 106 | } 107 | 108 | public static function getNavigationIcon(): ?string 109 | { 110 | return 'heroicon-o-shield-check'; 111 | } 112 | 113 | public static function getNavigationLabel(): string 114 | { 115 | return __('filament-firewall::filament-firewall.filament.resource.ip.navigationLabel'); 116 | } 117 | 118 | public static function getLabel(): string 119 | { 120 | return __('filament-firewall::filament-firewall.filament.resource.ip.label'); 121 | } 122 | 123 | public static function getModelLabel(): string 124 | { 125 | return __('filament-firewall::filament-firewall.filament.resource.ip.modalLabel'); 126 | } 127 | 128 | public static function getPluralLabel(): string 129 | { 130 | return __('filament-firewall::filament-firewall.filament.resource.ip.pluralLabel'); 131 | } 132 | 133 | public static function getPluralModelLabel(): string 134 | { 135 | return __('filament-firewall::filament-firewall.filament.resource.ip.pluralModelLabel'); 136 | } 137 | 138 | public static function getNavigationGroup(): ?string 139 | { 140 | return __('filament-firewall::filament-firewall.filament.resource.ip.getNavigationGroup'); 141 | } 142 | 143 | public static function getEloquentQuery(): Builder 144 | { 145 | return parent::getEloquentQuery() 146 | ->withoutGlobalScopes([ 147 | SoftDeletingScope::class, 148 | ]); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/Filament/Resources/FirewallIpResource/Pages/ManageFirewallIps.php: -------------------------------------------------------------------------------- 1 | label(__('filament-firewall::filament-firewall.action.addMyIp')) 23 | ->action('addMyIp'), 24 | CreateAction::make() 25 | ->label(__('filament-actions::create.single.label', ['label' => null])), 26 | ]; 27 | } 28 | 29 | public function addMyIp() 30 | { 31 | try { 32 | $currIp = Request::getClientIp(); 33 | 34 | if (FilamentFirewall::getFirewallIpModel()::query()->where('ip', $currIp)->whereNull('prefix_size')->first()) { 35 | 36 | Notification::make() 37 | ->title(__('filament-firewall::filament-firewall.action.addMyIp.alreadyAdded')) 38 | ->danger() 39 | ->send(); 40 | 41 | return; 42 | } 43 | 44 | $data = [ 45 | 'ip' => $currIp, 46 | 'blocked' => false, 47 | ]; 48 | 49 | $record = new ($this->getModel())($data); 50 | 51 | if ($tenant = Filament::getTenant()) { 52 | $record = $this->associateRecordWithTenant($record, $tenant); 53 | 54 | } else { 55 | 56 | $record->save(); 57 | } 58 | 59 | Notification::make() 60 | ->title(__('filament-actions::create.single.notifications.created.title')) 61 | ->success() 62 | ->send(); 63 | 64 | } catch (\Exception $e) { 65 | // 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/FilamentFirewall.php: -------------------------------------------------------------------------------- 1 | getFirewallIpModel()::query()->where('blocked', false)->get(); 12 | } 13 | 14 | public function withinWhitelist($ip): bool 15 | { 16 | $list = $this->getWhiteList() 17 | ->map(function ($record) { 18 | $ip = $record->ip; 19 | if ($record->prefix_size) { 20 | $ip = (string) str($ip)->finish('/')->finish($record->prefix_size); 21 | } 22 | return $ip; 23 | }) 24 | ->toArray(); 25 | return \Symfony\Component\HttpFoundation\IpUtils::checkIp($ip, $list); 26 | } 27 | 28 | public function getBlackList(): Collection 29 | { 30 | return $this->getFirewallIpModel()::blocked()->get(); 31 | } 32 | 33 | public function withinBlackList($ip): bool 34 | { 35 | $list = $this->getBlackList() 36 | ->map(function ($record) { 37 | $ip = $record->ip; 38 | if ($record->prefix_size) { 39 | $ip = (string) str($ip)->finish('/')->finish($record->prefix_size); 40 | } 41 | return $ip; 42 | }) 43 | ->toArray(); 44 | return \Symfony\Component\HttpFoundation\IpUtils::checkIp($ip, $list); 45 | } 46 | 47 | public function getFirewallIpModel(): string 48 | { 49 | return config('filament-firewall.models.ip', \SolutionForest\FilamentFirewall\Models\Ip::class); 50 | } 51 | } -------------------------------------------------------------------------------- /src/FilamentFirewallPanel.php: -------------------------------------------------------------------------------- 1 | resources($resources); 19 | } 20 | 21 | public function boot(Panel $panel): void 22 | { 23 | // 24 | } 25 | 26 | public static function make(): static 27 | { 28 | return app(static::class); 29 | } 30 | 31 | public static function get(): static 32 | { 33 | return filament(app(static::class)->getId()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/FilamentFirewallServiceProvider.php: -------------------------------------------------------------------------------- 1 | name(static::$name) 16 | ->hasTranslations() 17 | ->hasConfigFile() 18 | ->hasMigrations([ 19 | 'update_firewall_ips_table', 20 | ]) 21 | ->hasInstallCommand(function (InstallCommand $command) { 22 | $command 23 | ->publishConfigFile() 24 | ->publishMigrations() 25 | ->startWith(function (InstallCommand $command) { 26 | $command->call('vendor:publish', [ 27 | '--tag' => 'firewall' // Required package 28 | ]); 29 | }) 30 | ->endWith(function (InstallCommand $command) { 31 | // run migration 32 | $command->call('migrate'); 33 | }); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Middleware/WhitelistRangeMiddleware.php: -------------------------------------------------------------------------------- 1 | ip(); 22 | 23 | $allow = false; 24 | 25 | try { 26 | 27 | $currIpIsAllowOrDeny = FilamentFirewall::getFirewallIpModel()::query() 28 | ->where('ip', $currIp) 29 | ->whereNull('prefix_size') 30 | ->get() 31 | ->unique('blocked'); 32 | // have allow/deny record for requesting IP 33 | if ($currIpIsAllowOrDeny->isNotEmpty()) { 34 | // Allow access if "ALLOW ACCESS" for requesting IP 35 | if ($currIpIsAllowOrDeny->filter(fn ($record) => ! $record->blocked)->isNotEmpty()) { 36 | return true; 37 | } 38 | // Only "DENY ACCESS" for requesting IP 39 | else { 40 | return false; 41 | } 42 | } 43 | 44 | $allow = FilamentFirewall::withinWhiteList($currIp); 45 | } catch (QueryException $e) { 46 | // Base table or view not found 47 | 48 | } 49 | 50 | 51 | return $allow; 52 | } 53 | 54 | public function handle($request, \Closure $next) 55 | { 56 | if ($this->skip($request)) { 57 | return $next($request); 58 | } 59 | 60 | if (! $this->allow()) { 61 | return $this->respond(config('firewall.responses.block')); 62 | } 63 | 64 | return $next($request); 65 | } 66 | 67 | public function skip($request) 68 | { 69 | $this->prepare($request); 70 | 71 | 72 | if ($this->isWhitelist()) { 73 | return true; 74 | } 75 | 76 | if (IpUtils::checkIp($this->ip(), config('filament-firewall.skip_whitelist_range', []))) { 77 | return true; 78 | } 79 | 80 | if (!config('firewall.enabled', true)) { 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Models/Ip.php: -------------------------------------------------------------------------------- 1 | 'datetime', 18 | 'blocked' => 'bool', 19 | ]; 20 | } 21 | --------------------------------------------------------------------------------