-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b99e698
commit 6e81957
Showing
9 changed files
with
397 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Middlewares; | ||
|
||
use Auth\Auth; | ||
use RuntimeException; | ||
use App\Middlewares\BaseMiddleware; | ||
|
||
/** | ||
* Middleware that verifies if a user is authenticated | ||
* and has permissions to access the current action. | ||
*/ | ||
class AuthMiddleware extends BaseMiddleware | ||
{ | ||
protected array $actions; | ||
private bool $allowedAction; | ||
private const ALLOWED_ACTION = true; | ||
private const NOT_ALLOWED_ACTION = false; | ||
|
||
/** | ||
* __construct | ||
* | ||
* @param mixed $actions | ||
* @param mixed $allowedAction | ||
* @return void | ||
*/ | ||
public function __construct(array $actions = [], bool $allowedAction = self::NOT_ALLOWED_ACTION) | ||
{ | ||
$this->actions = $actions; | ||
$this->allowedAction = $allowedAction; | ||
} | ||
|
||
/** | ||
* Execute the action, checking authentication and permissions. | ||
* | ||
* @return bool True if the action was executed successfully, false otherwise. | ||
* @throws RuntimeException If the user does not have sufficient permissions. | ||
*/ | ||
public function execute() | ||
{ | ||
if (!(new Auth(app()))->check()) { | ||
$this->validatePermission(); | ||
} | ||
} | ||
|
||
/** | ||
* Validates if the user has permissions to access the current action. | ||
* | ||
* @throws RuntimeException If the user doesn't have permissions | ||
* to access the current action. | ||
*/ | ||
private function validatePermission() | ||
{ | ||
// Get the current action from the application's controller | ||
$action = app()->controller; | ||
$isAllowed = ($this->allowedAction === self::NOT_ALLOWED_ACTION) | ||
? (empty($this->actions) || in_array($action, $this->actions)) | ||
: in_array($action, $this->actions); | ||
|
||
// Throw an exception if the user doesn't have permissions | ||
if (!$isAllowed) { | ||
$this->throwPermissionException(); | ||
} | ||
} | ||
|
||
/** | ||
* Throws a RuntimeException indicating insufficient permissions. | ||
* @throws RuntimeException If the user does not have sufficient permissions. | ||
*/ | ||
private function throwPermissionException() | ||
{ | ||
if (app()->isProduction()) { | ||
$viewFile = config('paths.viewsErrorsPath') . | ||
DIRECTORY_SEPARATOR . config('app.errorPages.500'); | ||
|
||
$output = app()->controller->renderView($viewFile); | ||
die($output); | ||
} | ||
|
||
throw new RuntimeException('You do not have sufficient permissions to perform this operation.'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Middlewares; | ||
|
||
use Fiber; | ||
use App\Middlewares\BaseMiddleware; | ||
|
||
/** | ||
* Middleware to ensure each request is processed in a separate `Fiber` | ||
* | ||
* The `Fiber` class has been added in PHP 8.1+, so this middleware is only used | ||
* on PHP 8.1+. On supported PHP versions, this middleware is automatically | ||
* added to the list of middleware handlers, so there's no need to reference | ||
* this class in application code. | ||
*/ | ||
class FiberMiddleware extends BaseMiddleware | ||
{ | ||
/** | ||
* @param callable $callback | ||
*/ | ||
public function execute(callable $callback) | ||
{ | ||
$fiber = new Fiber(function () use ($callback) { | ||
|
||
$response = yield $callback(); | ||
return $response; | ||
}); | ||
|
||
$fiber->start(); | ||
|
||
if ($fiber->isTerminated()) { | ||
return $fiber->getReturn(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Middlewares; | ||
|
||
use App\Middlewares\BaseMiddleware; | ||
|
||
/** | ||
* Checks if the site is in maintenance mode. | ||
*/ | ||
class MaintenanceMiddleware extends BaseMiddleware | ||
{ | ||
/** | ||
* Configuration property. | ||
* @var mixed | ||
*/ | ||
protected $config; | ||
|
||
/** | ||
* Checks if the user is authenticated and has | ||
* permissions to access the current action. | ||
* | ||
* @return void | ||
*/ | ||
public function execute() | ||
{ | ||
$maintenance = env('APP_DOWN', false); | ||
if ($maintenance === true) | ||
return $this->showViewMaintenance(); | ||
} | ||
|
||
/** | ||
* Displays the maintenance view. | ||
* @return void | ||
*/ | ||
private function showViewMaintenance() | ||
{ | ||
$viewFile = config('paths.viewsErrorsPath') . | ||
DIRECTORY_SEPARATOR . config('view.errorPages.503'); | ||
$output = app()->controller->renderView($viewFile); | ||
die($output); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Middlewares; | ||
|
||
use Fiber; | ||
use App\Middlewares\BaseMiddleware; | ||
|
||
|
||
/** | ||
* Class RateLimiterMiddleware | ||
* This class provides rate limiting functionality for API requests. | ||
*/ | ||
class RateLimiterMiddleware extends BaseMiddleware | ||
{ | ||
private int $maxRequestsPerSecond; | ||
private float $lastRequestTime; | ||
private int $maxBurstRequests = 10; | ||
private int $burstRequestCount = 0; | ||
private int $burstRequestsPerRequest = 1; | ||
private int $lastBurstReset = 0; | ||
private Fiber $rateLimiterFiber; | ||
private array $beforeLimitingHooks; | ||
|
||
/** | ||
* RateLimiter constructor. | ||
* | ||
* @param int $maxRequestsPerSecond Maximum number of requests per second. | ||
* @param int $burstRequestsPerRequest Number of burst requests allowed per single request. | ||
*/ | ||
public function __construct(int $maxRequestsPerSecond, int $burstRequestsPerRequest = 1) | ||
{ | ||
$this->maxRequestsPerSecond = $maxRequestsPerSecond; | ||
$this->lastRequestTime = microtime(true); | ||
$this->burstRequestsPerRequest = $burstRequestsPerRequest; | ||
|
||
$this->rateLimiterFiber = new Fiber(function () { | ||
while (true) { | ||
$currentTime = microtime(true); | ||
$timeSinceLastRequest = $currentTime - $this->lastRequestTime; | ||
|
||
if ($timeSinceLastRequest < 1 / $this->maxRequestsPerSecond) { | ||
usleep((int)((1 / $this->maxRequestsPerSecond - $timeSinceLastRequest) * 1000000)); | ||
} | ||
|
||
$this->lastRequestTime = microtime(true); | ||
Fiber::suspend(); | ||
} | ||
}); | ||
|
||
$this->rateLimiterFiber->start(); | ||
} | ||
|
||
/** | ||
* Resets the burst counter if needed. | ||
* @return void | ||
*/ | ||
private function resetBurstIfNeeded() | ||
{ | ||
if (time() - $this->lastBurstReset > 60) { | ||
$this->burstRequestCount = 0; | ||
$this->lastBurstReset = time(); | ||
} | ||
} | ||
|
||
/** | ||
* Adds a hook/event before applying limiting. | ||
* | ||
* @param callable $beforeLimitingHook Function to execute before | ||
* applying the limitation. | ||
*/ | ||
public function addBeforeLimitingHook(callable $beforeLimitingHook) | ||
{ | ||
$this->beforeLimitingHooks[] = $beforeLimitingHook; | ||
} | ||
|
||
/** | ||
* Makes a request, applying the limitation and executing events/hooks. | ||
* @param callable $apiCall API call function to be executed. | ||
*/ | ||
public function makeRequest(callable $apiCall) | ||
{ | ||
$this->resetBurstIfNeeded(); | ||
|
||
foreach ($this->beforeLimitingHooks as $beforeLimitingHook) { | ||
call_user_func($beforeLimitingHook); | ||
} | ||
|
||
for ($i = 0; $i < $this->burstRequestsPerRequest; $i++) { | ||
if ($this->burstRequestCount < $this->maxBurstRequests) { | ||
$this->burstRequestCount++; | ||
$apiCall(); | ||
} else { | ||
$this->rateLimiterFiber->resume(); | ||
$apiCall(); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @return void | ||
*/ | ||
public function execute() | ||
{ | ||
new self(10); | ||
} | ||
} |
Oops, something went wrong.