DataMapperAdapter   F
last analyzed

Complexity

Total Complexity 69

Size/Duplication

Total Lines 552
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 100%

Importance

Changes 23
Bugs 7 Features 3
Metric Value
wmc 69
c 23
b 7
f 3
lcom 1
cbo 18
dl 0
loc 552
ccs 224
cts 224
cp 1
rs 2.8301

29 Methods

Rating   Name   Duplication   Size   Complexity  
A getPassword() 0 4 1
A getUserClass() 0 4 1
A getUserCredentialsStrategies() 0 4 1
A __construct() 0 11 2
A getAccessToken() 0 15 3
B setAccessToken() 0 30 6
A getAuthorizationCode() 0 16 2
B setAuthorizationCode() 0 28 4
A expireAuthorizationCode() 0 9 1
A checkClientCredentials() 0 10 2
A isPublicClient() 0 10 2
A getClientDetails() 0 16 3
A getClientScope() 0 10 2
A checkRestrictedGrantType() 0 16 3
A getRefreshToken() 0 18 3
B setRefreshToken() 0 29 6
A unsetRefreshToken() 0 12 2
A scopeExists() 0 18 2
A getDefaultScope() 0 17 2
B checkUserCredentials() 0 22 6
A getUserDetails() 0 13 3
A setPassword() 0 4 1
A setUserClass() 0 11 3
A getScopeDataMapper() 0 4 1
A getTokenDataMapper() 0 4 1
A getClientDataMapper() 0 4 1
A getUserDataMapper() 0 8 1
A addUserCredentialsStrategy() 0 12 3
A removeUserCredentialsStrategy() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like DataMapperAdapter 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 DataMapperAdapter, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Stefano Torresi (http://stefanotorresi.it)
4
 * @license See the file LICENSE.txt for copying permission.
5
 * ************************************************
6
 */
7
8
namespace Thorr\OAuth2\Storage;
9
10
use Assert\Assertion;
11
use DateTime;
12
use InvalidArgumentException;
13
use OAuth2\Storage;
14
use Thorr\OAuth2\DataMapper;
15
use Thorr\OAuth2\Entity;
16
use Thorr\OAuth2\GrantType\UserCredentials\PasswordStrategy;
17
use Thorr\OAuth2\GrantType\UserCredentials\UserCredentialsStrategyInterface;
18
use Thorr\Persistence\DataMapper\EntityFinderInterface;
19
use Thorr\Persistence\DataMapper\Manager\DataMapperManager;
20
use Thorr\Persistence\DataMapper\Manager\DataMapperManagerAwareInterface;
21
use Thorr\Persistence\DataMapper\Manager\DataMapperManagerAwareTrait;
22
use Zend\Crypt\Password\PasswordInterface;
23
use Zend\Stdlib\PriorityList;
24
25
class DataMapperAdapter implements
26
    Storage\AuthorizationCodeInterface,
27
    Storage\AccessTokenInterface,
28
    Storage\ClientCredentialsInterface,
29
    Storage\RefreshTokenInterface,
30
    Storage\ScopeInterface,
31
    Storage\UserCredentialsInterface,
32
    DataMapperManagerAwareInterface
33
{
34
    use DataMapperManagerAwareTrait;
35
36
    /**
37
     * @var PasswordInterface
38
     */
39
    protected $password;
40
41
    /**
42
     * @var string
43
     */
44
    protected $userClass = Entity\UserInterface::class;
45
46
    /**
47
     * @var PriorityList
48
     */
49
    protected $userCredentialsStrategies;
50
51
    /**
52
     * @param DataMapperManager $dataMapperManager
53
     * @param PasswordInterface $password
54
     */
55 61
    public function __construct(DataMapperManager $dataMapperManager, PasswordInterface $password, $addDefaultUserCredentialsStrategy = true)
56
    {
57 61
        $this->setDataMapperManager($dataMapperManager);
58 61
        $this->setPassword($password);
59 61
        $this->userCredentialsStrategies = new PriorityList();
60 61
        $this->userCredentialsStrategies->isLIFO(false);
61
62 61
        if ($addDefaultUserCredentialsStrategy) {
63 55
            $this->addUserCredentialsStrategy(new PasswordStrategy($password), 'default');
64 55
        }
65 61
    }
66
67
    /**
68
     * {@inheritdoc}
69
     *
70
     * @param string $oauthToken
71
     */
72 3
    public function getAccessToken($oauthToken)
73
    {
74 3
        $token = $this->getTokenDataMapper(Entity\AccessToken::class)->findByToken($oauthToken);
75
76 3
        if (! $token instanceof Entity\AccessToken) {
77 1
            return;
78
        }
79
80
        return [
81 2
            'expires'   => $token->getExpiryUTCTimestamp(),
82 2
            'client_id' => $token->getClient()->getUuid(),
83 2
            'user_id'   => $token->getUser() ? $token->getUser()->getUuid() : null,
84 2
            'scope'     => $token->getScopesString(),
85 2
        ];
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     *
91
     * @param string      $token
92
     * @param string      $clientId
93
     * @param string|null $userId
94
     */
95 2
    public function setAccessToken($token, $clientId, $userId, $expiryTimestamp, $scope = null)
96
    {
97 2
        $authTokenDataMapper = $this->getTokenDataMapper(Entity\AccessToken::class);
98
99 2
        $client = $this->getClientDataMapper()->findByUuid($clientId);
100
101 2
        if (! $accessToken = $authTokenDataMapper->findByToken($token)) {
102 1
            $accessToken = new Entity\AccessToken(null, $token, $client);
103 1
        } else {
104 1
            $accessToken->setClient($client);
105
        }
106
107 2
        if ($userId) {
108 1
            $user = $this->getUserDataMapper()->findByUuid($userId);
109
110 1
            if ($user instanceof $this->userClass) {
111 1
                $accessToken->setUser($user);
112 1
            }
113 1
        }
114
115 2
        $expiryDate = is_int($expiryTimestamp) ? new DateTime('@' . $expiryTimestamp) : null;
116 2
        $accessToken->setExpiryDate($expiryDate);
117
118 2
        if ($scope) {
119 1
            $scopes = $this->getScopeDataMapper()->findScopes(explode(' ', $scope));
120 1
            $accessToken->setScopes($scopes);
121 1
        }
122
123 2
        $authTokenDataMapper->save($accessToken);
124 2
    }
125
126
    /**
127
     * {@inheritdoc}
128
     *
129
     * @param string $code
130
     */
131 2
    public function getAuthorizationCode($code)
132
    {
133 2
        $authorizationCode = $this->getTokenDataMapper(Entity\AuthorizationCode::class)->findByToken($code);
134
135 2
        if (! $authorizationCode instanceof Entity\AuthorizationCode) {
136 1
            return;
137
        }
138
139
        return [
140 1
            'expires'      => $authorizationCode->getExpiryUTCTimestamp(),
141 1
            'client_id'    => $authorizationCode->getClient()->getUuid(),
142 1
            'user_id'      => $authorizationCode->getUser()->getUuid(),
143 1
            'redirect_uri' => $authorizationCode->getRedirectUri(),
144 1
            'scope'        => $authorizationCode->getScopesString(),
145 1
        ];
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 2
    public function setAuthorizationCode($code, $clientId, $userId, $redirectUri, $expiryTimestamp, $scope = null)
152
    {
153 2
        $authCodeDataMapper = $this->getTokenDataMapper(Entity\AuthorizationCode::class);
154
155 2
        $client = $this->getClientDataMapper()->findByUuid($clientId);
156 2
        $user   = $this->getUserDataMapper()->findByUuid($userId);
157
158
        /** @var Entity\AuthorizationCode $authorizationCode */
159 2
        $authorizationCode = $authCodeDataMapper->findByToken($code);
160
161 2
        if (! $authorizationCode) {
162 1
            $authorizationCode = new Entity\AuthorizationCode(null, $code, $client, $user, null, $redirectUri);
163 1
        } else {
164 1
            $authorizationCode->setUser($user);
165 1
            $authorizationCode->setClient($client);
166 1
            $authorizationCode->setRedirectUri($redirectUri);
167
        }
168
169 2
        $expiryDate = is_int($expiryTimestamp) ? new DateTime('@' . $expiryTimestamp) : null;
170 2
        $authorizationCode->setExpiryDate($expiryDate);
171
172 2
        if ($scope) {
173 1
            $scopes = $this->getScopeDataMapper()->findScopes(explode(' ', $scope));
174 1
            $authorizationCode->setScopes($scopes);
175 1
        }
176
177 2
        $authCodeDataMapper->save($authorizationCode);
178 2
    }
179
180
    /**
181
     * {@inheritdoc}
182
     *
183
     * @param string $token
184
     */
185 1
    public function expireAuthorizationCode($token)
186
    {
187 1
        $authCodeDataMapper = $this->getTokenDataMapper(Entity\AuthorizationCode::class);
188
189 1
        $authorizationCode = $authCodeDataMapper->findByToken($token);
190 1
        $authorizationCode->setExpiryDate(new DateTime());
191
192 1
        $authCodeDataMapper->save($authorizationCode);
193 1
    }
194
195
    /**
196
     * {@inheritdoc}
197
     *
198
     * @param string $clientId
199
     * @param string $clientSecret
200
     */
201 3
    public function checkClientCredentials($clientId, $clientSecret = null)
202
    {
203 3
        $client = $this->getClientDataMapper()->findByUuid($clientId);
204
205 3
        if (! $client instanceof Entity\Client) {
206 1
            return false;
207
        }
208
209 2
        return $this->password->verify($clientSecret, $client->getSecret());
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     *
215
     * @param string $clientId
216
     */
217 3
    public function isPublicClient($clientId)
218
    {
219 3
        $client = $this->getClientDataMapper()->findByUuid($clientId);
220
221 3
        if (! $client instanceof Entity\Client) {
222 1
            return false;
223
        }
224
225 2
        return $client->isPublic();
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     *
231
     * @param string $clientId
232
     */
233 2
    public function getClientDetails($clientId)
234
    {
235 2
        $client = $this->getClientDataMapper()->findByUuid($clientId);
236
237 2
        if (! $client instanceof Entity\Client) {
238 1
            return false;
239
        }
240
241
        return [
242 1
            'redirect_uri' => $client->getRedirectUri(),
243 1
            'client_id'    => $client->getUuid(),
244 1
            'grant_types'  => $client->getGrantTypes(),
245 1
            'user_id'      => $client->getUser() ? $client->getUser()->getUuid() : null,
246 1
            'scope'        => $client->getScopesString(),
247 1
        ];
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     *
253
     * @param string $clientUuid
254
     */
255 2
    public function getClientScope($clientUuid)
256
    {
257 2
        $client = $this->getClientDataMapper()->findByUuid($clientUuid);
258
259 2
        if (! $client instanceof Entity\Client) {
260 1
            throw new InvalidArgumentException('Invalid client uuid provided');
261
        }
262
263 1
        return $client->getScopesString();
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     *
269
     * if no grant type is defined for the client, then any type is valid
270
     *
271
     * @param string $clientUuid
272
     * @param string $grantType
273
     */
274 5
    public function checkRestrictedGrantType($clientUuid, $grantType)
275
    {
276 5
        $client = $this->getClientDataMapper()->findByUuid($clientUuid);
277
278 5
        if (! $client instanceof Entity\Client) {
279 1
            return false;
280
        }
281
282 4
        $clientGrantTypes = $client->getGrantTypes();
283
284 4
        if (empty($clientGrantTypes)) {
285 1
            return true;
286
        }
287
288 3
        return in_array($grantType, $clientGrantTypes);
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     *
294
     * @param string $refreshToken
295
     */
296 3
    public function getRefreshToken($refreshToken)
297
    {
298 3
        $refreshTokenDataMapper = $this->getTokenDataMapper(Entity\RefreshToken::class);
299
300 3
        $token = $refreshTokenDataMapper->findByToken($refreshToken);
301
302 3
        if (! $token instanceof Entity\RefreshToken) {
303 1
            return;
304
        }
305
306
        return [
307 2
            'refresh_token' => $token->getToken(),
308 2
            'client_id'     => $token->getClient()->getUuid(),
309 2
            'user_id'       => $token->getUser() ? $token->getUser()->getUuid() : null,
310 2
            'expires'       => $token->getExpiryUTCTimestamp(),
311 2
            'scope'         => $token->getScopesString(),
312 2
        ];
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     *
318
     * @param string      $token
319
     * @param string      $clientId
320
     * @param string|null $userId
321
     * @param int|null    $expiryTimestamp
322
     * @param string      $scope
323
     */
324 2
    public function setRefreshToken($token, $clientId, $userId, $expiryTimestamp, $scope = null)
325
    {
326 2
        $refreshTokenDataMapper = $this->getTokenDataMapper(Entity\RefreshToken::class);
327
328 2
        $client = $this->getClientDataMapper()->findByUuid($clientId);
329
330 2
        if (! $refreshToken = $refreshTokenDataMapper->findByToken($token)) {
331 1
            $refreshToken = new Entity\RefreshToken(null, $token, $client);
332 1
        } else {
333 1
            $refreshToken->setClient($client);
334
        }
335
336 2
        if ($userId) {
337 1
            $user = $this->getUserDataMapper()->findByUuid($userId);
338 1
            if ($user instanceof $this->userClass) {
339 1
                $refreshToken->setUser($user);
340 1
            }
341 1
        }
342
343 2
        $expiryDate = is_int($expiryTimestamp) ? new DateTime('@' . $expiryTimestamp) : null;
344 2
        $refreshToken->setExpiryDate($expiryDate);
345
346 2
        if ($scope) {
347 1
            $scopes = $this->getScopeDataMapper()->findScopes(explode(' ', $scope));
348 1
            $refreshToken->setScopes($scopes);
349 1
        }
350
351 2
        $refreshTokenDataMapper->save($refreshToken);
352 2
    }
353
354
    /**
355
     * {@inheritdoc}
356
     *
357
     * @param string $token
358
     */
359 2
    public function unsetRefreshToken($token)
360
    {
361 2
        $refreshTokenDataMapper = $this->getTokenDataMapper(Entity\RefreshToken::class);
362
363 2
        $refreshToken = $refreshTokenDataMapper->findByToken($token);
364
365 2
        if (! $refreshToken instanceof Entity\RefreshToken) {
366 1
            throw new InvalidArgumentException('Invalid token provided');
367
        }
368
369 1
        $refreshTokenDataMapper->remove($refreshToken);
370 1
    }
371
372
    /**
373
     * {@inheritdoc}
374
     *
375
     * @param string $scopesString
376
     */
377 6
    public function scopeExists($scopesString)
378
    {
379 6
        $scopes      = explode(' ', $scopesString);
380 6
        $foundScopes = $this->getScopeDataMapper()->findScopes($scopes);
381 6
        $inputScopes = $scopes;
382
383
        $matches = array_filter($foundScopes, function (Entity\Scope $scope) use (&$inputScopes) {
384 4
            $result = in_array($scope, $inputScopes);
385 4
            if ($result) {
386 4
                $matchKey = array_search($scope, $inputScopes);
387 4
                unset($inputScopes[$matchKey]);
388 4
            }
389
390 4
            return $result;
391 6
        });
392
393 6
        return count($matches) === count($scopes);
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399 5
    public function getDefaultScope($clientId = null)
400
    {
401 5
        $scopes = $this->getScopeDataMapper()->findDefaultScopes();
402
403 5
        if (! count($scopes)) {
404 2
            return;
405
        }
406
407 3
        $scopeNames = array_map(
408 3
            function (Entity\Scope $scope) {
409 3
                return $scope->getName();
410 3
            },
411
            $scopes
412 3
        );
413
414 3
        return implode(' ', $scopeNames);
415
    }
416
417
    /**
418
     * {@inheritdoc}
419
     *
420
     * @param string $credential
421
     * @param string $password
422
     */
423 6
    public function checkUserCredentials($credential, $password)
424
    {
425 6
        $user = $this->getUserDataMapper()->findByCredential($credential);
426
427 5
        $userClass = $this->userClass;
428
429 5
        if (! $user instanceof $userClass) {
430 1
            return false;
431
        }
432
433 4
        foreach ($this->userCredentialsStrategies as $strategy) {
434
            $result = $strategy instanceof UserCredentialsStrategyInterface
435 4
                ? $strategy->isValid($user, $password)
436 4
                : call_user_func($strategy, $user, $password);
437
438 4
            if (! $result) {
439 1
                break;
440
            }
441 4
        }
442
443 4
        return isset($result) ? $result : false;
444
    }
445
446
    /**
447
     * {@inheritdoc}
448
     *
449
     * @param string|null $credential
450
     */
451 4
    public function getUserDetails($credential)
452
    {
453 4
        $user = $this->getUserDataMapper()->findByCredential($credential);
454
455 4
        if (! $user instanceof $this->userClass) {
456 2
            return false;
457
        }
458
459
        return [
460 2
            'user_id' => (string) $user->getUuid(),
461 2
            'scope'   => $user instanceof Entity\ScopesProviderInterface ? $user->getScopesString() : null,
462 2
        ];
463
    }
464
465
    /**
466
     * @return PasswordInterface
467
     */
468 1
    public function getPassword()
469
    {
470 1
        return $this->password;
471
    }
472
473
    /**
474
     * @param PasswordInterface $password
475
     */
476 61
    public function setPassword(PasswordInterface $password)
477
    {
478 61
        $this->password = $password;
479 61
    }
480
481
    /**
482
     * @return string
483
     */
484 1
    public function getUserClass()
485
    {
486 1
        return $this->userClass;
487
    }
488
489
    /**
490
     * @param string $userClass
491
     */
492 2
    public function setUserClass($userClass)
493
    {
494 2
        if (! class_exists($userClass) || ! is_a($userClass, Entity\UserInterface::class, true)) {
495 1
            throw new InvalidArgumentException(sprintf(
496 1
                'Invalid user class: must implement %s',
497
                Entity\UserInterface::class
498 1
            ));
499
        }
500
501 1
        $this->userClass = $userClass;
502 1
    }
503
504
    /**
505
     * @return DataMapper\ScopeMapperInterface
506
     */
507 14
    protected function getScopeDataMapper()
508
    {
509 14
        return $this->getDataMapperManager()->getDataMapperForEntity(Entity\Scope::class);
510
    }
511
512
    /**
513
     * @param string $tokenClass
514
     *
515
     * @return DataMapper\TokenMapperInterface
516
     */
517 17
    protected function getTokenDataMapper($tokenClass)
518
    {
519 17
        return $this->getDataMapperManager()->getDataMapperForEntity($tokenClass);
520
    }
521
522
    /**
523
     * @return EntityFinderInterface
524
     */
525 21
    protected function getClientDataMapper()
526
    {
527 21
        return $this->getDataMapperManager()->getDataMapperForEntity(Entity\Client::class);
528
    }
529
530
    /**
531
     * @return DataMapper\UserMapperInterface
532
     */
533 14
    protected function getUserDataMapper()
534
    {
535 14
        $userDataMapper = $this->getDataMapperManager()->getDataMapperForEntity($this->userClass);
536
537 14
        Assertion::isInstanceOf($userDataMapper, DataMapper\UserMapperInterface::class);
538
539 13
        return $userDataMapper;
540
    }
541
542
    /**
543
     * @param int $flag extraction flag @see PriorityList
544
     *
545
     * @return array
546
     */
547 5
    public function getUserCredentialsStrategies($flag = PriorityList::EXTR_DATA)
548
    {
549 5
        return $this->userCredentialsStrategies->toArray($flag);
550
    }
551
552
    /**
553
     * @param callable|UserCredentialsStrategyInterface $strategy
554
     * @param string                                    $name
555
     */
556 60
    public function addUserCredentialsStrategy($strategy, $name, $priority = 0)
557
    {
558 60
        if (! is_callable($strategy)
559 60
            && ! $strategy instanceof UserCredentialsStrategyInterface) {
560 1
            throw new InvalidArgumentException(sprintf(
561 1
                "User credential strategy must be a callable or implement '%s'",
562
                UserCredentialsStrategyInterface::class
563 1
            ));
564
        }
565
566 59
        $this->userCredentialsStrategies->insert($name, $strategy, $priority);
567 59
    }
568
569
    /**
570
     * @param string $name
571
     */
572 1
    public function removeUserCredentialsStrategy($name)
573
    {
574 1
        $this->userCredentialsStrategies->remove($name);
575 1
    }
576
}
577