Test Failed
Push — feature-laravel-5.4 ( 7ff291...137501 )
by Kirill
03:27
created

CorsMiddleware::injectHeaders()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 2
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of laravel.su package.
4
 * For the full copyright and license information, please view the LICENSE
5
 * file that was distributed with this source code.
6
 */
7
declare(strict_types = 1);
8
9
namespace App\Http\Middleware;
10
11
use Illuminate\Http\Request;
12
use Illuminate\Http\Response;
13
14
/**
15
 * Class CorsMiddleware
16
 */
17
class CorsMiddleware
18
{
19
    private const ORIGIN_REQUEST = 1;
20
    private const ORIGIN_ANY     = 2;
21
    private const ORIGIN_LARAVEL = 4;
22
    private const ORIGIN_NONE    = 8;
23
24
    /**
25
     * @var array
26
     */
27
    private $allowedMethods = [
28
        'GET',
29
        'PUT',
30
        'POST',
31
        'HEAD',
32
        'PATCH',
33
        'DELETE',
34
        'OPTIONS',
35
    ];
36
37
    /**
38
     * @var array
39
     */
40
    private $allowedHeaders = [
41
        'X-Key',
42
        'X-Api-Token',
43
        'X-Access-Token',
44
        'X-Requested-With',
45
        'X-Requested-With',
46
47
        'DNT',
48
        'Host',
49
        'Pragma',
50
        'Origin',
51
        'Accept',
52
        'Referer',
53
        'Connection',
54
        'User-Agent',
55
        'Keep-Alive',
56
        'Content-Type',
57
        'Cache-Control',
58
        'Authorization',
59
        'Accept-Charset',
60
        'Content-Length',
61
        'Accept-Encoding',
62
        'Accept-Language',
63
        'If-Modified-Since',
64
    ];
65
66
    /**
67
     * @param Request  $request
68
     * @param \Closure $next
69
     * @return Response
70
     * @throws \InvalidArgumentException
71
     */
72
    public function handle(Request $request, \Closure $next)
73
    {
74
        $response = $next($request);
75
76
        if ($response instanceof Response) {
77
            $response = $this->injectHeaders($request, $response);
78
        }
79
80
        return $response;
81
    }
82
83
    /**
84
     * @param Request  $request
85
     * @param Response $response
86
     * @return Response
87
     * @throws \InvalidArgumentException
88
     */
89
    private function injectHeaders(Request $request, Response $response)
90
    {
91
        $response->headers->add([
92
            // Disable all cache
93
            'Cache-Control'                    => 'private, no-cache, no-store, must-revalidate',
94
            // Echo domain
95
            'Access-Control-Allow-Origin'      => $this->getAccessControlAllowOrigin($request, self::ORIGIN_REQUEST),
96
            // Send allowed headers
97
            'Access-Control-Allow-Headers'     => implode(',', $this->allowedHeaders),
98
            // Send allowed methods
99
            'Access-Control-Allow-Methods'     => implode(',', $this->allowedMethods),
100
            // Allow credentials
101
            'Access-Control-Allow-Credentials' => 'true',
102
            // No P3P policy (?)
103
            'P3P'                              => 'policyref="/w3c/p3p.xml", ' .
104
                'CP="NON DSP COR CUR ADM DEV PSA PSD OUR UNR BUS UNI COM NAV INT DEM STA"',
105
            // Blocks api queries into iframes
106
            'X-Frame-Options'                  => 'DENY',
107
            // Disable autodetect Content-Type
108
            'X-Content-Type-Options'           => 'nosniff',
109
            // Api responses into IE? o_0 Why not? :D
110
            'X-Xss-Protection'                 => '0',
111
        ]);
112
113
        return $response;
114
    }
115
116
    /*
117
     * @param Request $request
118
     * @param int $mode
119
     * @return string
120
     * @throws \InvalidArgumentException
121
     */
122
    private function getAccessControlAllowOrigin(Request $request, int $mode = self::ORIGIN_REQUEST): string
123
    {
124
        switch ($mode) {
125
            case self::ORIGIN_REQUEST:
126
                $headers = $request->headers;
127
                $referer = $headers->get('Referer', '*');
128
129
                return $this->parseUrl($headers->get('Origin', $referer));
130
131
            case self::ORIGIN_ANY:
132
                return '*';
133
134
            case self::ORIGIN_NONE:
135
                return '';
136
137
            case self::ORIGIN_LARAVEL:
138
                return '*.laravel.su';
139
        }
140
141
        throw new \InvalidArgumentException('Invalid CORS mode');
142
    }
143
144
    /**
145
     * @param string $url
146
     * @return string
147
     */
148
    private function parseUrl(string $url): string
149
    {
150
        if ($url === '*') {
151
            return '*';
152
        }
153
154
        $scheme = parse_url($url, PHP_URL_SCHEME) ?: 'http';
155
        $host   = parse_url($url, PHP_URL_HOST);
156
        $port   = parse_url($url, PHP_URL_PORT) ?: 80;
157
158
        return $scheme . '://' . $host . ($port !== 80 ? ':' . $port : '');
159
    }
160
}
161