Compare commits
38 commits
evolutions
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0c4dbc6f0 | ||
|
|
6f687a8cd4 | ||
|
|
71453c6a8c | ||
|
|
cb88b7ddb5 | ||
|
|
c67e0b476f | ||
|
|
cd6bb56bb6 | ||
|
|
ddb4c688dc | ||
|
|
c2eb783716 | ||
|
|
92dbff8b1c | ||
|
|
1a4f23106f | ||
|
|
facbea062e | ||
|
|
267cec076b | ||
|
|
594f03f834 | ||
|
|
c54dad85ee | ||
|
|
0299d83971 | ||
|
|
6bed78da14 | ||
|
|
f376e1dd4b | ||
|
|
a4e0fafa16 | ||
|
|
26f818b6b9 | ||
|
|
4afdddde91 | ||
|
|
e5056174bc | ||
|
|
61efdb6099 | ||
|
|
5cf7ba3b45 | ||
|
|
572d90b1c5 | ||
|
|
f142732754 | ||
|
|
625685de1a | ||
|
|
6c775737c0 | ||
|
|
b05ed93c2b | ||
|
|
d2066e2340 | ||
|
|
55d3213542 | ||
|
|
5120fb122c | ||
|
|
37d036116d | ||
|
|
45f525bfb1 | ||
|
|
a96ec5560f | ||
|
|
5f0c6b136f | ||
|
|
c4db27a523 | ||
|
|
c538f5d349 | ||
|
|
0b398200c8 |
29 changed files with 12235 additions and 498 deletions
|
|
@ -3,16 +3,22 @@ root = true
|
|||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.json]
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
|
||||
[*.scss]
|
||||
indent_size = 2
|
||||
|
||||
[*.{tsx,ts,cjs}]
|
||||
indent_size = 2
|
||||
|
|
|
|||
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -1,7 +1,4 @@
|
|||
workspace.code-workspace
|
||||
vendor
|
||||
.phpunit.result.cache
|
||||
.phpunit.cache
|
||||
.vscode/launch.json
|
||||
composer.lock
|
||||
build
|
||||
vendor/
|
||||
node_modules/
|
||||
/auth.json
|
||||
/.npmrc
|
||||
11
.gitlab/issue_templates/bug.md
Normal file
11
.gitlab/issue_templates/bug.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
## Comportement constaté
|
||||
|
||||
|
||||
## Comportement attendu
|
||||
|
||||
|
||||
## Solution envisagée
|
||||
|
||||
|
||||
## Étapes pour la reproduction du bug
|
||||
*
|
||||
7
.gitlab/issue_templates/feature.md
Normal file
7
.gitlab/issue_templates/feature.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
## Présentation du besoin
|
||||
|
||||
|
||||
## Implémentation
|
||||
|
||||
|
||||
## Maquettes, documentations...
|
||||
3
.husky/commit-msg
Normal file
3
.husky/commit-msg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
npx --no -- commitlint --config node_modules/@uwil/husky-config/commitlint.config.cjs --edit $1
|
||||
27
.vscode/settings.json
vendored
27
.vscode/settings.json
vendored
|
|
@ -1,3 +1,26 @@
|
|||
{
|
||||
"phpcs.executablePath": "vendor/bin/phpcs"
|
||||
}
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#00d6d6",
|
||||
"activityBar.activeBorder": "#1a1d02",
|
||||
"activityBar.background": "#00d6d6",
|
||||
"activityBar.foreground": "#15202b",
|
||||
"activityBar.inactiveForeground": "#15202b99",
|
||||
"activityBarBadge.background": "#df00df",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"sash.hoverBorder": "#00d6d6",
|
||||
"statusBar.background": "#00a3a3",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#00d6d6",
|
||||
"statusBarItem.remoteBackground": "#00a3a3",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#00a3a3",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#00a3a399",
|
||||
"titleBar.inactiveForeground": "#e7e7e799",
|
||||
"commandCenter.border": "#e7e7e799"
|
||||
},
|
||||
"peacock.remoteColor": "#00a3a3",
|
||||
"intelephense.environment.phpVersion": "8.3.0",
|
||||
"intelephense.completion.fullyQualifyGlobalConstantsAndFunctions": true,
|
||||
"intelephense.phpdoc.useFullyQualifiedNames": true
|
||||
}
|
||||
101
CHANGELOG.md
Normal file
101
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to `laravel-admin-secure` will be documented in this file.
|
||||
|
||||
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
- 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/).
|
||||
|
||||
## **[v6.0.1] - 05/10/2026**
|
||||
|
||||
### Changed
|
||||
|
||||
- chore: ⬆️ bump dependencies
|
||||
|
||||
## **[v6.0.0] - 22/06/2026**
|
||||
|
||||
### Added
|
||||
|
||||
- feat: ✨ add commit lint from husky configuration
|
||||
|
||||
### Changed
|
||||
|
||||
- chore: 🔧 update linters configuration
|
||||
- chore: 🔧 rename package into laravel-admin-secure
|
||||
|
||||
### Fixed
|
||||
|
||||
- perf: 🚨 resolve linters errors
|
||||
|
||||
## **[v5.1.0] - 17/04/2026**
|
||||
|
||||
### Added
|
||||
|
||||
- chore: :arrow_up: Added Laravel 13 compatibility
|
||||
|
||||
## **[v5.0.0] - 09/01/2026**
|
||||
|
||||
### Added
|
||||
|
||||
- feat: ✨ Remove LDC Web API dependency
|
||||
|
||||
## **[v4.0.0] - 07/04/2025**
|
||||
|
||||
### Added
|
||||
|
||||
- feat: ✨ add Laravel 12 compatibility
|
||||
|
||||
## **[v3.0.0] - 03/09/2024**
|
||||
|
||||
### Added
|
||||
|
||||
- Add Laravel 11 compatibility
|
||||
- Bump deps for Laravel 11
|
||||
|
||||
### Changed
|
||||
|
||||
- Change namespace for UWIL complience
|
||||
|
||||
## **[v2.0.3] - 04/04/2024**
|
||||
|
||||
### Added
|
||||
|
||||
- Add staging to environnements where sync command should be executed
|
||||
|
||||
### Changed
|
||||
|
||||
- PHPCS and PHPSTAN fixes
|
||||
|
||||
## **[v2.0.2] - 19/09/2024**
|
||||
|
||||
### Changed
|
||||
|
||||
- fixed artisan command to fetch list
|
||||
|
||||
## **[v2.0.1] - 19/09/2023**
|
||||
|
||||
### Changed
|
||||
|
||||
- Delete API key
|
||||
- Artisan config cache fix
|
||||
- Cache was ignored
|
||||
|
||||
## **[v2.0.0] - 05/06/2023**
|
||||
|
||||
### Added
|
||||
|
||||
- Bump laravel 10
|
||||
- Add manual Cird possibility
|
||||
|
||||
### Changed
|
||||
|
||||
- Allows connections from private ip ranges
|
||||
- Add posibility to config manual CIDR exceptions
|
||||
|
||||
## **[v1.0.1] - 04/07/2022**
|
||||
|
||||
- Run scheduled only on production envirronement
|
||||
|
||||
## **[v1.0.0] - 24/06/2022**
|
||||
|
||||
- Initial release
|
||||
7
MIGRATION.md
Normal file
7
MIGRATION.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Migration
|
||||
|
||||
All notable changes to `laravel-admin-secure` will be documented in this file.
|
||||
|
||||
## v5.1.1 -> v6.0.0
|
||||
- renommage du package de `ldc-web-admin-secure-laravel` en `laravel-admin-secure`,
|
||||
- changement du namespace pour le middleware de `Uwil\AdminSecure\Laravel\Http\Middleware` à `Uwil\LaravelAdminSecure\Middlewares`.
|
||||
80
README.md
80
README.md
|
|
@ -1,35 +1,59 @@
|
|||
# LDC Web Admin Secure Laravel
|
||||
# UWIL Laravel Admin Secure
|
||||
|
||||
## Install on Laravel
|
||||
```
|
||||
composer req ldc/web-admin-secure-laravel:1.0.1
|
||||
```
|
||||
.env.example :
|
||||
UWIL Laravel Admin Secure est un package à installer afin de protéger nos interfaces d'administration à une liste d'IPs spécifiques.
|
||||
|
||||
```dotenv
|
||||
# ADMIN SECURE
|
||||
# - API ENDPOINT (Optional)
|
||||
LDC_WEB_API_ENDPOINT="https://ldc-web.ldc.fr/api/ldc-networks"
|
||||
# - API KEY
|
||||
LDC_WEB_API_KEY="CE8S52Qq7stf5k8frF4T"
|
||||
## Table des matières
|
||||
|
||||
- [UWIL Laravel Admin Secure](#uwil-laravel-admin-secure)
|
||||
- [Table des matières](#table-des-matières)
|
||||
- [Compatibilité](#Compatibilité)
|
||||
- [Documentation](#documentation)
|
||||
- [Installation](#installation)
|
||||
- [Détails](#détails)
|
||||
- [Changelog](#changelog)
|
||||
- [Copyright et licence](#copyright-et-licence)
|
||||
|
||||
## Compatibilité
|
||||
|
||||
| Version | [1.0.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/1.0.0?ref_type=tags) -> [1.0.1](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/1.0.1?ref_type=tags) | [2.0.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/2.0.0?ref_type=tags) -> [2.0.3](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/2.0.3?ref_type=tags) | [3.0.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/3.0.0?ref_type=tags) | [4.0.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/4.0.0?ref_type=tags) -> [5.0.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/5.0.0?ref_type=tags) | [5.1.0](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/5.1.0?ref_type=tags) -> [6.0.1](https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/tree/6.0.1?ref_type=tags) |
|
||||
|:-------------------------------------------------------------------------|:--:|:--:|:--:|:--:|:--:|
|
||||
| [laravel\framework^13.0](https://github.com/laravel/framework/tree/13.x) | ❌ | ❌ | ❌ | ❌ | ✅ |
|
||||
| [laravel\framework^12.0](https://github.com/laravel/framework/tree/12.x) | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| [laravel\framework^11.0](https://github.com/laravel/framework/tree/11.x) | ❌ | ❌ | ✅ | ✅ | ✅ |
|
||||
| [laravel\framework^10.0](https://github.com/laravel/framework/tree/10.x) | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
| [laravel\framework^9.0](https://github.com/laravel/framework/tree/10.x) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| [laravel\framework^8.0](https://github.com/laravel/framework/tree/10.x) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
## Documentation
|
||||
|
||||
### Installation
|
||||
|
||||
1. Installer ce package composer comme ceci:
|
||||
|
||||
```bash
|
||||
composer req uwil/laravel-admin-secure
|
||||
```
|
||||
|
||||
2. Ajouter simplement l'alias du middleware `admin-secure` sur les routes que vous voulez protéger,
|
||||
|
||||
2. Enfin, renseigner la liste des IPs dans votre `.env`:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
## Changelog
|
||||
|
||||
## Available Commands
|
||||
```
|
||||
ldcweb:admin-secure-sync
|
||||
```
|
||||
## Usage
|
||||
Put 'admin-secure' middleware on route to protect it.
|
||||
## Cron configure
|
||||
Dernière version v6.0.1.
|
||||
|
||||
Nothing to do command is already configureed to run everyday.
|
||||
You just need to install this package.
|
||||
**!! Runs only in production !!**
|
||||
Voir le journal de modifications [CHANGELOG.md](CHANGELOG.md) pour les détails.
|
||||
|
||||
## Copyright et licence
|
||||
|
||||
Laravel Admin Secure est écrit et maintenu par UWIL.
|
||||
|
||||
Copyright (c) 2022 et au-delà UWIL.
|
||||
|
|
|
|||
12
changelog.md
12
changelog.md
|
|
@ -1,12 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## **Version 2.0.0**
|
||||
*Changed*
|
||||
- Compatible only with **php 8.2**
|
||||
- Allow connections from private ip ranges
|
||||
- Allow setting Cidr list into config
|
||||
## **Version 1.0.1**
|
||||
*Changed*
|
||||
- Run scheduled only on production envirronement
|
||||
## **Version 1.0.0**
|
||||
### Initial release
|
||||
1
commitlint.config.cjs
Normal file
1
commitlint.config.cjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('@uwil/husky-config/commitlint.config.cjs');
|
||||
|
|
@ -1,49 +1,64 @@
|
|||
{
|
||||
"name": "ldc/web-admin-secure-laravel",
|
||||
"name": "uwil/laravel-admin-secure",
|
||||
"description": "Restrict access to LDC Network",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Vincent BRIDIER",
|
||||
"email": "vincent.bridier@ldc.fr",
|
||||
"homepage": "https://www.ldc.fr"
|
||||
}
|
||||
"version": "6.0.1",
|
||||
"keywords": [
|
||||
"uwil",
|
||||
"laravel",
|
||||
"security"
|
||||
],
|
||||
"readme": "https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/blob/main/README.md",
|
||||
"support": {
|
||||
"email": "digital@ldc.fr",
|
||||
"issues": "https://lab.s-web.fr/ldc-web/projets/laravel-admin-secure/-/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"illuminate/support": "~10",
|
||||
"laravel/framework": "~10",
|
||||
"dxw/cidr": "^3.1"
|
||||
"php": "^8.3",
|
||||
"illuminate/support": "~11|~12|~13",
|
||||
"laravel/framework": "~11|~12|~13",
|
||||
"symfony/options-resolver": "^7.0|^8.0",
|
||||
"dxw/cidr": "^4.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9",
|
||||
"squizlabs/php_codesniffer": "^3",
|
||||
"orchestra/testbench": "^8",
|
||||
"slevomat/coding-standard": "^7",
|
||||
"phpunit/php-code-coverage": "^9"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"LDC\\WebAdminSecureLaravel\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"LDC\\WebAdminSecureLaravel\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"LDC\\WebAdminSecureLaravel\\AdminSecureServiceProvider"
|
||||
]
|
||||
}
|
||||
"driftingly/rector-laravel": "^2.3",
|
||||
"larastan/larastan": "^3.9",
|
||||
"laravel/pint": "^1.29",
|
||||
"orchestra/testbench": "^11.1",
|
||||
"slevomat/coding-standard": "^8.3",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"@php ./vendor/bin/testbench package:discover --ansi"
|
||||
],
|
||||
"test": "vendor/bin/phpunit"
|
||||
"phpcs": [
|
||||
"./vendor/bin/phpcs"
|
||||
],
|
||||
"phpstan": [
|
||||
"./vendor/bin/phpstan"
|
||||
],
|
||||
"rector": [
|
||||
"./vendor/bin/rector"
|
||||
]
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Uwil\\LaravelAdminSecure\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Uwil\\LaravelAdminSecure\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Uwil\\LaravelAdminSecure\\AdminSecureServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
|
|
|||
10198
composer.lock
generated
Normal file
10198
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,8 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'endpoint' => env('LDC_WEB_API_ENDPOINT', 'https://ldc-web.ldc.fr/api/ldc-networks'),
|
||||
'apiKey' => env('LDC_WEB_API_KEY'),
|
||||
'cidrManualAllowedList' => collect(explode(',', strval(env(
|
||||
'LDC_WEB_CIDR_MANUAL_ALLOWED_LIST'
|
||||
))))->filter()
|
||||
'cidrList' => env('UWIL_ADMIN_SECURE_CIDR_LIST'),
|
||||
'cidrProjectAllowedList' => env('UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST'),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1324
package-lock.json
generated
Normal file
1324
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
8
package.json
Normal file
8
package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "laravel-admin-secure",
|
||||
"version": "6.0.1",
|
||||
"description": "UWIL K8S utils",
|
||||
"dependencies": {
|
||||
"@uwil/husky-config": "^1.0.1"
|
||||
}
|
||||
}
|
||||
144
phpcs.xml
144
phpcs.xml
|
|
@ -1,122 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="RealWorld" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
<ruleset name="Php Coding Standard"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
|
||||
<description>Optimized standard: Static analysis complementary to Pint.</description>
|
||||
|
||||
<arg name="colors" />
|
||||
<arg name="extensions" value="php" />
|
||||
<arg name="parallel" value="75" />
|
||||
<file>src/</file>
|
||||
<rule ref="PSR1" />
|
||||
<rule ref="PSR12" />
|
||||
<file>config/</file>
|
||||
|
||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||
<exclude-pattern>*/storage/*</exclude-pattern>
|
||||
<exclude-pattern>*/packages/*</exclude-pattern>
|
||||
<exclude-pattern type="relative">*.blade.php</exclude-pattern>
|
||||
|
||||
<rule ref="PSR12"/>
|
||||
|
||||
<rule ref="Generic.Classes.DuplicateClassName"/>
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement"/>
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
|
||||
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
|
||||
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
|
||||
<rule ref="Generic.Commenting.DocComment.SpacingBeforeTags"/>
|
||||
<rule ref="Generic.Commenting.Todo"/>
|
||||
<rule ref="Generic.Commenting.Fixme"/>
|
||||
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
|
||||
<rule ref="Generic.Files.ByteOrderMark"/>
|
||||
<rule ref="Generic.Files.LineEndings"/>
|
||||
<rule ref="Generic.Files.LineLength"/>
|
||||
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
|
||||
<rule ref="Generic.Formatting.MultipleStatementAlignment"/>
|
||||
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
|
||||
<rule ref="Generic.Functions.CallTimePassByReference"/>
|
||||
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
|
||||
<rule ref="Generic.Metrics.CyclomaticComplexity"/>
|
||||
<rule ref="Generic.Metrics.CyclomaticComplexity">
|
||||
<properties>
|
||||
<property name="complexity" value="10"/>
|
||||
<property name="absoluteComplexity" value="20"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Generic.Metrics.NestingLevel"/>
|
||||
<rule ref="Generic.NamingConventions.ConstructorName"/>
|
||||
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
|
||||
<rule ref="Generic.NamingConventions.CamelCapsFunctionName"/>
|
||||
|
||||
<rule ref="Generic.PHP.DeprecatedFunctions"/>
|
||||
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions"/>
|
||||
<rule ref="Generic.PHP.NoSilencedErrors"/>
|
||||
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
|
||||
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
|
||||
<rule ref="Generic.WhiteSpace.ScopeIndent"/>
|
||||
<rule ref="MySource.PHP.EvalObjectFactory"/>
|
||||
<rule ref="MySource.PHP.GetRequestData"/>
|
||||
<rule ref="MySource.PHP.AjaxNullComparison"/>
|
||||
<rule ref="PEAR.Classes.ClassDeclaration"/>
|
||||
<rule ref="PEAR.Commenting.InlineComment"/>
|
||||
<rule ref="PSR1.Classes.ClassDeclaration"/>
|
||||
<rule ref="PSR1.Files.SideEffects"/>
|
||||
<rule ref="PSR2.Classes.ClassDeclaration"/>
|
||||
<rule ref="PSR2.Classes.PropertyDeclaration"/>
|
||||
<rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
|
||||
<rule ref="PSR2.ControlStructures.SwitchDeclaration"/>
|
||||
<rule ref="PSR2.Files.EndFileNewline"/>
|
||||
<rule ref="PSR2.Methods.MethodDeclaration"/>
|
||||
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
|
||||
<rule ref="PSR2.Namespaces.UseDeclaration"/>
|
||||
<rule ref="Squiz.PHP.CommentedOutCode"/>
|
||||
<rule ref="Squiz.PHP.DisallowMultipleAssignments">
|
||||
<exclude name="Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure" />
|
||||
</rule>
|
||||
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
|
||||
|
||||
<rule ref="Squiz.PHP.DiscouragedFunctions">
|
||||
<properties>
|
||||
<property name="error" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.PHP.Eval"/>
|
||||
<rule ref="Squiz.PHP.GlobalKeyword"/>
|
||||
<rule ref="Squiz.PHP.Heredoc"/>
|
||||
<rule ref="Squiz.PHP.InnerFunctions"/>
|
||||
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
|
||||
<rule ref="Squiz.PHP.NonExecutableCode"/>
|
||||
<rule ref="Squiz.Scope.MemberVarScope"/>
|
||||
<rule ref="Squiz.Scope.MethodScope"/>
|
||||
<rule ref="Squiz.Scope.StaticThisUsage"/>
|
||||
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.MemberVarSpacing">
|
||||
<properties>
|
||||
<property name="spacingBeforeFirst " value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
|
||||
<properties>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
|
||||
<properties>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
|
||||
<rule ref="Squiz.Commenting.BlockComment">
|
||||
<exclude name="Squiz.Commenting.BlockComment.WrongStart" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.ClassComment">
|
||||
<exclude name="Squiz.Commenting.ClassComment.Missing" />
|
||||
<exclude name="Squiz.Commenting.ClassComment.TagNotAllowed" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.DocCommentAlignment"/>
|
||||
<rule ref="Squiz.Commenting.EmptyCatchComment"/>
|
||||
<rule ref="Squiz.Commenting.FunctionComment">
|
||||
<exclude name="Squiz.Commenting.FunctionComment.MissingParamComment" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>
|
||||
<rule ref="Squiz.Commenting.InlineComment">
|
||||
<exclude name="Squiz.Commenting.InlineComment.DocBlock" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.LongConditionClosingComment">
|
||||
<exclude name="Squiz.Commenting.LongConditionClosingComment.SpacingBefore" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.PostStatementComment"/>
|
||||
<rule ref="Squiz.Commenting.VariableComment"/>
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse" />
|
||||
|
||||
<!-- For Laravel migrations -->
|
||||
<rule ref="Generic.NamingConventions.ConstructorName"/>
|
||||
<rule ref="Generic.NamingConventions.CamelCapsFunctionName"/>
|
||||
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<properties>
|
||||
<property name="lineLimit" value="150"/>
|
||||
<property name="absoluteLineLimit" value="250"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses" />
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
|
||||
<properties>
|
||||
<property name="spacesCountAroundEqualsSign" value="0" />
|
||||
<property name="declareOnFirstLine" value="false" />
|
||||
<property name="linesCountBeforeDeclare" value="1" />
|
||||
<property name="linesCountAfterDeclare" value="1" />
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
|
||||
<exclude-pattern>*/database/migrations/*</exclude-pattern>
|
||||
</rule>
|
||||
|
|
|
|||
14
phpstan.neon
Normal file
14
phpstan.neon
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
includes:
|
||||
- vendor/larastan/larastan/extension.neon
|
||||
|
||||
parameters:
|
||||
paths:
|
||||
- config/
|
||||
- src/
|
||||
|
||||
level: 7
|
||||
|
||||
ignoreErrors:
|
||||
-
|
||||
message: '#Called .env. outside of the config directory#'
|
||||
path: config/*
|
||||
80
pint.json
Normal file
80
pint.json
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"preset": "laravel",
|
||||
"exclude": [
|
||||
"packages"
|
||||
],
|
||||
"rules": {
|
||||
"indentation_type": true,
|
||||
"single_line_empty_body": false,
|
||||
"types_spaces": {
|
||||
"space": "single",
|
||||
"space_multiple_catch": "single"
|
||||
},
|
||||
"control_structure_braces": true,
|
||||
"new_with_parentheses": {
|
||||
"anonymous_class": true,
|
||||
"named_class": true
|
||||
},
|
||||
"class_definition": {
|
||||
"single_item_single_line": false,
|
||||
"single_line": false,
|
||||
"space_before_parenthesis": true
|
||||
},
|
||||
"curly_braces_position": {
|
||||
"allow_single_line_empty_anonymous_classes": false,
|
||||
"classes_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"anonymous_classes_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"control_structures_opening_brace": "same_line",
|
||||
"functions_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"anonymous_functions_opening_brace": "same_line"
|
||||
},
|
||||
"binary_operator_spaces": {
|
||||
"default": "align_single_space_minimal",
|
||||
"operators": {
|
||||
"=": "align_single_space",
|
||||
"|": "single_space",
|
||||
"=>": "align_single_space",
|
||||
".=": "align_single_space"
|
||||
}
|
||||
},
|
||||
"concat_space": {
|
||||
"spacing": "one"
|
||||
},
|
||||
"align_multiline_comment": {
|
||||
"comment_type": "all_multiline"
|
||||
},
|
||||
"array_indentation": true,
|
||||
"method_chaining_indentation": true,
|
||||
"statement_indentation": {
|
||||
"stick_comment_to_next_continuous_control_statement": true
|
||||
},
|
||||
"no_unused_imports": true,
|
||||
"fully_qualified_strict_types": {
|
||||
"import_symbols": true,
|
||||
"leading_backslash_in_global_namespace": true,
|
||||
"phpdoc_tags": []
|
||||
},
|
||||
"visibility_required": {
|
||||
"elements": [
|
||||
"const",
|
||||
"method",
|
||||
"property"
|
||||
]
|
||||
},
|
||||
"no_useless_else": true,
|
||||
"function_declaration": {
|
||||
"closure_function_spacing": "one",
|
||||
"closure_fn_spacing": "one",
|
||||
"trailing_comma_single_line": false
|
||||
},
|
||||
"single_line_after_imports": true,
|
||||
"combine_consecutive_issets": true,
|
||||
"array_syntax": {
|
||||
"syntax": "short"
|
||||
},
|
||||
"single_quote": true,
|
||||
"blank_line_after_namespace": true,
|
||||
"blank_line_after_opening_tag": true,
|
||||
"declare_strict_types": true
|
||||
}
|
||||
}
|
||||
52
rector.php
Normal file
52
rector.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use RectorLaravel\Rector\Class_\CommandHiddenPropertyToHiddenAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\DescriptionPropertyToDescriptionAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\FillablePropertyToFillableAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\HiddenPropertyToHiddenAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\ModelCastsPropertyToCastsMethodRector;
|
||||
use RectorLaravel\Rector\Class_\SignaturePropertyToSignatureAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\TablePropertyToTableAttributeRector;
|
||||
use RectorLaravel\Rector\Class_\WithoutTimestampsPropertyToWithoutTimestampsAttributeRector;
|
||||
use RectorLaravel\Rector\MethodCall\ValidationRuleArrayStringValueToArrayRector;
|
||||
use RectorLaravel\Set\LaravelLevelSetList;
|
||||
use RectorLaravel\Set\LaravelSetList;
|
||||
use RectorLaravel\Set\LaravelSetProvider;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withSetProviders(LaravelSetProvider::class)
|
||||
->withPaths([
|
||||
__DIR__ . '/config',
|
||||
__DIR__ . '/src',
|
||||
])
|
||||
->withPhpSets()
|
||||
->withComposerBased(laravel: true)
|
||||
->withSets([
|
||||
LaravelLevelSetList::UP_TO_LARAVEL_130,
|
||||
LaravelSetList::LARAVEL_ARRAYACCESS_TO_METHOD_CALL,
|
||||
LaravelSetList::LARAVEL_CODE_QUALITY,
|
||||
LaravelSetList::LARAVEL_COLLECTION,
|
||||
LaravelSetList::LARAVEL_IF_HELPERS,
|
||||
LaravelSetList::LARAVEL_LEGACY_FACTORIES_TO_CLASSES,
|
||||
LaravelSetList::LARAVEL_TYPE_DECLARATIONS,
|
||||
])
|
||||
->withTypeCoverageLevel(0)
|
||||
->withDeadCodeLevel(0)
|
||||
->withCodeQualityLevel(0)
|
||||
->withSkip([
|
||||
// Disable Laravel notations.
|
||||
CommandHiddenPropertyToHiddenAttributeRector::class,
|
||||
DescriptionPropertyToDescriptionAttributeRector::class,
|
||||
FillablePropertyToFillableAttributeRector::class,
|
||||
HiddenPropertyToHiddenAttributeRector::class,
|
||||
ModelCastsPropertyToCastsMethodRector::class,
|
||||
SignaturePropertyToSignatureAttributeRector::class,
|
||||
TablePropertyToTableAttributeRector::class,
|
||||
WithoutTimestampsPropertyToWithoutTimestampsAttributeRector::class,
|
||||
|
||||
// Validation.
|
||||
ValidationRuleArrayStringValueToArrayRector::class,
|
||||
]);
|
||||
|
|
@ -1,249 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\LaravelAdminSecure;
|
||||
|
||||
use Dxw\CIDR\IPAddress;
|
||||
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 LDC\WebAdminSecureLaravel\Exceptions\AdminSecureException;
|
||||
use LDC\WebAdminSecureLaravel\Exceptions\AdminSecureInvalidAdressException;
|
||||
use Uwil\LaravelAdminSecure\Exceptions\AdminSecureException;
|
||||
|
||||
abstract class AdminSecure
|
||||
{
|
||||
/**
|
||||
* The cache key where to store ip list.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static string $cache_key = 'ldcweb_ip_list';
|
||||
|
||||
/**
|
||||
* The cache key where to store config cidr list.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static string $cache_config_key = 'ldcweb_ip_cidr_config_list';
|
||||
|
||||
/**
|
||||
* Private ipV4 CIDR list
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
/** @var array<int,string> 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<int,string> 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';
|
||||
|
||||
/** @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 \LDC\WebAdminSecureLaravel\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
|
||||
{
|
||||
// ! HA PROXY HACK.
|
||||
$ip = request()->server('HTTP_X_FORWARDED_FOR') ?? request()->getClientIp();
|
||||
$ip = IPAddress::Make($ip);
|
||||
// ! REVERSE PROXY HACK.
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Ip Adress list from LDC Web
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address>
|
||||
* @throws \LDC\WebAdminSecureLaravel\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 = config('admin-secure.endpoint');
|
||||
$key = config('admin-secure.apiKey');
|
||||
if (!$endpoint or !\filter_var($endpoint, \FILTER_VALIDATE_URL)) {
|
||||
throw new AdminSecureException('LDC_WEB_API_ENDPOINT is required in .env to ba a valid url.');
|
||||
}
|
||||
if (!$key) {
|
||||
throw new AdminSecureException('LDC_WEB_API_KEY is required in .env .');
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Ip Adress list from LDC Web
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>
|
||||
* @throws \LDC\WebAdminSecureLaravel\AdminSecureException If Wrong env value.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
public static function fetchConfigAllowedCidrList(): array
|
||||
{
|
||||
$allowed_cidr_config_list = \collect(config('admin-secure.cidrManualAllowedList'))->filter();
|
||||
$allowed_cidr_config_list_hash = \md5($allowed_cidr_config_list->implode(','));
|
||||
$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::flush(self::$cache_config_key);
|
||||
}
|
||||
|
||||
/** @var array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range> $allowed_cidr_list */
|
||||
return \collect(Cache::rememberForever(self::$cache_config_key, fn () => [
|
||||
'list' => $allowed_cidr_list,
|
||||
'hash' => $allowed_cidr_config_list_hash
|
||||
]))->get('list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ip list from cache
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address>
|
||||
*/
|
||||
public static function getCacheList(): array
|
||||
{
|
||||
try {
|
||||
$ip_list = Cache::get(self::$cache_key, self::fetchIpList());
|
||||
self::validateIpList($ip_list);
|
||||
/** @var array<\Dxw\CIDR\IPv4Address|\Dxw\CIDR\IPv6Address> $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
|
||||
{
|
||||
self::validateIpList($ip_list);
|
||||
Cache::put(self::$cache_key, $ip_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate ip list
|
||||
*
|
||||
* @param array $ip_list
|
||||
* @return void
|
||||
* @throws \LDC\WebAdminSecureLaravel\Exceptions\AdminSecureInvalidAdressException If an IP is invalid.
|
||||
* Get Admin secure options.
|
||||
*/
|
||||
private static function validateIpList(array $ip_list): void
|
||||
public static function getOptions(): Options
|
||||
{
|
||||
\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)));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (! self::$options) {
|
||||
self::$options = new Options(\config('admin-secure'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse adresses
|
||||
*
|
||||
* @param \Illuminate\Http\Client\Response $response
|
||||
* @return array<\Dxw\CIDR\IPAddress>
|
||||
*/
|
||||
private static function parseAdresses(Response $response): array
|
||||
{
|
||||
return \collect($response->json())
|
||||
->map(fn (string $ip) => IPAddress::Make($ip)->unwrap())->all();
|
||||
return self::$options;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\LaravelAdminSecure;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Routing\Router;
|
||||
use LDC\WebAdminSecureLaravel\Console\SyncFromLDCWeb;
|
||||
use LDC\WebAdminSecureLaravel\Http\Middleware\AdminSecureMiddleware;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Uwil\LaravelAdminSecure\Middlewares\AdminSecureMiddleware;
|
||||
|
||||
class AdminSecureServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -22,20 +22,8 @@ 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'));
|
||||
|
||||
// * 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('ldcweb:admin-secure-sync')->daily();
|
||||
}
|
||||
});
|
||||
// * Set filter condition.
|
||||
$this->app->singleton('hasToFilterIp', fn (Application $app) => $app->environment('production', 'staging'));
|
||||
|
||||
/** @var \Illuminate\Routing\Router */
|
||||
$router = $this->app->make(Router::class);
|
||||
|
|
@ -47,6 +35,7 @@ class AdminSecureServiceProvider extends ServiceProvider
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\Override]
|
||||
public function register()
|
||||
{
|
||||
$this->mergeConfigFrom(
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use LDC\WebAdminSecureLaravel\AdminSecure;
|
||||
|
||||
/**
|
||||
* Send Report Status To LDC Web.
|
||||
*
|
||||
* @author Vincent BRIDIER <vincent.bridier@ldc.fr>
|
||||
*/
|
||||
class SyncFromLDCWeb extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ldcweb:admin-secure-sync';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync IP list from LDC Web';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return integer
|
||||
* @throws \LDC\WebAdminSecureLaravel\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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel\Exceptions;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\LaravelAdminSecure\Exceptions;
|
||||
|
||||
class AdminSecureException extends \Exception
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel\Exceptions;
|
||||
|
||||
class AdminSecureInvalidAdressException extends AdminSecureException
|
||||
{
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace LDC\WebAdminSecureLaravel\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Dxw\CIDR\IP;
|
||||
use Dxw\CIDR\IPv4Address;
|
||||
use Dxw\CIDR\IPv4Range;
|
||||
use Dxw\CIDR\IPv6Address;
|
||||
use Dxw\CIDR\IPv6Range;
|
||||
use Illuminate\Http\Request;
|
||||
use LDC\WebAdminSecureLaravel\AdminSecure;
|
||||
|
||||
class AdminSecureMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!\app()->make('hasToFilterIp')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$client_ip = AdminSecure::fetchClientIp();
|
||||
$ip_is_allowed = \collect(AdminSecure::fetchIpList())
|
||||
->reduce(fn (bool|null $initial, IPv4Address|IPv6Address $ip_address) => $initial || IP::contains(
|
||||
$ip_address,
|
||||
$client_ip
|
||||
)->unwrap());
|
||||
|
||||
$ip_is_allowed_by_config = \collect(AdminSecure::fetchConfigAllowedCidrList())
|
||||
->reduce(fn (bool|null $initial, IPv4Range|IPv6Range $ip_range) => $initial || IP::contains(
|
||||
$ip_range,
|
||||
$client_ip
|
||||
)->unwrap());
|
||||
|
||||
if ($ip_is_allowed or $ip_is_allowed_by_config or AdminSecure::ipIsPrivate($client_ip)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
abort(403, \trans('Denied IP'));
|
||||
}
|
||||
}
|
||||
44
src/Middlewares/AdminSecureMiddleware.php
Normal file
44
src/Middlewares/AdminSecureMiddleware.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\LaravelAdminSecure\Middlewares;
|
||||
|
||||
use Closure;
|
||||
use Dxw\CIDR\IPv4Range;
|
||||
use Dxw\CIDR\IPv6Range;
|
||||
use Illuminate\Http\Request;
|
||||
use Uwil\LaravelAdminSecure\AdminSecure;
|
||||
|
||||
class AdminSecureMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (! \app()->make('hasToFilterIp')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$client_ip = AdminSecure::fetchClientIp();
|
||||
|
||||
$ip_is_allowed = \collect(AdminSecure::getOptions()->getCidrList())
|
||||
->reduce(fn (?bool $initial, IPv4Range | IPv6Range $ip_range) => $initial || $ip_range->containsAddress(
|
||||
$client_ip
|
||||
));
|
||||
|
||||
$ip_is_allowed_by_config = \collect(AdminSecure::getOptions()->getCidrAllowedList())
|
||||
->reduce(fn (?bool $initial, IPv4Range | IPv6Range $ip_range) => $initial || $ip_range->containsAddress(
|
||||
$client_ip
|
||||
));
|
||||
|
||||
if ($ip_is_allowed or $ip_is_allowed_by_config or AdminSecure::ipIsPrivate($client_ip)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
abort(403, \trans('Denied IP'));
|
||||
}
|
||||
}
|
||||
149
src/Options.php
Normal file
149
src/Options.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\LaravelAdminSecure;
|
||||
|
||||
use Dxw\CIDR\IPv4Range;
|
||||
use Dxw\CIDR\IPv6Range;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Uwil\LaravelAdminSecure\Exceptions\AdminSecureException;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
final class Options
|
||||
{
|
||||
/** @var array<string, mixed> The configuration options. */
|
||||
private $options;
|
||||
|
||||
/** @var \Symfony\Component\OptionsResolver\OptionsResolver The options resolver. */
|
||||
private $resolver;
|
||||
|
||||
/** @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<string, mixed> $options The configuration options.
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->resolver = new OptionsResolver();
|
||||
|
||||
$this->configureOptions($this->resolver);
|
||||
|
||||
$this->options = $this->resolver->resolve($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cidr list.
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>
|
||||
*/
|
||||
public function getCidrList(): array
|
||||
{
|
||||
return $this->options['cidrList'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cidr project allowed list.
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>
|
||||
*/
|
||||
public function getCidrAllowedList(): array
|
||||
{
|
||||
return $this->options['cidrProjectAllowedList'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the options of the client.
|
||||
*
|
||||
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver The resolver for the options.
|
||||
*/
|
||||
private function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'cidrList' => [],
|
||||
'cidrProjectAllowedList' => [],
|
||||
]);
|
||||
|
||||
$resolver->setNormalizer('cidrList', $this->normalizeCidrList(...));
|
||||
$resolver->setNormalizer('cidrProjectAllowedList', $this->normalizeCidrProjectList(...));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise Cidr list for cidrList option.
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>
|
||||
*
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function normalizeCidrList(
|
||||
\Symfony\Component\OptionsResolver\Options $options,
|
||||
?string $value
|
||||
): array {
|
||||
// phpcs:enable
|
||||
return $this->doNormalizeCidrList($value, 'cidrList');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise Cidr list for cidrProjectAllowedList option.
|
||||
*
|
||||
* @return array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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{list: array<\Dxw\CIDR\IPv4Range|\Dxw\CIDR\IPv6Range>, hash: string}|null $cached */
|
||||
$cached = Cache::get($cacheConfigKey);
|
||||
|
||||
// Flush cache if config hash changed
|
||||
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);
|
||||
if ($ip_range->isErr()) {
|
||||
throw new AdminSecureException(\trans(
|
||||
'CIDR LIST not valid (ipv4 or ipv6 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;
|
||||
}
|
||||
|
||||
return $cached['list'];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue