Completed
Push — master ( f18f2c...d23b89 )
by CodexShaper
03:11
created

Manager::refresh()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 6
cp 0
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace CodexShaper\OAuth2\Server;
4
5
use CodexShaper\OAuth2\Server\Repositories\AccessTokenRepository;
6
use CodexShaper\OAuth2\Server\Repositories\AuthCodeRepository;
7
use CodexShaper\OAuth2\Server\Repositories\ClientRepository;
8
use CodexShaper\OAuth2\Server\Repositories\RefreshTokenRepository;
9
use CodexShaper\OAuth2\Server\Repositories\ScopeRepository;
10
use CodexShaper\OAuth2\Server\Repositories\UserRepository;
11
use DateInterval;
12
use Defuse\Crypto\Key;
13
use League\OAuth2\Server\AuthorizationServer;
14
use League\OAuth2\Server\CryptKey;
15
use League\OAuth2\Server\Grant\AuthCodeGrant;
16
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
17
use League\OAuth2\Server\Grant\ImplicitGrant;
18
use League\OAuth2\Server\Grant\PasswordGrant;
19
use League\OAuth2\Server\Grant\RefreshTokenGrant;
20
use League\OAuth2\Server\ResourceServer;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, CodexShaper\OAuth2\Server\ResourceServer.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use phpseclib\Crypt\RSA;
22
23
class Manager
24
{
25
    /**
26
     * Authorization server.
27
     *
28
     * @var string
29
     */
30
    protected $server;
31
32
    /**
33
     * All scopes.
34
     *
35
     * @var string
36
     */
37
    protected static $scopes = [
38
        'read'       => 'Read',
39
        'read_write' => 'Read and Write',
40
    ];
41
42
    /**
43
     * Default scope.
44
     *
45
     * @var string
46
     */
47
    protected static $defaultScope;
48
49
    /**
50
     * Enable or didable implicit grant.
51
     *
52
     * @var string
53
     */
54
    protected $isEnableImplicitGrant = false;
55
56
    /**
57
     * Refresh token expire at.
58
     *
59
     * @var string
60
     */
61
    protected static $refreshTokensExpireAt = 'P1Y';
62
63
    /**
64
     * token exipre at.
65
     *
66
     * @var string
67
     */
68
    protected static $tokensExpireAt = 'P1Y';
69
70
    /**
71
     * Create RSA authorisation keys.
72
     *
73
     * @return void
74
     */
75
    public static function keys($dir = null)
76
    {
77
        if (!$dir) {
78
            $dir = __DIR__.'/../storage/rsa';
79
        }
80
81
        if (!is_dir($dir)) {
82
            mkdir($dir, 0777, true);
83
        }
84
85
        $publicKey = $dir.'/oauth-public.key';
86
        $privateKey = $dir.'/oauth-private.key';
87
        $rsa = new RSA();
88
        $keys = $rsa->createKey(4096);
89
90
        file_put_contents($publicKey, $keys['publickey']);
91
        file_put_contents($privateKey, $keys['privatekey']);
92
    }
93
94
    /**
95
     * Add New scopes.
96
     *
97
     * @return void
98
     */
99
    public static function setScopes(array $scopes = [])
100
    {
101
        if (!empty($scopes)) {
102
            return;
103
        }
104
105
        static::$scopes = array_merge(static::$scopes, $scopes);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge(static::$scopes, $scopes) of type array is incompatible with the declared type string of property $scopes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
106
    }
107
108
    /**
109
     * Get all available scopes.
110
     *
111
     * @return array
112
     */
113
    public static function getScopes()
114
    {
115
        return static::$scopes;
116
    }
117
118
    /**
119
     * Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally
120
     * append additional scopes or remove requested scopes.
121
     *
122
     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
123
     * @param string                                                $grantType
124
     * @param \League\OAuth2\Server\Entities\ClientEntityInterface  $clientEntity
0 ignored issues
show
Bug introduced by
There is no parameter named $clientEntity. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
125
     * @param null|string                                           $userIdentifier
0 ignored issues
show
Bug introduced by
There is no parameter named $userIdentifier. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
126
     *
127
     * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
128
     */
129
    public static function filterScopes($scopes, $grantType)
130
    {
131
        if (!in_array($grantType, ['password', 'personal_access', 'client_credentials'])) {
132
            $scopes = array_filter($scopes, function ($scope) {
133
                return trim($scope->getIdentifier()) !== '*';
134
            });
135
        }
136
137
        return array_filter($scopes, function ($scope) {
138
            return static::hasScope($scope->getIdentifier());
139
        });
140
    }
141
142
    /**
143
     * Create a CryptKey instance without permissions check.
144
     *
145
     * @param string $identifier
146
     *
147
     * @return bool
148
     */
149
    public static function hasScope($identifier)
150
    {
151
        return trim($identifier) === '*' || array_key_exists($identifier, static::$scopes);
152
    }
153
154
    /**
155
     * Check is isset a scope or not.
156
     *
157
     * @param string $identifier
158
     *
159
     * @return bool
160
     */
161
    public static function isValidateScope($identifier)
162
    {
163
        return array_key_exists($identifier, static::$scopes);
164
    }
165
166
    /**
167
     * Make the authorization service instance.
168
     *
169
     * @return \League\OAuth2\Server\AuthorizationServer
170
     */
171
    public function makeAuthorizationServer()
172
    {
173
        $encryptionKey = 'def00000069ceedc03a1f91fd51a49ce08ebb8580d688f4fbc3774c86aaa4df516eea0f72c1f62e3e577ec9f0c83c773b890222966bf93ac22e84a9eca55638be310665b';
174
175
        $this->server = new AuthorizationServer(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \League\OAuth2\Serve...String($encryptionKey)) of type object<League\OAuth2\Server\AuthorizationServer> is incompatible with the declared type string of property $server.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
176
            new ClientRepository(),
177
            new AccessTokenRepository(),
178
            new ScopeRepository(),
179
            $this->makeCryptKey('private'),
180
            Key::loadFromAsciiSafeString($encryptionKey)
181
        );
182
183
        $this->server->setDefaultScope(static::$defaultScope);
184
        $this->enableGrantTypes();
185
186
        return $this->server;
187
    }
