GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Http   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 366
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 128
dl 0
loc 366
rs 9.2
c 0
b 0
f 0
wmc 40

10 Methods

Rating   Name   Duplication   Size   Complexity  
A protect() 0 15 3
B login() 0 31 11
A setUsersValidation() 0 5 1
A parseBasic() 0 5 1
A setRealm() 0 5 1
C verify() 0 79 11
A parseDigest() 0 25 3
A setType() 0 7 2
A setUsers() 0 5 1
A __construct() 0 12 6

How to fix   Complexity   

Complex Class

Complex classes like Http often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Http, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the O2System Framework package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @author         Steeve Andrian Salim
9
 * @copyright      Copyright (c) Steeve Andrian Salim
10
 */
11
12
// ------------------------------------------------------------------------
13
14
namespace O2System\Security\Authentication;
15
16
// ------------------------------------------------------------------------
17
18
/**
19
 * Class HttpAuthentication
20
 *
21
 * @package O2System\Security\Authentication
22
 */
23
class Http
24
{
25
    /**
26
     * HttpAuthentication::AUTH_BASIC
27
     *
28
     * Basic Realm HTTP Authentication.
29
     *
30
     * @var int
31
     */
32
    const AUTH_BASIC = 1;
33
34
    /**
35
     * HttpAuthentication::AUTH_DIGEST
36
     *
37
     * Digest HTTP Authentication.
38
     *
39
     * @var int
40
     */
41
    const AUTH_DIGEST = 2;
42
43
    /**
44
     * HttpAuthentication::$type
45
     *
46
     * HTTP authentication type.
47
     *
48
     * @var int
49
     */
50
    private $type;
51
52
    /**
53
     * HttpAuthentication::$realm
54
     *
55
     * HTTP authentication realm.
56
     *
57
     * @var string
58
     */
59
    private $realm;
60
61
    /**
62
     * HttpAuthentication::$authenticate
63
     *
64
     * HTTP authentication validation.
65
     *
66
     * @var \Closure
67
     */
68
    private $validation;
69
70
    /**
71
     * HttpAuthentication::$users
72
     *
73
     * List of users access.
74
     *
75
     * @var array
76
     */
77
    private $users = [];
78
79
    // ------------------------------------------------------------------------
80
81
    /**
82
     * HttpAuthentication::__construct
83
     */
84
    public function __construct($realm, $type = HttpAuthentication::AUTH_BASIC)
0 ignored issues
show
Bug introduced by
The type O2System\Security\Authen...tion\HttpAuthentication was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
85
    {
86
        $this->setRealm($realm)
87
            ->setType($type);
88
89
        if (class_exists('\O2System\Framework', false) or class_exists('\O2System\Reactor', false)) {
90
            if ($security = config()->getItem('security')) {
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

90
            if ($security = /** @scrutinizer ignore-call */ config()->getItem('security')) {
Loading history...
91
                if ($security->offsetExists('httpAuthentication')) {
92
                    $this->users = $security->offsetGet('httpAuthentication');
93
                }
94
            } elseif (false !== ($users = config()->loadFile('HttpAuthentication'))) {
95
                $this->users = $users;
96
            }
97
        }
98
    }
99
100
    // ------------------------------------------------------------------------
101
102
    /**
103
     * HttpAuthentication::setType
104
     *
105
     * Sets WWW-Authenticate Type
106
     *
107
     * @param int $type WWW-Authenticate Type.
108
     *
109
     * @return static
110
     */
111
    public function setType($type)
112
    {
113
        if (in_array($type, [self::AUTH_BASIC, self::AUTH_DIGEST])) {
114
            $this->type = $type;
115
        }
116
117
        return $this;
118
    }
119
120
    // ------------------------------------------------------------------------
121
122
    /**
123
     * HttpAuthentication::setRealm
124
     *
125
     * Sets WWW-Authenticate Realm
126
     *
127
     * @param string $realm WWW-Authenticate Realm.
128
     *
129
     * @return static
130
     */
131
    public function setRealm($realm)
132
    {
133
        $this->realm = trim($realm);
134
135
        return $this;
136
    }
137
138
    // ------------------------------------------------------------------------
139
140
    /**
141
     * HttpAuthentication::setUsers
142
     *
143
     * Sets WWW-Authenticate Users.
144
     *
145
     * @param array $users WWW-Authenticate Users.
146
     *
147
     * @return static
148
     */
149
    public function setUsers(array $users)
150
    {
151
        $this->users = $users;
152
153
        return $this;
154
    }
155
156
    // ------------------------------------------------------------------------
157
158
    /**
159
     * HttpAuthentication::setUsersValidation
160
     *
161
     * Sets WWW-Authenticate Validation.
162
     *
163
     * @param \Closure $closure WWW-Authenticate Validation Callback.
164
     *
165
     * @return static
166
     */
167
    public function setUsersValidation(\Closure $closure)
168
    {
169
        $this->validation = $closure;
170
171
        return $this;
172
    }
173
174
    // ------------------------------------------------------------------------
175
176
    /**
177
     * HttpAuthentication::verify
178
     *
179
     * Verify client access based on request headers.
180
     *
181
     * @return bool
182
     */
183
    public function verify()
184
    {
185
        switch ($this->type) {
186
            default:
187
            case self::AUTH_BASIC:
188
189
                if ($authorization = input()->server('HTTP_AUTHORIZATION')) {
190
                    $authentication = unserialize(base64_decode($authorization));
191
                    if ($this->login($authentication[ 'username' ], $authentication[ 'password' ])) {
192
                        return true;
193
                    }
194
                } else {
195
                    $authentication = $this->parseBasic();
196
                }
197
198
                if ($this->login($authentication[ 'username' ], $authentication[ 'password' ])) {
199
200
                    header('Authorization: Basic ' . base64_encode(serialize($authentication)));
201
202
                    return true;
203
                } else {
204
                    unset($_SERVER[ 'PHP_AUTH_USER' ], $_SERVER[ 'PHP_AUTH_PW' ]);
205
                    $this->protect();
206
                }
207
208
                break;
209
            case self::AUTH_DIGEST:
210
                if ($authorization = input()->server('HTTP_AUTHORIZATION')) {
211
                    $authentication = $this->parseDigest($authorization);
212
                } elseif ($authorization = input()->server('PHP_AUTH_DIGEST')) {
213
                    $authentication = $this->parseDigest($authorization);
214
                }
215
216
                if (isset($authentication) AND
217
                    false !== ($password = $this->login($authentication[ 'username' ]))
218
                ) {
219
                    $A1 = md5($authentication[ 'username' ] . ':' . $this->realm . ':' . $password);
0 ignored issues
show
Bug introduced by
Are you sure $password of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

219
                    $A1 = md5($authentication[ 'username' ] . ':' . $this->realm . ':' . /** @scrutinizer ignore-type */ $password);
Loading history...
220
                    $A2 = md5($_SERVER[ 'REQUEST_METHOD' ] . ':' . $authentication[ 'uri' ]);
221
                    $response = md5(
222
                        $A1
223
                        . ':'
224
                        . $authentication[ 'nonce' ]
225
                        . ':'
226
                        . $authentication[ 'nc' ]
227
                        . ':'
228
                        . $authentication[ 'cnonce' ]
229
                        . ':'
230
                        . $authentication[ 'qop' ]
231
                        . ':'
232
                        . $A2
233
                    );
234
235
                    if ($authentication[ 'response' ] === $response) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $authentication does not seem to be defined for all execution paths leading up to this point.
Loading history...
236
                        header(
237
                            sprintf(
238
                                'Authorization: Digest username="%s", realm="%s", nonce="%s", uri="%s", qop=%s, nc=%s, cnonce="%s", response="%s", opaque="%s"',
239
                                $authentication[ 'username' ],
240
                                $this->realm,
241
                                $authentication[ 'nonce' ],
242
                                $authentication[ 'uri' ],
243
                                $authentication[ 'qop' ],
244
                                $authentication[ 'nc' ],
245
                                $authentication[ 'cnonce' ],
246
                                $response,
247
                                $authentication[ 'opaque' ]
248
                            )
249
                        );
250
251
                        return true;
252
                    }
253
                }
254
255
                unset($_SERVER[ 'PHP_AUTH_DIGEST' ], $_SERVER[ 'HTTP_AUTHORIZATION' ]);
256
                $this->protect();
257
258
                break;
259
        }
260
261
        return false;
262
    }
263
264
    // ------------------------------------------------------------------------
265
266
    /**
267
     * HttpAuthentication::login
268
     *
269
     * Perform WWW-Authenticate Login.
270
     *
271
     * @param string $username Authentication username.
272
     * @param string $password Authentication password.
273
     *
274
     * @return bool|string
275
     */
276
    public function login($username, $password = null)
277
    {
278
        switch ($this->type) {
279
            default:
280
            case self::AUTH_BASIC:
281
                if (isset($username) AND isset($password)) {
282
                    if ($this->validation instanceof \Closure) {
0 ignored issues
show
introduced by
$this->validation is always a sub-type of Closure.
Loading history...
283
                        return call_user_func_array($this->validation, func_get_args());
284
                    } else {
285
                        if (array_key_exists($username, $this->users)) {
286
                            if ($this->users[ $username ] === $password) {
287
                                return true;
288
                            }
289
                        }
290
                    }
291
                }
292
                break;
293
            case self::AUTH_DIGEST:
294
                if (isset($username)) {
295
                    if ($this->validation instanceof \Closure) {
0 ignored issues
show
introduced by
$this->validation is always a sub-type of Closure.
Loading history...
296
                        return call_user_func_array($this->validation, func_get_args());
297
                    } else {
298
                        if (array_key_exists($username, $this->users)) {
299
                            return $this->users[ $username ];
300
                        }
301
                    }
302
                }
303
                break;
304
        }
305
306
        return false;
307
    }
308
309
    // ------------------------------------------------------------------------
310
311
    /**
312
     * HttpAuthentication::parseBasic
313
     *
314
     * Parse Basic Realm HTTP Authentication data.
315
     *
316
     * @return array Basic Realm HTTP Authentication data.
317
     */
318
    protected function parseBasic()
319
    {
320
        return [
321
            'username' => input()->server('PHP_AUTH_USER'),
322
            'password' => input()->server('PHP_AUTH_PW'),
323
        ];
324
    }
325
326
    // ------------------------------------------------------------------------
327
328
    /**
329
     * HttpAuthentication::protect
330
     *
331
     * Protect requested page with HTTP Authorization form dialog.
332
     *
333
     * @return void
334
     */
335
    protected function protect()
336
    {
337
        header('HTTP/1.1 401 Unauthorized');
338
339
        switch ($this->type) {
340
            default:
341
            case self::AUTH_BASIC:
342
                header('WWW-Authenticate: Basic realm="' . $this->realm . '"');
343
                break;
344
            case self::AUTH_DIGEST:
345
                header(
346
                    'WWW-Authenticate: Digest realm="' . $this->realm .
347
                    '", qop="auth", nonce="' . md5(uniqid()) . '", opaque="' . md5(uniqid()) . '"'
348
                );
349
                break;
350
        }
351
    }
352
353
    // ------------------------------------------------------------------------
354
355
    /**
356
     * HttpAuthentication::parseBasic
357
     *
358
     * Parse Digest HTTP Authentication data.
359
     *
360
     * @param string $digest Authentication Digest.
361
     *
362
     * @return array Digest HTTP Authentication data.
363
     */
364
    protected function parseDigest($digest)
365
    {
366
        $digest = str_replace('Digest ', '', $digest);
367
        $digest = trim($digest);
368
369
        $parts = explode(',', $digest);
370
        $parts = array_map('trim', $parts);
371
372
        $data = [];
373
        foreach ($parts as $part) {
374
            $elements = explode('=', $part);
375
            $elements = array_map(
376
                function ($element) {
377
                    return trim(str_replace('"', '', $element));
378
379
                },
380
                $elements
381
            );
382
383
            $data[ $elements[ 0 ] ] = $elements[ 1 ];
384
        }
385
386
        return empty($data)
0 ignored issues
show
Bug Best Practice introduced by
The expression return empty($data) ? false : $data could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
387
            ? false
388
            : $data;
389
    }
390
}