Passed
Pull Request — master (#1)
by Vince
01:27
created

header::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 1
rs 10
1
<?php
2
/**
3
 * ==================================
4
 * Responsible PHP API
5
 * ==================================
6
 *
7
 * @link Git https://github.com/vince-scarpa/responsibleAPI.git
8
 *
9
 * @api Responible API
10
 * @package responsible\core\headers
11
 *
12
 * @author Vince scarpa <[email protected]>
13
 *
14
 */
15
namespace responsible\core\headers;
16
17
use responsible\core\encoder;
18
use responsible\core\exception;
19
use responsible\core\server;
20
use responsible\core\user;
21
use responsible\core\auth;
22
use responsible\core\interfaces;
23
24
class header extends server implements interfaces\optionsInterface
25
{
26
    use \responsible\core\traits\optionsTrait;
27
28
    /**
29
     * Max age constant
30
     */
31
    const MAX_WINDOW = 3600;
32
33
    /**
34
     * [$REQUEST_APPLICATION]
35
     * @var array
36
     */
37
    private $REQUEST_APPLICATION = array(
38
        'xml' => 'text/xml',
39
        'json' => 'application/json',
40
        'html' => 'text/html',
41
        'array' => 'text/plain',
42
        'object' => 'text/plain',
43
    );
44
45
    /**
46
     * [$REQUEST_TYPE / Default is json]
47
     * @var string
48
     */
49
    private $REQUEST_TYPE;
50
51
    /**
52
     * [$REQUEST_METHOD]
53
     * @var array
54
     */
55
    private $REQUEST_METHOD = [];
56
57
    /**
58
     * [__construct Silence...]
59
     */
60
    public function __construct() {}
61
62
    /**
63
     * [requestType]
64
     * @return void
65
     */
66
    public function requestType($type = 'json')
67
    {
68
        $this->REQUEST_TYPE = $type;
69
    }
70
71
    /**
72
     * [getRequestType]
73
     * @return string
74
     */
75
    public function getRequestType()
76
    {
77
        return $this->REQUEST_TYPE;
78
    }
79
80
    /**
81
     * [requestMethod Set and return the request method]
82
     * @return object
83
     */
84
    public function requestMethod()
85
    {
86
        switch (strtolower($_SERVER['REQUEST_METHOD'])) {
87
88
            case 'get':
89
                $this->REQUEST_METHOD = ['method' => 'get', 'data' => $_GET];
90
                break;
91
92
            case 'post':
93
                $_POST_DATA = $_POST;
94
                $jsonData = json_decode(file_get_contents("php://input"));
95
96
                if (is_object($jsonData) || is_array($jsonData)) {
97
                    $_POST_DATA = json_decode(file_get_contents("php://input"), true);
98
                }
99
                $_POST = array_merge($_REQUEST, $_POST);
100
                $_REQUEST = array_merge($_POST, $_POST_DATA);
101
102
                $this->REQUEST_METHOD = ['method' => 'post', 'data' => $_REQUEST];
103
                break;
104
105
            case 'options':
106
                $this->REQUEST_METHOD = ['method' => 'options', 'data' => $_POST];
107
                echo json_encode(['success'=>true]);
108
                $this->setHeaders();
109
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
110
                break;
111
112
            case 'put':
113
                parse_str(file_get_contents("php://input"), $_PUT);
114
115
                foreach ($_PUT as $key => $value) {
116
                    unset($_PUT[$key]);
117
                    $_PUT[str_replace('amp;', '', $key)] = $value;
118
                }
119
120
                $_REQUEST = array_merge($_REQUEST, $_PUT);
121
122
                $this->REQUEST_METHOD = ['method' => 'put', 'data' => $_REQUEST];
123
                break;
124
125
            case 'patch':
126
                # [TODO]
127
                $this->REQUEST_METHOD = ['method' => 'patch', 'data' => []];
128
                break;
129
130
            case 'delete':
131
                # [TODO]
132
                $this->REQUEST_METHOD = ['method' => 'delete', 'data' => []];
133
                break;
134
135
            default:
136
                $this->REQUEST_METHOD = [];
137
                break;
138
        }
139
    }
140
141
    /**
142
     * [getMethod Get the request method]
143
     * @return object
144
     */
145
    public function getMethod()
146
    {
147
        return (object) $this->REQUEST_METHOD;
148
    }
149
150
    /**
151
     * [setAllowedMethods Set the allowed methods for endpoints]
152
     * @param array $methods [GET, POST, PUT, PATCH, DELETE, ect..]
153
     */
154
    public function setAllowedMethods(array $methods)
155
    {
156
        $this->setHeader('Access-Control-Allow-Methods', array(
157
            implode(',', $methods),
158
        ));
159
160
        $requestMethod = $this->getServerMethod();
161
        if (!in_array($requestMethod, $methods)) {
162
            (new exception\errorException)->error('METHOD_NOT_ALLOWED');
163
        }
164
    }
165
166
    /**
167
     * [getMethod Get the request method]
168
     * @return string
169
     */
170
    public function getServerMethod()
171
    {
172
        if (!isset($_SERVER['REQUEST_METHOD'])) {
173
            return '';
174
        }
175
        return $_SERVER['REQUEST_METHOD'];
176
    }
177
178
    /**
179
     * [getHeaders List all headers Server headers and Apache headers]
180
     * @return array
181
     */
182
    public function getHeaders()
183
    {
184
        $headers_list = headers_list();
185
        foreach ($headers_list as $index => $headValue) {
186
            @list($key, $value) = explode(": ", $headValue);
187
            
188
            if (!is_null($key) && !is_null($value) ) {
189
                $headers_list[$key] = $value;
190
                unset($headers_list[$index]);
191
            }
192
        }
193
194
        if (!function_exists('apache_request_headers')) {
195
            $apacheRequestHeaders = $this->apacheRequestHeaders();
196
        } else {
197
            $apacheRequestHeaders = apache_request_headers();
198
        }
199
200
        if( is_null($apacheRequestHeaders) || empty($apacheRequestHeaders) ) {
201
            return [];
202
        }
203
204
        $apache_headers = array_merge($headers_list, $apacheRequestHeaders);
205
206
        $headers = array();
207
208
        foreach ($_SERVER as $key => $value) {
209
            if (substr($key, 0, 5) != 'HTTP_') {
210
                continue;
211
            }
212
            $header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
213
            $headers[$header] = $value;
214
        }
215
216
        return array_merge($headers, $apache_headers);
217
    }
218
219
    /**
220
     * [setHeader Append aditional headers]
221
     * @return void
222
     */
223
    public function setHeader($header, $headerValue = array(), $status = '', $delimiter = ';')
224
    {
225
        $header = trim(str_replace(':', '', $header)) . ': ';
226
        $headerValue = implode($delimiter . ' ', $headerValue);
227
228
        header($header . $status . $headerValue);
229
    }
230
231
    /**
232
     * [setHeaders Default headers]
233
     * @return void
234
     */
235
    public function setHeaders()
236
    {
237
        $application = 'json';
238
        if (isset($this->REQUEST_APPLICATION[$this->getRequestType()])) {
239
            $application = $this->REQUEST_APPLICATION[$this->getRequestType()];
240
        }
241
242
        $this->setHeader('Content-Type', array(
243
            $application, 'charset=UTF-8',
244
        ));
245
246
        $this->setHeader('Accept-Ranges', array(
247
            'bytes',
248
        ));
249
250
        $this->setHeader('Access-Control-Allow-Credentials', array(
251
            true,
252
        ));
253
254
        $this->setHeader('Access-Control-Allow-Origin', array(
255
            '*',
256
        ));
257
258
        if( !array_key_exists('Access-Control-Allow-Methods', $this->getHeaders()) ) {
259
            $this->setHeader('Access-Control-Allow-Methods', array(
260
                'GET,POST,OPTIONS',
261
            ));
262
        }
263
264
        $this->setHeader('Access-Control-Expose-Headers', array(
265
            'Content-Range',
266
        ));
267
268
        $this->setHeader('Access-Control-Allow-Headers', array(
269
            'origin,x-requested-with,Authorization,cache-control',
270
        ));
271
272
        $this->setHeader('Access-Control-Max-Age', array(
273
            $this->getMaxWindow(),
274
        ));
275
276
        $this->setHeader('Expires', array(
277
            'Wed, 20 September 1978 00:00:00 GMT',
278
        ));
279
280
        $this->setHeader('Cache-Control', array(
281
            'no-store, no-cache, must-revalidate',
282
        ));
283
284
        $this->setHeader('Cache-Control', array(
285
            'post-check=0, pre-check=0',
286
        ));
287
288
        $this->setHeader('Pragma', array(
289
            'no-cache',
290
        ));
291
292
        $this->setHeader('X-Content-Type-Options', array(
293
            'nosniff',
294
        ));
295
296
        $this->setHeader('X-XSS-Protection', array(
297
            '1', 'mode=block',
298
        ));
299
300
        if (isset($this->getOptions()['addHeaders']) &&
301
            (is_array($this->getOptions()['addHeaders']) && !empty($this->getOptions()['addHeaders']))
302
        ) {
303
            foreach ($this->getOptions()['addHeaders'] as $i => $customHeader) {
304
                if (is_array($customHeader) && sizeof($customHeader) == 2) {
305
                    $this->setHeader($customHeader[0], array(
306
                        $customHeader[1],
307
                    ));
308
                }
309
            }
310
        }
311
    }
312
313
    /**
314
     * [apacheRequestHeaders Native replacment fuction]
315
     * https://www.php.net/manual/en/function.apache-request-headers.php#70810
316
     * @return array
317
     */
318
    public function apacheRequestHeaders()
319
    {
320
        $arh = array();
321
        $rx_http = '/\AHTTP_/';
322
323
        foreach ($_SERVER as $key => $val) {
324
            if (preg_match($rx_http, $key)) {
325
                $arh_key = preg_replace($rx_http, '', $key);
326
                $rx_matches = explode('_', $arh_key);
327
                if (count($rx_matches) > 0 and strlen($arh_key) > 2) {
328
                    foreach ($rx_matches as $ak_key => $ak_val) {
329
                        $rx_matches[$ak_key] = ucfirst($ak_val);
330
                    }
331
332
                    $arh_key = implode('-', $rx_matches);
333
                }
334
                $arh[$arh_key] = $val;
335
            }
336
        }
337
        return ($arh);
338
    }
339
340
    /**
341
     * [setHeaderStatus]
342
     * @param void
343
     */
344
    public function setHeaderStatus($status)
345
    {
346
        http_response_code($status);
347
    }
348
349
    /**
350
     * [getHeaderStatus]
351
     * @return integer
352
     */
353
    public function getHeaderStatus()
354
    {
355
        return http_response_code();
356
    }
357
358
    /**
359
     * [hasBearerToken Check if bearer token is present]
360
     * @return string|null
361
     */
362
    public function hasBearerToken()
363
    {
364
        $auth_headers = $this->getHeaders();
365
366
        if (isset($auth_headers["Authorization"]) && !empty($auth_headers["Authorization"])) {
367
368
            list($type, $clientToken) = explode(" ", $auth_headers["Authorization"], 2);
369
370
            if (strcasecmp($type, "Bearer") == 0 && !empty($clientToken)) {
371
                return $clientToken;
372
            }
373
        }
374
        return;
375
    }
376
377
    /**
378
     * [authorizationHeaders Scan for "Authorization" header]
379
     * @return string|array [mixed: string / error]
380
     */
381
    public function authorizationHeaders($skipError = false)
382
    {
383
        $auth_headers = $this->getHeaders();
384
385
        if (isset($auth_headers["Authorization"]) && !empty($auth_headers["Authorization"])) {
386
387
            /**
388
             * Test if it's a Authorization Basic & client_credentials
389
             */
390
            if (isset($_REQUEST['grant_type']) && $_REQUEST['grant_type'] == 'client_credentials') {
391
                if ($refreshToken = $this->accessCredentialHeaders($auth_headers)) {
392
                    return [
393
                        'client_access_request' => $refreshToken,
394
                    ];
395
                }
396
            }
397
398
            /**
399
             * Test if it's a Authorization Bearer & refresh_token
400
             */
401
            if (isset($_REQUEST['grant_type']) && $_REQUEST['grant_type'] == 'refresh_token') {
402
                if ($refreshToken = $this->accessRefreshHeaders($auth_headers)) {
403
                    return [
404
                        'client_access_request' => $refreshToken,
405
                    ];
406
                }
407
            }
408
409
            /**
410
             * Test if it's a Authorization Bearer token
411
             */
412
            if (strcasecmp(trim($auth_headers["Authorization"]), "Bearer") == 0) {
413
                $this->unauthorised();
414
            }
415
416
            list($type, $clientToken) = explode(" ", $auth_headers["Authorization"], 2);
417
418
            if (strcasecmp($type, "Bearer") == 0 && !empty($clientToken)) {
419
                return $clientToken;
420
            } else {
421
                if (!$skipError) {
422
                    $this->unauthorised();
423
                }
424
            }
425
        } else {
426
            if (!$skipError) {
427
                $this->unauthorised();
428
            }
429
        }
430
431
        return '';
432
    }
433
434
    /**
435
     * [accessRefreshHeaders description]
436
     * @return string|array [mixed: string / error]
437
     */
438
    private function accessRefreshHeaders($auth_headers)
439
    {
440
        list($type, $clientToken) = explode(" ", $auth_headers["Authorization"], 2);
441
442
        if (strcasecmp($type, "Bearer") == 0 && !empty($clientToken)) {
443
444
            $user = new user\user;
445
            $account = $user
446
                ->setOptions($this->options)
447
                ->load(
448
                    $clientToken,
449
                    array(
450
                        'loadBy' => 'refresh_token',
451
                        'getJWT' => true,
452
                        'authorizationRefresh' => true,
453
                    )
454
                );
455
456
            if( empty($account) ) {
457
                $this->unauthorised();
458
            }
459
460
            $tokens = [
461
                'token' => $account['JWT'],
462
                'refresh_token' => $account['refreshToken']['token']
463
            ];
464
465
            $account['refreshToken'] = $tokens;
466
467
            return $account;
468
469
        } else {
470
            $this->unauthorised();
471
        }
472
    }
473
474
    /**
475
     * [accessCredentialHeaders Check if the credentials are correct]
476
     * @param  array $auth_headers
477
     * @return string|array [mixed: string / error]
478
     */
479
    private function accessCredentialHeaders($auth_headers)
480
    {
481
        $cipher = new encoder\cipher;
482
483
        list($type, $clientCredentials) = explode(" ", $auth_headers["Authorization"], 2);
484
485
        if (strcasecmp($type, "Basic") == 0 && !empty($clientCredentials)) {
486
            $credentails = explode('/', $clientCredentials);
487
            if (!empty($credentails) && is_array($credentails)) {
488
                $credentails = explode(':', $cipher->decode($clientCredentials));
489
490
                if (!empty($credentails) && is_array($credentails) && sizeof($credentails) == 2) {
491
                    $user = new user\user;
492
                    $user->setAccountID($credentails[0]);
493
494
                    $account = $user
495
                        ->setOptions($this->options)
496
                        ->load(
497
                            $credentails[0],
498
                            array(
499
                                'loadBy' => 'account_id',
500
                                'getJWT' => true,
501
                                'authorizationRefresh' => true,
502
                            )
503
                        );
504
505
                    $tokens = [
506
                        'token' => $account['JWT'],
507
                        'refresh_token' => $account['refreshToken']['token']
508
                    ];
509
510
                    $account['refreshToken'] = $tokens;
511
512
                    if (!empty($account)) {
513
                        if (strcasecmp($account['secret'], $credentails[1]) == 0) {
514
                            return $account;
515
                        }
516
                    }
517
                }
518
            }
519
        } else {
520
            $this->unauthorised();
521
        }
522
    }
523
524
    /**
525
     * [unauthorised Set an unauthorised header]
526
     * @return array [exit exception message]
527
     */
528
    public function unauthorised()
529
    {
530
        $this->setHeaders();
531
532
        $this->setHeader('HTTP/1.1', array(
533
            'Unauthorized',
534
        ), 401);
535
536
        (new exception\errorException)->error('UNAUTHORIZED');
537
    }
538
539
    /**
540
     * [getMaxWindow Get the max control age window]
541
     * @return integer
542
     */
543
    private function getMaxWindow()
544
    {
545
        if ($this->getOptions()) {
546
            if (isset($this->getOptions()['maxWindow']) && !empty($this->getOptions()['maxWindow'])) {
547
                if (!is_numeric($this->getOptions()['maxWindow'])) {
548
                    (new exception\errorException)
549
                        ->message('maxWindow option must be an integer type')
550
                        ->error('APPLICATION_ERROR');
551
                }
552
553
                return $this->getOptions()['maxWindow'];
554
            }
555
        }
556
        return self::MAX_WINDOW;
557
    }
558
559
    /**
560
     * [setData Set request method data]
561
     * @param array $data
562
     * @return void
563
     */
564
    public function setData($data = []) 
565
    {
566
        $this->REQUEST_METHOD['data'] = $data;
567
    }
568
}
569