From e6643365d21dbce7311729d09a3d8b7d19941e24 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 7 Feb 2022 02:01:59 +0100 Subject: [PATCH] new: [flood protection] behaviour added simple expiration system to allow flood protections to be added to any functionality --- src/Controller/AppController.php | 3 + .../Component/FloodProtectionComponent.php | 58 +++++++++++++++++++ src/Model/Table/FloodProtectionsTable.php | 21 +++++++ 3 files changed, 82 insertions(+) create mode 100644 src/Controller/Component/FloodProtectionComponent.php create mode 100644 src/Model/Table/FloodProtectionsTable.php diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 006736c..551fd49 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -41,6 +41,7 @@ class AppController extends Controller public $restResponsePayload = null; public $user = null; public $breadcrumb = []; + public $request_ip = null; /** * Initialization hook method. @@ -83,6 +84,7 @@ class AppController extends Controller Configure::write('DebugKit.forceEnable', true); } $this->loadComponent('CustomPagination'); + $this->loadComponent('FloodProtection'); /* * Enable the following component for recommended CakePHP form protection settings. * see https://book.cakephp.org/4/en/controllers/components/form-protection.html @@ -92,6 +94,7 @@ class AppController extends Controller public function beforeFilter(EventInterface $event) { + //$this->request_ip = Configure::read('') $this->loadModel('Users'); $this->Users->checkForNewInstance(); $this->authApiUser(); diff --git a/src/Controller/Component/FloodProtectionComponent.php b/src/Controller/Component/FloodProtectionComponent.php new file mode 100644 index 0000000..d61c6d3 --- /dev/null +++ b/src/Controller/Component/FloodProtectionComponent.php @@ -0,0 +1,58 @@ +remote_ip = $_SERVER[$ip_source]; + $temp = explode(PHP_EOL, $_SERVER[$ip_source]); + if (count($temp) > 1) { + $this->remote_ip = $temp[0]; + } + $this->FloodProtections = TableRegistry::getTableLocator()->get('FloodProtections'); + } + + public function check(string $action, int $limit = 5, int $expiration_time = 300): bool + { + $results = $this->FloodProtections->find('all')->where(['request_action' => $action, 'remote_ip' => $this->remote_ip, 'expiration' > time()])->toList(); + if (count($results) >= $limit) { + throw new TooManyRequestsException(__('Too many {0} requests have been issued ({1} requests allowed ever {2} seconds)', [$action, $limit, $expiration_time])); + } + return false; + } + + public function set(string $action, int $expiration_time = 300): bool + { + $entry = $this->FloodProtections->newEmptyEntity(); + $entry->expiration = time() + $expiration_time; + $entry->remote_ip = $this->remote_ip; + $entry->request_action = $action; + return (bool)$this->FloodProtections->save($entry); + + } + + public function checkAndSet(string $action, int $limit = 5, int $expiration_time = 300): bool + { + $result = $this->check($action, $limit, $expiration_time); + $this->set($action, $expiration_time); + return $result; + } + + public function cleanup(): void + { + $this->FloodProtection->deleteAll(['expiration' < time()]); + } +} diff --git a/src/Model/Table/FloodProtectionsTable.php b/src/Model/Table/FloodProtectionsTable.php new file mode 100644 index 0000000..045fc59 --- /dev/null +++ b/src/Model/Table/FloodProtectionsTable.php @@ -0,0 +1,21 @@ +setDisplayField('request_ip'); + } + + public function validationDefault(Validator $validator): Validator + { + return $validator; + } +}