Passed
Pull Request — master (#11)
by Joao
01:52
created

CorsMiddleware   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 99
Duplicated Lines 0 %

Test Coverage

Coverage 82.86%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 46
c 1
b 0
f 0
dl 0
loc 99
ccs 29
cts 35
cp 0.8286
rs 10
wmc 12

5 Methods

Rating   Name   Duplication   Size   Complexity  
A withAcceptCorsMethods() 0 4 1
A withAcceptCorsHeaders() 0 4 1
A beforeProcess() 0 18 4
A validateCors() 0 25 5
A withCorsOrigins() 0 4 1
1
<?php
2
3
namespace ByJG\RestServer\Middleware;
4
5
use ByJG\RestServer\Exception\Error401Exception;
6
use ByJG\RestServer\HttpRequest;
7
use ByJG\RestServer\HttpResponse;
8
use ByJG\RestServer\ResponseBag;
9
10
class CorsMiddleware implements BeforeMiddlewareInterface
11
{
12
13
    const CORS_OK = 'CORS_OK';
14
    const CORS_FAILED = 'CORS_FAILED';
15
    const CORS_OPTIONS = 'CORS_OPTIONS';
16
17
    protected $corsOrigins = ['.*'];
18
    protected $corsMethods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
19
    protected $corsHeaders = [
20
        'Authorization',
21
        'Content-Type',
22
        'Accept',
23
        'Origin',
24
        'User-Agent',
25
        'Cache-Control',
26
        'Keep-Alive',
27
        'X-Requested-With',
28
        'If-Modified-Since'
29
    ];
30
31
    /**
32
     * Undocumented function
33
     *
34
     * @param mixed $dispatcherStatus
35
     * @param HttpResponse $response
36
     * @param HttpRequest $request
37
     * @return MiddlewareResult
38
     */
39 4
    public function beforeProcess(
40
        $dispatcherStatus,
41
        HttpResponse $response,
42
        HttpRequest $request
43
    )
44
    {
45 4
        $corsStatus = $this->validateCors($response, $request);
46 4
        if ($corsStatus != self::CORS_OK) {
47 2
            if ($corsStatus == self::CORS_OPTIONS) {
48 1
                $response->emptyResponse();
49 1
                $response->getResponseBag()->setSerializationRule(ResponseBag::RAW);
50 1
                return MiddlewareResult::stopProcessingOthers();
51 1
            } elseif ($corsStatus == self::CORS_FAILED) {
52 1
                throw new Error401Exception("CORS verification failed. Request Blocked.");
53
            }
54
        }
55
56 2
        return MiddlewareResult::continue();
57
    }
58
59
    /**
60
     * Undocumented function
61
     *
62
     * @param HttpResponse $response
63
     * @param HttpRequest $request
64
     * @return string
65
     */
66 4
    protected function validateCors(HttpResponse $response, HttpRequest $request)
67
    {
68 4
        $corsStatus = self::CORS_OK;
69
70 4
        if (!empty($request->server('HTTP_ORIGIN'))) {
71 4
            $corsStatus = self::CORS_FAILED;
72
73 4
            foreach ((array)$this->corsOrigins as $origin) {
74 4
                if (preg_match("~^.*//$origin$~", $request->server('HTTP_ORIGIN'))) {
0 ignored issues
show
Bug introduced by
It seems like $request->server('HTTP_ORIGIN') can also be of type boolean; however, parameter $subject of preg_match() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

74
                if (preg_match("~^.*//$origin$~", /** @scrutinizer ignore-type */ $request->server('HTTP_ORIGIN'))) {
Loading history...
75 3
                    $response->addHeader("Access-Control-Allow-Origin", $request->server('HTTP_ORIGIN'));
76 3
                    $response->addHeader('Access-Control-Allow-Credentials', 'true');
77 3
                    $response->addHeader('Access-Control-Max-Age', '86400');    // cache for 1 day
78
79
                    // Access-Control headers are received during OPTIONS requests
80 3
                    if ($request->server('REQUEST_METHOD') == 'OPTIONS') {
81 1
                        $response->addHeader("Access-Control-Allow-Methods", implode(",", array_merge(['OPTIONS'], $this->corsMethods)));
82 1
                        $response->addHeader("Access-Control-Allow-Headers", implode(",", $this->corsHeaders));
83 1
                        return self::CORS_OPTIONS;
84
                    }
85 2
                    $corsStatus = self::CORS_OK;
86 2
                    break;
87
                }
88
            }
89
        }
90 3
        return $corsStatus;
91
    }
92
93 2
    public function withCorsOrigins($origins)
94
    {
95 2
        $this->corsOrigins = $origins;
96 2
        return $this;
97
    }
98
99
    public function withAcceptCorsHeaders($headers)
100
    {
101
        $this->corsHeaders = $headers;
102
        return $this;
103
    }
104
105
    public function withAcceptCorsMethods($methods)
106
    {
107
        $this->corsMethods = $methods;
108
        return $this;
109
    }
110
}
111