Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 34 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
CorsMiddleware | |
0.00% |
0 / 34 |
|
0.00% |
0 / 3 |
56 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
process | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
30 | |||
isOriginAllowed | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace BO\Zmscitizenapi\Middleware; |
6 | |
7 | use BO\Zmscitizenapi\Localization\ErrorMessages; |
8 | use BO\Zmscitizenapi\Services\Core\LoggerService; |
9 | use Psr\Http\Message\ResponseInterface; |
10 | use Psr\Http\Message\ServerRequestInterface; |
11 | use Psr\Http\Server\MiddlewareInterface; |
12 | use Psr\Http\Server\RequestHandlerInterface; |
13 | |
14 | class CorsMiddleware implements MiddlewareInterface |
15 | { |
16 | private const ERROR_CORS = 'corsOriginNotAllowed'; |
17 | private array $whitelist = []; |
18 | private LoggerService $logger; |
19 | public function __construct(LoggerService $logger) |
20 | { |
21 | $this->logger = $logger; |
22 | $this->whitelist = \App::getCorsAllowedOrigins(); |
23 | } |
24 | |
25 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface |
26 | { |
27 | try { |
28 | $origin = $request->getHeaderLine('Origin'); |
29 | // Allow requests without Origin header (direct browser access) |
30 | if (empty($origin)) { |
31 | /*$this->logger->logInfo('Direct browser request - no Origin header', [ |
32 | 'uri' => (string)$request->getUri(), |
33 | 'headers' => $request->getHeaders() |
34 | ]);*/ |
35 | return $handler->handle($request); |
36 | } |
37 | |
38 | if (!$this->isOriginAllowed($origin)) { |
39 | $this->logger->logInfo(sprintf('CORS blocked - Origin %s not allowed. URI: %s', $origin, $request->getUri())); |
40 | $response = \App::$slim->getResponseFactory()->createResponse(); |
41 | $language = $request->getAttribute('language'); |
42 | $response = $response->withStatus(ErrorMessages::get(self::ERROR_CORS, $language)['statusCode']) |
43 | ->withHeader('Content-Type', 'application/json'); |
44 | $response->getBody()->write(json_encode([ |
45 | 'errors' => [ErrorMessages::get(self::ERROR_CORS, $language)] |
46 | ])); |
47 | return $response; |
48 | } |
49 | |
50 | // Handle preflight OPTIONS requests |
51 | if ($request->getMethod() === 'OPTIONS') { |
52 | $response = \App::$slim->getResponseFactory()->createResponse(200); |
53 | return $response |
54 | ->withHeader('Access-Control-Allow-Origin', $origin) |
55 | ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') |
56 | ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-CSRF-Token') |
57 | ->withHeader('Access-Control-Allow-Credentials', 'true') |
58 | ->withHeader('Access-Control-Max-Age', '86400'); |
59 | } |
60 | |
61 | $response = $handler->handle($request); |
62 | return $response |
63 | ->withHeader('Access-Control-Allow-Origin', $origin) |
64 | ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') |
65 | ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-CSRF-Token') |
66 | ->withHeader('Access-Control-Allow-Credentials', 'true') |
67 | ->withHeader('Access-Control-Max-Age', '86400'); |
68 | } catch (\Throwable $e) { |
69 | $this->logger->logError($e, $request); |
70 | throw $e; |
71 | } |
72 | } |
73 | |
74 | private function isOriginAllowed(string $origin): bool |
75 | { |
76 | return in_array($origin, $this->whitelist, true); |
77 | } |
78 | } |