Issues (15)

src/Middleware/CorsMiddleware.php (1 issue)

Checks if the types of the passed arguments in a function/method call are compatible.

Bug Minor
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->preFlight($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 preFlight(HttpResponse $response, HttpRequest $request)
67
    {
68
        // TODO: Still missing some headers
69
        // https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
70 4
        $corsStatus = self::CORS_OK;
71
72 4
        if (!empty($request->server('HTTP_ORIGIN'))) {
73 4
            $corsStatus = self::CORS_FAILED;
74
75 4
            foreach ((array)$this->corsOrigins as $origin) {
76 4
                if (preg_match("~^.*//$origin$~", $request->server('HTTP_ORIGIN'))) {
0 ignored issues
show
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

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