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.

API::basicAuthenticateLoginPassword()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace App\Controllers\API;
4
5
use FFMVC\Helpers;
6
7
8
/**
9
 * Api Controller Class.
10
 *
11
 * @author Vijay Mahrra <[email protected]>
12
 * @copyright Vijay Mahrra
13
 * @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
14
 */
15
class API
16
{
17
    /**
18
     * response errors
19
     * 1xx: Informational - Transfer Protocol Information
20
     * 2xx: Success - Client's request successfully accepted
21
     *     - 200 OK, 201 - Created, 202 - Accepted, 204 - No Content (purposefully)
22
     * 3xx: Redirection - Client needs additional action to complete request
23
     *     - 301 - new location for resource
24
     *     - 304 - not modified
25
     * 4xx: Client Error - Client caused the problem
26
     *     - 400 - Bad request - nonspecific failure
27
     *     - 401 - unauthorised
28
     *     - 403 - forbidden
29
     *     - 404 - not found
30
     *     - 405 - method not allowed
31
     *     - 406 - not acceptable (e.g. not in correct format like json)
32
     * 5xx: Server Error - The server was responsible.
33
     *
34
     * @var array errors
35
     */
36
    protected $errors = [];
37
38
    /**
39
     * response data.
40
     *
41
     * @var array data
42
     */
43
    protected $data = [];
44
45
    /**
46
     * response params.
47
     *
48
     * @var array params
49
     */
50
    protected $params = [];
51
52
    /**
53
     * Error format required by RFC6794.
54
     *
55
     * @var type
56
     * @link https://tools.ietf.org/html/rfc6749
57
     */
58
    protected $OAuthErrorTypes = [
59
        'invalid_request' => [
60
            'code' => 'invalid_request',
61
            'description' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.',
62
            'uri' => '',
63
            'state' => '',
64
            'status' => 400
65
        ],
66
        'invalid_credentials' => [
67
            'code' => 'invalid_credentials',
68
            'description' => 'Credentials for authentication were invalid.',
69
            'uri' => '',
70
            'state' => '',
71
            'status' => 403
72
        ],
73
        'invalid_client' => [
74
            'code' => 'invalid_client',
75
            'description' => 'Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).',
76
            'uri' => '',
77
            'state' => '',
78
            'status' => 401
79
        ],
80
        'invalid_grant' => [
81
            'code' => 'invalid_grant',
82
            'description' => 'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.',
83
            'uri' => '',
84
            'state' => '',
85
            'status' => 401
86
        ],
87
        'unsupported_grant_type' => [
88
            'code' => 'unsupported_grant_type',
89
            'description' => 'The authorization grant type is not supported by the authorization server.',
90
            'uri' => '',
91
            'state' => '',
92
            'status' => 400
93
        ],
94
        'unauthorized_client' => [
95
            'code' => 'unauthorized_client',
96
            'description' => 'The client is not authorized to request an authorization code using this method.',
97
            'uri' => '',
98
            'state' => '',
99
            'status' => 401
100
        ],
101
        'access_denied' => [
102
            'code' => 'access_denied',
103
            'description' => 'The resource owner or authorization server denied the request.',
104
            'uri' => '',
105
            'state' => '',
106
            'status' => 400
107
        ],
108
        'unsupported_response_type' => [
109
            'code' => 'unsupported_response_type',
110
            'description' => 'The authorization server does not support obtaining an authorization code using this method.',
111
            'uri' => '',
112
            'state' => '',
113
            'status' => 400
114
        ],
115
        'invalid_scope' => [
116
            'code' => 'invalid_scope',
117
            'description' => 'The requested scope is invalid, unknown, or malformed.',
118
            'uri' => '',
119
            'state' => '',
120
            'status' => 400
121
        ],
122
        'server_error' => [
123
            'code' => 'server_error',
124
            'description' => 'The authorization server encountered an unexpected condition that prevented it from fulfilling the request.',
125
            'uri' => '',
126
            'state' => '',
127
            'status' => 500
128
        ],
129
        'temporarily_unavailable' => [
130
            'code' => 'temporarily_unavailable',
131
            'description' => 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.',
132
            'uri' => '',
133
            'state' => '',
134
            'status' => 400
135
        ],
136
    ];
137
138
    /**
139
     * The OAuth Error to return if an OAuthError occurs.
140
     *
141
     * @var boolean|array OAuthError
142
     */
143
    protected $OAuthError = null;
144
145
    /**
146
     * initialize
147
     */
148
    public function __construct()
149
    {
150
        $this->params['http_status'] = 200;
151
    }
152
153
    /**
154
     * compile and send the json response.
155
     *
156
     * @param \Base $f3
157
     * @return void
158
     */
159
    public function afterRoute(\Base $f3)
160
    {
161
        $this->params['headers'] = empty($this->params['headers']) ? [] : $this->params['headers'];
162
        $this->params['headers']['Version'] = $f3->get('api.version');
163
164
        // if an OAuthError is set, return that too
165
        $data = [];
166
        if (!empty($this->OAuthError)) {
167
            $data['error'] = $this->OAuthError;
168
        }
169
170
        if (count($this->errors)) {
171
            foreach ($this->errors as $code => $message) {
172
                $data['error']['errors'][] = [
173
                    'code' => $code,
174
                    'message' => $message
175
                ];
176
            }
177
            ksort($this->errors);
178
        }
179
180
        Helpers\Response::json(array_merge($data, $this->data), $this->params);
181
    }
182
183
    /**
184
     * add to the list of errors that occured during this request.
185
     *
186
     * @param string $code        the error code
187
     * @param string $message     the error message
188
     * @param null|int    $http_status the http status code
189
     * @return void
190
     */
191
    public function failure(string $code, string $message, int $http_status = null)
192
    {
193
        $this->errors[$code] = $message;
194
195
        if (!empty($http_status)) {
196
            $this->params['http_status'] = $http_status;
197
        }
198
    }
199
200
    /**
201
     * Get OAuth Error Type.
202
     *
203
     * @param string $type
204
     *
205
     * @return array|bool error type or boolean false
206
     */
207
    protected function getOAuthErrorType(string $type)
208
    {
209
        return array_key_exists($type, $this->OAuthErrorTypes) ? $this->OAuthErrorTypes[$type] : false;
210
    }
211
212
    /**
213
     * Set the RFC-compliant OAuth Error to return.
214
     *
215
     * @param string $code  of error code from RFC
216
     * @return array|boolean the OAuth error array
217
     */
218
    public function setOAuthError(string $code)
219
    {
220
        $this->OAuthError = $this->getOAuthErrorType($code);
221
222
        // only set https status if not set anywhere else
223
        if ($this->params['http_status'] == 200) {
224
            $this->params['http_status'] = $this->OAuthError['status'];
225
        }
226
227
        return $this->OAuthError;
228
    }
229
230
    /**
231
     * Basic Authentication for email:password
232
     *
233
     * Check that the credentials match the database
234
     * Cache result for 30 seconds.
235
     *
236
     * @return bool success/failure
237
     */
238
    public function basicAuthenticateLoginPassword(): bool
239
    {
240
        $auth = new \Auth(new \DB\SQL\Mapper(\Registry::get('db'), 'users', ['email', 'password'], 30), [
241
            'id' => 'email',
242
            'pw' => 'password',
243
        ]);
244
245
        return (bool) $auth->basic(function ($pw) {
246
            return Helpers\Str::password($pw);
247
        });
248
    }
249
250
    /**
251
     * Authentication for client_id and client_secret
252
     *
253
     * Check that the credentials match a registered app
254
     * @param string $clientId the client id to check
255
     * @param string $clientSecret the client secret to check
256
     * @return bool success/failure
257
     */
258
    public function authenticateClientIdSecret(string $clientId, string $clientSecret): bool
259
    {
260
        if (empty($clientId) || empty($clientSecret)) {
261
            return false;
262
        }
263
        // validate client_id/secret
264
        return false;
265
    }
266
267
    /**
268
     * Basic Authentication for client_id:client_secret
269
     *
270
     * Check that the credentials match a registered app
271
     *
272
     * @return bool success/failure
273
     */
274
    public function basicAuthenticateClientIdSecret(): bool
275
    {
276
        $f3 = \Base::instance();
277
        return $this->authenticateClientIdSecret($f3->get('REQUEST.PHP_AUTH_USER'), $f3->get('REQUEST.PHP_AUTH_PW'));
278
    }
279
280
    /**
281
     * Validate the provided access token or get the bearer token from the incoming http request
282
     * do $f3->set('access_token') if OK.
283
     *
284
     * Or login using app token with HTTP Auth using one of
285
     *
286
     * email:password
287
     * email:access_token
288
     *
289
     * Or by URL query string param - ?access_token=$access_token
290
     *
291
     * Sets hive vars: user[] (mandatory), api_app[] (optional) and userScopes[]
292
     *
293
     * @return null|boolean true/false on valid access credentials
294
     */
295
    protected function validateAccess()
296
    {
297
        $f3 = \Base::instance();
298
299
        // return if forcing access to https and not https
300
        if ('http' == $f3->get('SCHEME') && !empty($f3->get('api.https'))) {
301
            $this->failure('api_connection_error', "Connection only allowed via HTTPS!", 400);
302
            $this->setOAuthError('unauthorized_client');
303
304
            return false;
305
        }
306
307
        // get token from request to set the user and app
308
        // override if anything in basic auth or client_id/secret AFTER
309
        $token = $f3->get('REQUEST.access_token');
310
        if (!empty($token)) {
311
            // token does not exist!
312
            $tokenExists = null; // perform a real check here against your db!
313 View Code Duplication
            if (null == $tokenExists) {
314
                $this->failure('authentication_error', "The token does not exist!", 401);
315
                $this->setOAuthError('invalid_grant');
316
317
                return false;
318
            }
319
            // check token is not out-of-date
320
            $expires = 0; // get the expiry from your db!
321 View Code Duplication
            if (time() > $expires) {
322
                $this->failure('authentication_error', "The token expired!", 401);
323
                $this->setOAuthError('invalid_grant');
324
325
                return false;
326
            }
327
            // if token found load the user for the token from your db!
328
        }
329
330
        // login with client_id and client_secret in request OR login/password using basic auth
331
        if ($f3->get('REQUEST.PHP_AUTH_USER')) {
332
            if ($this->basicAuthenticateClientIdSecret()) {
333
            } elseif ($this->basicAuthenticateLoginPassword()) {
334
            }
335
        }
336
337
        $userAuthenticated = false; // do a proper check here based on the above
338
        if (!$userAuthenticated) {
339
            $this->failure('authentication_error', "Not possible to authenticate the request.", 400);
340
            $this->setOAuthError('invalid_credentials');
341
342
            return false;
343
        }
344
345
        return true;
346
    }
347
348
    /**
349
     * catch-all
350
     *
351
     * @return void
352
     */
353
    public function unknown()
354
    {
355
        $this->setOAuthError('invalid_request');
356
        $this->failure('api_connection_error', 'Unknown API Request', 400);
357
    }
358
359
}
360