Completed
Push — master ( 61de71...876a9e )
by Sam
15s
created

CorsService::setAllowHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php namespace Nord\Lumen\Cors;
2
3
use Nord\Lumen\Cors\Contracts\CorsService as CorsServiceContract;
4
use Symfony\Component\HttpFoundation\Request;
5
use Symfony\Component\HttpFoundation\Response;
6
7
class CorsService implements CorsServiceContract
8
{
9
10
    /**
11
     * Allowed request origins.
12
     *
13
     * @var array
14
     */
15
    private $allowOrigins = [];
16
17
    /**
18
     * Allowed HTTP methods.
19
     *
20
     * @var array
21
     */
22
    private $allowMethods = [];
23
24
    /**
25
     * Allowed HTTP headers.
26
     *
27
     * @var array
28
     */
29
    private $allowHeaders = [];
30
31
    /**
32
     * Whether or not the response can be exposed when credentials are present.
33
     *
34
     * @var bool
35
     */
36
    private $allowCredentials = false;
37
38
    /**
39
     * HTTP Headers that are allowed to be exposed to the web browser.
40
     *
41
     * @var array
42
     */
43
    private $exposeHeaders = [];
44
45
    /**
46
     * Indicates how long preflight request can be cached.
47
     *
48
     * @var int
49
     */
50
    private $maxAge = 0;
51
52
53
    /**
54
     * CorsService constructor.
55
     *
56
     * @param array $config
57
     */
58
    public function __construct(array $config = [])
59
    {
60
        if (isset($config['allow_origins'])) {
61
            $this->allowOrigins = $config['allow_origins'];
62
        }
63
64
        if (isset($config['allow_headers'])) {
65
            $this->setAllowHeaders($config['allow_headers']);
66
        }
67
68
        if (isset($config['allow_methods'])) {
69
            $this->setAllowMethods($config['allow_methods']);
70
        }
71
72
        if (isset($config['allow_credentials'])) {
73
            $this->allowCredentials = $config['allow_credentials'];
74
        }
75
76
        if (isset($config['expose_headers'])) {
77
            $this->setExposeHeaders($config['expose_headers']);
78
        }
79
80
        if (isset($config['max_age'])) {
81
            $this->setMaxAge($config['max_age']);
82
        }
83
    }
84
85
86
    /**
87
     * @inheritdoc
88
     */
89
    public function handlePreflightRequest(Request $request): Response
90
    {
91
        $response = new Response();
92
93
        // Do not set any headers if the origin is not allowed
94
        if ($this->isOriginAllowed($request->headers->get('Origin'))) {
95
            $response = $this->setAccessControlAllowOriginHeader($request, $response);
96
97
            if ($this->allowCredentials) {
98
                $response->headers->set('Access-Control-Allow-Credentials', 'true');
99
            }
100
101
            if ($this->maxAge) {
102
                $response->headers->set('Access-Control-Max-Age', (string)$this->maxAge);
103
            }
104
105
            $allowMethods = $this->isAllMethodsAllowed()
106
                ? strtoupper($request->headers->get('Access-Control-Request-Method'))
107
                : implode(', ', $this->allowMethods);
108
109
            $response->headers->set('Access-Control-Allow-Methods', $allowMethods);
110
111
            $allowHeaders = $this->isAllHeadersAllowed()
112
                ? strtolower($request->headers->get('Access-Control-Request-Headers'))
113
                : implode(', ', $this->allowHeaders);
114
115
            $response->headers->set('Access-Control-Allow-Headers', $allowHeaders);
116
        }
117
118
        return $response;
119
    }
120
121
122
    /**
123
     * @inheritdoc
124
     */
125
    public function handleRequest(Request $request, Response $response): Response
126
    {
127
        // Do not set any headers if the origin is not allowed
128
        if ($this->isOriginAllowed($request->headers->get('Origin'))) {
129
            $response = $this->setAccessControlAllowOriginHeader($request, $response);
130
131
            // Set Vary unless all origins are allowed
132
            if (!$this->isAllOriginsAllowed()) {
133
                $vary = $request->headers->has('Vary') ? $request->headers->get('Vary') . ', Origin' : 'Origin';
134
                $response->headers->set('Vary', $vary);
135
            }
136
137
            if ($this->allowCredentials) {
138
                $response->headers->set('Access-Control-Allow-Credentials', 'true');
139
            }
140
141
            if (!empty($this->exposeHeaders)) {
142
                $response->headers->set('Access-Control-Expose-Headers', implode(', ', $this->exposeHeaders));
143
            }
144
        }
145
146
        return $response;
147
    }
148
149
150
    /**
151
     * @inheritdoc
152
     */
153
    public function isCorsRequest(Request $request)
154
    {
155
        return $request->headers->has('Origin');
156
    }
157
158
159
    /**
160
     * @inheritdoc
161
     */
162
    public function isPreflightRequest(Request $request)
163
    {
164
        return $this->isCorsRequest($request) && $request->isMethod('OPTIONS') && $request->headers->has('Access-Control-Request-Method');
165
    }
166
167
    /**
168
     * @param Request  $request
169
     * @param Response $response
170
     *
171
     * @return Response
172
     */
173
    protected function setAccessControlAllowOriginHeader(Request $request, Response $response): Response
174
    {
175
        $origin = $request->headers->get('Origin');
176
177
        if ($this->isAllOriginsAllowed()) {
178
            $response->headers->set('Access-Control-Allow-Origin', '*');
179
        } elseif ($this->isOriginAllowed($origin)) {
180
            $response->headers->set('Access-Control-Allow-Origin', $origin);
181
        }
182
183
        return $response;
184
    }
185
186
187
    /**
188
     * Returns whether or not the origin is allowed.
189
     *
190
     * @param string|null $origin
191
     *
192
     * @return bool
193
     */
194
    protected function isOriginAllowed(?string $origin)
195
    {
196
        if ($this->isAllOriginsAllowed()) {
197
            return true;
198
        }
199
200
        return in_array($origin, $this->allowOrigins);
201
    }
202
203
204
    /**
205
     * Returns whether or not the method is allowed.
206
     *
207
     * @param string|null $method
208
     *
209
     * @return bool
210
     */
211
    protected function isMethodAllowed(?string $method)
212
    {
213
        if ($this->isAllMethodsAllowed()) {
214
            return true;
215
        }
216
217
        return in_array(strtoupper($method), $this->allowMethods);
218
    }
219
220
221
    /**
222
     * Returns whether or not the header is allowed.
223
     *
224
     * @param string|null $header
225
     *
226
     * @return bool
227
     */
228
    protected function isHeaderAllowed(?string $header)
229
    {
230
        if ($this->isAllHeadersAllowed()) {
231
            return true;
232
        }
233
234
        return in_array(strtolower($header), $this->allowHeaders);
235
    }
236
237
238
    /**
239
     * @return bool
240
     */
241
    protected function isAllOriginsAllowed()
242
    {
243
        return in_array('*', $this->allowOrigins);
244
    }
245
246
247
    /**
248
     * @return bool
249
     */
250
    protected function isAllMethodsAllowed()
251
    {
252
        return in_array('*', $this->allowMethods);
253
    }
254
255
256
    /**
257
     * @return bool
258
     */
259
    protected function isAllHeadersAllowed()
260
    {
261
        return in_array('*', $this->allowHeaders);
262
    }
263
264
265
    /**
266
     * @param array $allowMethods
267
     */
268
    protected function setAllowMethods(array $allowMethods)
269
    {
270
        $this->allowMethods = array_map('strtoupper', $allowMethods);
271
    }
272
273
274
    /**
275
     * @param array $allowHeaders
276
     */
277
    protected function setAllowHeaders(array $allowHeaders)
278
    {
279
        $this->allowHeaders = array_map('strtolower', $allowHeaders);
280
    }
281
282
283
    /**
284
     * @param array $exposeHeaders
285
     */
286
    protected function setExposeHeaders(array $exposeHeaders)
287
    {
288
        $this->exposeHeaders = array_map('strtolower', $exposeHeaders);
289
    }
290
291
292
    /**
293
     * @param int $maxAge
294
     */
295
    protected function setMaxAge(int $maxAge)
296
    {
297
        if ($maxAge < 0) {
298
            throw new \InvalidArgumentException('Max age must be a positive number or zero.');
299
        }
300
301
        $this->maxAge = $maxAge;
302
    }
303
}
304