188
189
    /**
190
     * Enable All grant types.
191
     *
192
     * @return void
193
     */
194
    protected function enableGrantTypes()
195
    {
196
        $this->server->enableGrantType(
0 ignored issues
show
Bug introduced by
The method enableGrantType cannot be called on $this->server (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
197
            new ClientCredentialsGrant(),
198
            new DateInterval(static::$tokensExpireAt) // access tokens will expire after 1 hour
199
        );
200
201
        $this->enableAuthCodeGrant();
202
        $this->enablePasswordGrant();
203
        $this->enableRefreshTokenGrant();
204
205
        if ($this->isEnableImplicitGrant) {
206
            $this->server->enableGrantType(
0 ignored issues
show
Bug introduced by
The method enableGrantType cannot be called on $this->server (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
207
                new ImplicitGrant(new DateInterval(static::$tokensExpireAt)),
208
                new DateInterval(static::$tokensExpireAt) // access tokens will expire after 1 hour
209
            );
210
        }
211
    }
212
213
    /**
214
     * Enable Authorization code Grant.
215
     *
216
     * @return void
217
     */
218 View Code Duplication
    protected function enableAuthCodeGrant()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
    {
220
        $authCodeGrant = new AuthCodeGrant(
221
            new AuthCodeRepository(),
222
            new RefreshTokenRepository(),
223
            new DateInterval('PT10M')
224
        );
225
226
        $authCodeGrant->setRefreshTokenTTL(new DateInterval(static::$refreshTokensExpireAt));
227
228
        $this->server->enableGrantType(
0 ignored issues
show
Bug introduced by
The method enableGrantType cannot be called on $this->server (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
229
            $authCodeGrant,
230
            new DateInterval(static::$tokensExpireAt)
231
        );
232
    }
233
234
    /**
235
     * Enable Password Grant.
236
     *
237
     * @return void
238
     */
239 View Code Duplication
    protected function enablePasswordGrant()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240
    {
241
        $passwordGrant = new PasswordGrant(
242
            new UserRepository(),
243
            new RefreshTokenRepository()
244
        );
245
246
        $passwordGrant->setRefreshTokenTTL(new DateInterval(static::$refreshTokensExpireAt)); // refresh tokens will expire after 1 month
247
248
        // Enable the password grant on the server
249
        $this->server->enableGrantType(
0 ignored issues
show
Bug introduced by
The method enableGrantType cannot be called on $this->server (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
250
            $passwordGrant,
251
            new DateInterval(static::$tokensExpireAt) // access tokens will expire after 1 hour
252
        );
253
    }
254
255
    /**
256
     * Enable Refresh token Grant.
257
     *
258
     * @return void
259
     */
260 View Code Duplication
    protected function enableRefreshTokenGrant()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
    {
262
        $refreshTokenGrant = new RefreshTokenGrant(new RefreshTokenRepository());
263
        $refreshTokenGrant->setRefreshTokenTTL(new \DateInterval(static::$refreshTokensExpireAt)); // new refresh tokens will expire after 1 month
264
265
        // Enable the refresh token grant on the server
266
        $this->server->enableGrantType(
0 ignored issues
show
Bug introduced by
The method enableGrantType cannot be called on $this->server (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
267
            $refreshTokenGrant,
268
            new \DateInterval(static::$tokensExpireAt) // new access tokens will expire after an hour
269
        );
270
    }
271
272
    /**
273
     * Create a resource server for validation.
274
     *
275
     * @return \League\OAuth2\Server\ResourServer
276
     */
277
    public function getResourceServer()
278
    {
279
        return new ResourceServer(
280
            new AccessTokenRepository(),
281
            $this->makeCryptKey('public')
282
        );
283
    }
284
285
    /**
286
     * Create a CryptKey instance without permissions check.
287
     *
288
     * @param string $key
0 ignored issues
show
Bug introduced by
There is no parameter named $key. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
289
     *
290
     * @return \League\OAuth2\Server\CryptKey
291
     */
292
    protected function makeCryptKey($type)
293
    {
294
        $key = __DIR__.'/../storage/rsa/oauth-'.$type.'.key';
295
296
        return new CryptKey($key, null, false);
297
    }
298
299
    public function validateUserForRequest($request)
300
    {
301
        $token = Model::instance('tokenModel')->find($request->getAttribute('oauth_access_token_id'));
302
        $client = Model::instance('clientModel')->find($request->getAttribute('oauth_client_id'));
303
304
        if (!$token || !$client) {
305
            return null;
306
        }
307
308
        if (!$token->user_id && !$client->user_id) {
309
            return null;
310
        }
311
312
        if ($token->user_id != null) {
313
            return $token->user_id;
314
        }
315
316
        return $client->user_id;
317
    }
318
319
    /**
320
     * Load all routes.
321
     *
322
     * @return void
323
     */
324
    public static function routes()
325
    {
326
        require __DIR__.'/../routes/oauth.php';
327
    }
328
329
    /**
330
     * Migrate tables.
331
     *
332
     * @param string $dir
333
     *
334
     * @return void
335
     */
336 View Code Duplication
    public static function migrate($dir = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
337
    {
338
        if (is_null($dir)) {
339
            $dir = __DIR__.'/../database/migrations';
340
        }
341
342
        foreach (glob($dir.'/*.php') as $file) {
343
            require_once $file;
344
            $table = pathinfo($file)['filename'];
345
            (new $table())->up();
346
        }
347
    }
348
349
    /**
350
     * Drop tables.
351
     *
352
     * @param string $dir
353
     *
354
     * @return void
355
     */
356 View Code Duplication
    public static function rollback($dir = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
357
    {
358
        if (is_null($dir)) {
359
            $dir = __DIR__.'/../database/migrations';
360
        }
361
362
        foreach (glob($dir.'/*.php') as $file) {
363
            require_once $file;
364
            $table = pathinfo($file)['filename'];
365
            (new $table())->down();
366
        }
367
    }
368
369
    /**
370
     * Drop and migrate fresh tables.
371
     *
372
     * @param string $dir
373
     *
374
     * @return void
375
     */
376
    public static function refresh($dir = null)
377
    {
378
        if (is_null($dir)) {
379
            $dir = __DIR__.'/../database/migrations';
380
        }
381
382
        static::rollback($dir);
383
        static::migrate($dir);
384
    }
385
}
386