Failed Conditions
Push — ng ( 935f22...b3431d )
by Florent
04:01
created

TokenEndpointScopeExtension::getScope()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\Server\Scope;
15
16
use OAuth2Framework\Component\Server\Core\AccessToken\AccessToken;
17
use OAuth2Framework\Component\Server\Core\Client\Client;
18
use OAuth2Framework\Component\Server\Core\ResourceOwner\ResourceOwner;
19
use OAuth2Framework\Component\Server\TokenEndpoint\Extension\TokenEndpointExtension;
20
use OAuth2Framework\Component\Server\TokenEndpoint\GrantTypeData;
21
use OAuth2Framework\Component\Server\TokenEndpoint\GrantType;
22
use OAuth2Framework\Component\Server\Scope\Policy\ScopePolicyManager;
23
use OAuth2Framework\Component\Server\Core\Response\OAuth2Exception;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
final class TokenEndpointScopeExtension implements TokenEndpointExtension
27
{
28
    /**
29
     * @var ScopeRepository
30
     */
31
    private $scopeRepository;
32
33
    /**
34
     * @var ScopePolicyManager
35
     */
36
    private $scopePolicyManager;
37
38
    /**
39
     * ScopeProcessor constructor.
40
     *
41
     * @param ScopeRepository         $scopeRepository
42
     * @param ScopePolicyManager $scopePolicyManager
43
     */
44
    public function __construct(ScopeRepository $scopeRepository, ScopePolicyManager $scopePolicyManager)
45
    {
46
        $this->scopeRepository = $scopeRepository;
47
        $this->scopePolicyManager = $scopePolicyManager;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function beforeAccessTokenIssuance(ServerRequestInterface $request, GrantTypeData $grantTypeData, GrantType $grantType, callable $next): GrantTypeData
54
    {
55
        /** @var GrantTypeData $grantTypeData */
56
        $grantTypeData = $next($request, $grantTypeData, $grantType);
57
        $scope = $this->getScope($request, $grantTypeData);
58
        $scope = $this->applyScopePolicy($scope, $grantTypeData->getClient());
0 ignored issues
show
Bug introduced by
It seems like $grantTypeData->getClient() can be null; however, applyScopePolicy() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
59
        $this->checkRequestedScopeIsAvailable($scope, $grantTypeData);
60
61
        if (!empty($scope)) {
62
            $grantTypeData = $grantTypeData->withParameter('scope', $scope);
63
        }
64
65
        return $grantTypeData;
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function afterAccessTokenIssuance(Client $client, ResourceOwner $resourceOwner, AccessToken $accessToken, callable $next): array
72
    {
73
        return $next($client, $resourceOwner, $accessToken);
74
    }
75
76
    /**
77
     * @param ServerRequestInterface $request
78
     * @param GrantTypeData          $grantTypeData
79
     *
80
     * @return string
81
     */
82
    private function getScope(ServerRequestInterface $request, GrantTypeData $grantTypeData): string
83
    {
84
        $params = $request->getParsedBody() ?? [];
85
        if (!array_key_exists('scope', $params)) {
86
            return $grantTypeData->hasParameter('scope') ? $grantTypeData->getParameter('scope') : '';
87
        }
88
89
        return $params['scope'];
90
    }
91
92
    /**
93
     * @param string $scope
94
     * @param Client $client
95
     *
96
     * @return string
97
     *
98
     * @throws OAuth2Exception
99
     */
100
    private function applyScopePolicy(string $scope, Client $client): string
101
    {
102
        try {
103
            return $this->scopePolicyManager->apply($scope, $client);
104
        } catch (\InvalidArgumentException $e) {
105
            throw new OAuth2Exception(400, OAuth2Exception::ERROR_INVALID_SCOPE, $e->getMessage(), [], $e);
106
        }
107
    }
108
109
    /**
110
     * @param string        $scope
111
     * @param GrantTypeData $grantTypeData
112
     *
113
     * @throws OAuth2Exception
114
     */
115
    private function checkRequestedScopeIsAvailable(string $scope, GrantTypeData $grantTypeData)
116
    {
117
        // The available scope can be limited by (in this order):
118
        // * the grant type (e.g. refresh token, authorization code parameter)
119
        // * the client configuration
120
        // * the scope repository
121
        $availableScope = $grantTypeData->hasParameter('scope') ? $grantTypeData->getParameter('scope') : $this->getAvailableScopesForClient($grantTypeData->getClient());
0 ignored issues
show
Bug introduced by
It seems like $grantTypeData->getClient() can be null; however, getAvailableScopesForClient() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
122
        $availableScopes = explode(' ', $availableScope);
123
        $requestedScopes = empty($scope) ? [] : explode(' ', $scope);
124
        $diff = array_diff($requestedScopes, $availableScopes);
125
        if (0 !== count($diff)) {
126
            throw new OAuth2Exception(400, OAuth2Exception::ERROR_INVALID_SCOPE, sprintf('An unsupported scope was requested. Available scope is/are: %s.', implode(' ,', $availableScopes)));
127
        }
128
    }
129
130
    /**
131
     * @param Client $client
132
     *
133
     * @return string
134
     */
135
    private function getAvailableScopesForClient(Client $client): string
136
    {
137
        return ($client->has('scope')) ? $client->get('scope') : implode(' ', $this->scopeRepository->all());
138
    }
139
}
140