Passed
Push — master ( 89971b...1a5c63 )
by Vince
01:47
created

userLoad::getUserJWT()   C

Complexity

Conditions 12
Paths 78

Size

Total Lines 65
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 33
c 2
b 0
f 0
nc 78
nop 1
dl 0
loc 65
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\user
11
 *
12
 * @author Vince scarpa <[email protected]>
13
 *
14
 */
15
namespace responsible\core\user;
16
17
use responsible\core\auth;
18
use responsible\core\exception;
19
use responsible\core\headers;
20
use responsible\core\keys;
21
use responsible\core\route;
22
use responsible\core\encoder;
23
24
class userLoad extends user
25
{
26
    /**
27
     * [$column Load user by what column]
28
     * @var string
29
     */
30
    private $column;
31
32
    /**
33
     * [$requestRefreshToken Request a new refresh JWT]
34
     * @var boolean
35
     */
36
    private $requestRefreshToken = false;
37
38
    /**
39
     * [$requestRefreshToken Request a new refresh JWT, request from authorization headers]
40
     * @var boolean
41
     */
42
    private $authorizationRefresh = false;
43
44
    /**
45
     * [$requestRefreshToken Get an encoded user token]
46
     * @var boolean
47
     */
48
    private $getToken = false;
49
50
    /**
51
     * [$secret]
52
     * @var string
53
     */
54
    private $secret = '';
55
56
    /**
57
     * [$keys]
58
     */
59
    private $keys;
60
61
    /**
62
     * [$jwt]
63
     * @var object
64
     */
65
    private $jwt;
66
67
    /**
68
     * [$property]
69
     * @var string
70
     */
71
    protected $property;
72
73
    /**
74
     * [$secret request by system to append the users secret from DB]
75
     * @var boolean
76
     */
77
    private $secretAppend = false;
78
79
    public function __construct($property = null, $options)
80
    {
81
        if (is_null($property) || empty($property)) {
82
            (new exception\errorException)
83
                ->message('No load property was provided!')
84
                ->error('ACCOUNT_ID');
85
        }
86
87
        $loadBy = $this->checkVal($options, 'loadBy', 'account_id');
88
89
        $this->getToken = $this->checkVal($options, 'getJWT');
90
        $this->requestRefreshToken = $this->checkVal($options, 'refreshToken');
91
        $this->authorizationRefresh = $this->checkVal($options, 'authorizationRefresh');
92
93
        $this->keys = new keys\key;
94
        $this->jwt = new auth\jwt;
95
96
        $this->setColumn($loadBy);
97
        $this->setProperty($property);
98
        $this->timeNow();
99
100
        $this->secret = $this->getDefaults()['config']['MASTER_KEY'];
101
102
        if( isset($options['secret']) && $options['secret'] == 'append' ) {
103
            $this->secretAppend = true;
104
        }
105
    }
106
107
    /**
108
     * [account Get the account]
109
     * @return array
110
     */
111
    public function account()
112
    {
113
        /**
114
         * [Validate the requested account exists]
115
         */
116
        $account = $this->DB()
117
            ->row(
118
                "SELECT
119
                USR.uid,
120
                USR.account_id,
121
                USR.name,
122
                USR.mail,
123
                USR.status,
124
                USR.access,
125
                USR.secret,
126
                USR.refresh_token,
127
                TKN.bucket
128
                FROM responsible_api_users USR
129
                INNER JOIN responsible_token_bucket TKN
130
                    ON USR.account_id = TKN.account_id
131
132
                    WHERE {$this->column} = ?
133
                    AND status = 1
134
            ;",
135
                array(
136
                    $this->property,
137
                ),
138
                \PDO::FETCH_OBJ
139
            );
140
141
        if( $this->secretAppend ) {
142
            $this->secret = $account->secret;
143
        }
144
145
        if (!empty($account)) {
146
            $this->setAccountID($account->account_id);
147
148
            $this->secret = $account->secret;
149
            
150
            if ($this->requestRefreshToken) {
151
                $account->refresh_token = $this->refreshTokenGenerate($account);
152
                $sentToken = (new headers\header)->hasBearerToken();
153
154
                if( $sentToken ) {
155
                    /**
156
                     * [$jwt Decode the JWT]
157
                     * @var auth\jwt
158
                     */
159
                    $jwt = new auth\jwt;
160
                    $decoded = $jwt
161
                        ->setOptions($this->getOptions())
162
                        ->token($sentToken)
163
                        ->key('payloadOnly')
164
                        ->decode()
165
                    ;
166
167
                    $leeway = ($this->checkVal($this->options['jwt'], 'leeway')) 
168
                        ?: $this->jwt->getLeeway()
169
                    ;
170
                    $absSeconds = ($decoded['exp'] - ($this->timeNow() - $leeway));
171
172
                    if( $absSeconds > 0 ) {
173
                        $account->JWT = $sentToken;
174
                    }
175
176
                    $account->tokenExpire = [
177
                        'tokenExpire' => [
178
                            'leeway' => $leeway,
179
                            'expiresIn' => $absSeconds,
180
                            'expiresString' => $this->tokenExpiresIn($absSeconds),
181
                        ]
182
                    ];
183
                }
184
185
                $account->refreshToken = [
186
                    'token' => $sentToken,
187
                    'refresh' => $account->refresh_token
188
                ];
189
            }
190
191
            if ($this->getToken) {
192
                $account->JWT = $this->getUserJWT();
193
                $account->refresh_token = $this->refreshTokenGenerate($account);
194
                $account->refreshToken = ['token' => $account->refresh_token];
195
            }
196
197
            return (array) $account;
198
        }
199
200
        (new exception\errorException)->error('UNAUTHORIZED');
201
    }
202
203
    /**
204
     * [refreshToken New way to request refresh token]
205
     * @return string
206
     */
207
    public function refreshTokenGenerate($account)
208
    {
209
        $offset = 86400;
210
        $time = ($this->timeNow()+$offset);
211
212
        if( isset($account->refresh_token) && !empty($account->refresh_token) ) {
213
            $raToken = explode('.', $account->refresh_token);
214
            if( !empty($raToken) ) {
215
                $raToken = array_values(array_filter($raToken));
216
                $time = ($raToken[0] <= ($this->timeNow()-$offset) ) ? ($this->timeNow()+$offset) : $raToken[0];
217
            }
218
        }
219
220
        $cipher = new encoder\cipher;
221
        $refreshHash = $account->account_id.':'.$account->secret;
222
        $refreshHash = $cipher->encode($cipher->hash('sha256', $refreshHash, $account->secret));
223
224
        $refreshHash = $time.'.'.$refreshHash;
225
        $account->refreshToken = $refreshHash;
226
227
        $updateProp = [
228
            'where' => [
229
                'account_id' => $account->account_id
230
            ],
231
            'update' => [
232
                'refresh_token' => $refreshHash,
233
            ]
234
        ];
235
        parent::update($updateProp);
236
237
        return $refreshHash;
238
    }
239
240
    /**
241
     * [refreshJWT Get a refresh JWT]
242
     * @return array
243
     */
244
    public function refreshJWT($userPayload)
245
    {
246
        $leeway = ($this->checkVal($this->options['jwt'], 'leeway')) ?: $this->jwt->getLeeway();
247
        $expires = $userPayload['payload']['exp'] + $leeway;
248
249
        $this->options['jwt'] = [
250
            'leeway' => $leeway,
251
            'issuedAt' => $expires,
252
            'expires' => $expires,
253
            'notBeFor' => $expires - 10,
254
        ];
255
256
        $absSeconds = ($userPayload['payload']['exp'] - ($this->timeNow() - $leeway));
257
258
        return [
259
            'tokenExpire' => [
260
                'leeway' => $leeway,
261
                'expiresIn' => $absSeconds,
262
                'expiresDate' => date(\DateTime::ISO8601, ($userPayload['payload']['exp'] + $leeway)),
263
                'expiresString' => $this->tokenExpiresIn($absSeconds),
264
            ],
265
            'refresh' => $this->getUserJWT(),
266
        ];
267
    }
268
269
    /**
270
     * [futureToken Get a future refresh JWT]
271
     * @return array|null
272
     */
273
    public function futureToken()
274
    {
275
        if (!isset($this->secret)) {
276
            (new exception\errorException)
277
                ->message('There was an error trying to retrieve the server master key. Please read the documentation on setting up a configuration file')
278
                ->error('NO_CONTENT');
279
        }
280
281
        $key = $this->secret;
282
        $userPayload = $this->getJWT($key);
283
284
        if (empty($userPayload) || !isset($userPayload['payload'])) {
285
            return;
286
        }
287
288
        /**
289
         * Check unlimited access set
290
         */
291
        $skipExpiry = $this->checkVal($this->options, 'unlimited', true);
292
293
        /**
294
         * Check token expiry
295
         */
296
        if($this->checkVal($userPayload['payload'], 'exp') && !$skipExpiry) {
297
            return $this->refreshJWT($userPayload);
298
        }
299
300
        return;
301
    }
302
303
    /**
304
     * [tokenExpiresIn Get the token expiry as a string]
305
     * @param  integer $seconds
306
     * @return string
307
     */
308
    private function tokenExpiresIn($seconds)
309
    {
310
        if ($seconds <= 0) {
311
            return 0;
312
        }
313
314
        $minutes = (float) $seconds / 60;
315
        $zero = new \DateTime('@0');
316
        $offset = new \DateTime('@' . $minutes * 60);
317
        $diff = $zero->diff($offset);
318
319
        return $diff->format('%a Days, %h Hours, %i Minutes, %s Seconds');
320
    }
321
322
    /**
323
     * [getUserJWT Get an ecoded user token]
324
     * @return string
325
     */
326
    public function getUserJWT($refresh = false)
327
    {
328
        if (!isset($this->secret)) {
329
            (new exception\errorException)
330
                ->message('There was an error trying to retrieve the server master key. Please read the documentation on setting up a configuration file')
331
                ->error('NO_CONTENT');
332
        }
333
334
        $key = $this->secret;
335
336
        /**
337
         * [$payload Set the default payload]
338
         * @var array
339
         */
340
        $payload = array(
341
            "iss" => (new route\router)->getIssuer(),
342
            "sub" => $this->getAccountID(),
343
            "iat" => $this->timeNow(),
344
            "nbf" => $this->timeNow() + 10,
345
        );
346
347
        /**
348
         * [$jwtOptions JWT options may be set as Responsible option overrides]
349
         * @var array
350
         */
351
        $exp = false;
352
        if (false !== ($jwtOptions = $this->checkVal($this->getOptions(), 'jwt'))) {
353
            if (false !== ($exp = $this->checkVal($jwtOptions, 'expires'))) {
354
                $payload['exp'] = $exp;
355
            }
356
            if (false !== ($iat = $this->checkVal($jwtOptions, 'issuedAt'))) {
357
                $payload['iat'] = $iat;
358
            }
359
            if (false !== ($nbf = $this->checkVal($jwtOptions, 'notBeFor'))) {
360
                if( strtolower($nbf) == 'issuedat' && isset($payload['iat']) ) {
361
                    $nbf = $payload['iat'] + 10;
362
                }
363
                $payload['nbf'] = $nbf;
364
            }
365
        }
366
367
        if( $refresh && $exp ) {
368
            $refreshPayload = $payload;
369
370
            $offset = $exp - $this->timeNow();
371
            $leeway = ($this->checkVal($this->options['jwt'], 'leeway')) ?: $this->jwt->getLeeway();
372
373
            $refreshPayload['exp'] = $exp+$offset+$leeway;
374
375
            $refreshJWT = $this->refreshJWT([
376
                'payload' => $refreshPayload
377
            ]);
378
379
            if( isset($refreshJWT['refresh']) ) {
380
                return $refreshJWT['refresh'];
381
            }
382
        }
383
384
        /**
385
         * Return the encoded JWT
386
         */
387
        return $this->jwt
388
            ->key($key)
389
            ->setPayload($payload)
390
            ->encode($payload)
391
        ;
392
    }
393
394
    /**
395
     * [setOptions Set the Responsible API options]
396
     * @param array $options
397
     */
398
    public function setOptions($options)
399
    {
400
        $this->options = $options;
401
        return $this;
402
    }
403
404
    /**
405
     * [setProperty Set the property we want the Responsible API load an account by]
406
     * @param string $property
407
     */
408
    private function setProperty($property)
409
    {
410
        $this->property = $property;
411
    }
412
413
    /**
414
     * [setColumn Set the column type we want the Responsible API load an account by]
415
     * @param string $column
416
     */
417
    private function setColumn($column)
418
    {
419
        switch ($column) {
420
            case ($column == 'account_id' || strtolower($column == 'accountid')):
421
                $this->column = 'BINARY USR.account_id';
422
                break;
423
424
            case ($column == 'username' || $column == 'name'):
425
                $this->column = 'USR.name';
426
                break;
427
428
            case ($column == 'email' || $column == 'mail'):
429
                $this->column = 'USR.mail';
430
                break;
431
432
            case ($column == 'refresh_token'):
433
                $this->column = 'USR.refresh_token';
434
                break;
435
            
436
            default:
437
                $this->column = '';
438
                break;
439
        }
440
    }
441
}
442