Failed Conditions
Push — master ( 2fcdae...1d0766 )
by Florent
04:40
created

UserInfo::getSectorIdentifierHost()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
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\OpenIdConnect\UserInfo;
15
16
use OAuth2Framework\Component\OpenIdConnect\UserInfo\Claim\ClaimSourceManager;
17
use OAuth2Framework\Component\OpenIdConnect\UserInfo\Pairwise\PairwiseSubjectIdentifierAlgorithm;
18
use OAuth2Framework\Component\OpenIdConnect\UserInfo\ScopeSupport\UserInfoScopeSupportManager;
19
use OAuth2Framework\Component\Core\Client\Client;
20
use OAuth2Framework\Component\Core\UserAccount\UserAccount;
21
22
class UserInfo
23
{
24
    /**
25
     * @var null|PairwiseSubjectIdentifierAlgorithm
26
     */
27
    private $pairwiseAlgorithm = null;
28
29
    /**
30
     * @var UserInfoScopeSupportManager
31
     */
32
    private $userinfoScopeSupportManager;
33
34
    /**
35
     * @var ClaimSourceManager
36
     */
37
    private $claimSourceManager;
38
39
    /**
40
     * UserInfo constructor.
41
     *
42
     * @param UserInfoScopeSupportManager $userinfoScopeSupportManager
43
     * @param ClaimSourceManager          $claimSourceManager
44
     */
45
    public function __construct(UserInfoScopeSupportManager $userinfoScopeSupportManager, ClaimSourceManager $claimSourceManager)
46
    {
47
        $this->userinfoScopeSupportManager = $userinfoScopeSupportManager;
48
        $this->claimSourceManager = $claimSourceManager;
49
    }
50
51
    /**
52
     * @param Client      $client
53
     * @param UserAccount $userAccount
54
     * @param string      $redirectUri
55
     * @param array       $requestedClaims
56
     * @param string      $scope
57
     * @param string|null $claimsLocales
58
     *
59
     * @return array
60
     */
61
    public function getUserinfo(Client $client, UserAccount $userAccount, string $redirectUri, array $requestedClaims, string $scope, ? string $claimsLocales): array
62
    {
63
        $requestedClaims = array_merge(
64
            $this->getClaimsFromClaimScope($scope),
65
            $requestedClaims
66
        );
67
        $claims = $this->getClaimValues($userAccount, $requestedClaims, $claimsLocales);
68
        $claims = array_merge(
69
            $claims,
70
            $this->claimSourceManager->getUserInfo($userAccount, $scope, [])
71
        );
72
        $claims['sub'] = $this->calculateSubjectIdentifier($client, $userAccount, $redirectUri);
73
74
        return $claims;
75
    }
76
77
    /**
78
     * @param string $scope
79
     *
80
     * @return array
81
     */
82
    private function getClaimsFromClaimScope(string $scope): array
83
    {
84
        $result = [];
85
86
        foreach (explode(' ', $scope) as $scp) {
87
            if ($this->userinfoScopeSupportManager->has($scp)) {
88
                $scope_claims = $this->userinfoScopeSupportManager->get($scp)->getAssociatedClaims();
89
                foreach ($scope_claims as $scope_claim) {
90
                    $result[$scope_claim] = null;
91
                }
92
            }
93
        }
94
95
        return $result;
96
    }
97
98
    /**
99
     * @param UserAccount $userAccount
100
     * @param string|null $claimsLocales
101
     * @param array       $claims
102
     *
103
     * @return array
104
     */
105
    private function getClaimValues(UserAccount $userAccount, array $claims, ? string $claimsLocales): array
106
    {
107
        $result = [];
108
        if (null === $claimsLocales) {
109
            $claimsLocales = [];
110
        } elseif (true === is_string($claimsLocales)) {
111
            $claimsLocales = explode(' ', $claimsLocales);
112
        }
113
        $claimsLocales[] = '';
114
        foreach ($claims as $claim => $config) {
115
            foreach ($claimsLocales as $claims_locale) {
116
                $claim_locale = $this->computeClaimWithLocale($claim, $claims_locale);
117
                $claim_value = $this->getUserClaim($userAccount, $claim_locale, $config);
118
                if (null !== $claim_value) {
119
                    $result[$claim_locale] = $claim_value;
120
121
                    break;
122
                }
123
            }
124
        }
125
126
        return $result;
127
    }
128
129
    /**
130
     * @param string $claim
131
     * @param string $locale
132
     *
133
     * @return string
134
     */
135
    private function computeClaimWithLocale($claim, $locale): string
136
    {
137
        if (empty($locale)) {
138
            return $claim;
139
        }
140
141
        return sprintf('%s#%s', $claim, $locale);
142
    }
143
144
    /**
145
     * @param UserAccount $userAccount
146
     * @param string      $claimName
147
     * @param null|array  $config
148
     *
149
     * @return null|mixed
150
     */
151
    private function getUserClaim(UserAccount $userAccount, string $claimName, ?array $config)
152
    {
153
        // FIXME: "acr" claim support has to be added.
154
        if ($userAccount->has($claimName)) {
155
            $claim = $userAccount->get($claimName);
156
            switch (true) {
157
                case is_array($config) && array_key_exists('value', $config):
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
158
                    if ($claim === $config['value']) {
159
                        return $claim;
160
                    }
161
162
                    break;
163
                case is_array($config) && array_key_exists('values', $config) && is_array($config['values']):
164
                    if (in_array($claim, $config['values'])) {
165
                        return $claim;
166
                    }
167
168
                    break;
169
                default:
170
                    return $claim;
171
            }
172
        }
173
174
        return null;
175
    }
176
177
    /**
178
     * @param PairwiseSubjectIdentifierAlgorithm $pairwiseAlgorithm
179
     */
180
    public function enablePairwiseSubject(PairwiseSubjectIdentifierAlgorithm $pairwiseAlgorithm)
181
    {
182
        $this->pairwiseAlgorithm = $pairwiseAlgorithm;
183
    }
184
185
    /**
186
     * @return bool
187
     */
188
    public function isPairwiseSubjectIdentifierSupported(): bool
189
    {
190
        return null !== $this->pairwiseAlgorithm;
191
    }
192
193
    /**
194
     * @return PairwiseSubjectIdentifierAlgorithm|null
195
     */
196
    public function getPairwiseSubjectIdentifierAlgorithm(): ? PairwiseSubjectIdentifierAlgorithm
197
    {
198
        return $this->pairwiseAlgorithm;
199
    }
200
201
    /**
202
     * @param Client      $client
203
     * @param UserAccount $userAccount
204
     * @param string      $redirectUri
205
     *
206
     * @return string
207
     */
208
    private function calculateSubjectIdentifier(Client $client, UserAccount $userAccount, string $redirectUri): string
209
    {
210
        $sub = $userAccount->getUserAccountId()->getValue();
211
        if (false === $this->isPairwiseSubjectIdentifierSupported()) {
212
            return $sub;
213
        }
214
        if (($client->has('subject_type') && ('pairwise' === $client->get('subject_type')))) {
215
            $sectorIdentifierHost = $this->getSectorIdentifierHost($client, $redirectUri);
216
217
            return $this->pairwiseAlgorithm->calculateSubjectIdentifier(
218
                $userAccount,
219
                $sectorIdentifierHost
220
            );
221
        }
222
223
        return $sub;
224
    }
225
226
    /**
227
     * @param Client $client
228
     * @param string $redirectUri
229
     *
230
     * @return string
231
     */
232
    private function getSectorIdentifierHost(Client $client, string $redirectUri): string
233
    {
234
        $uri = $redirectUri;
235
236
        if (true === $client->has('sector_identifier_uri')) {
237
            $uri = $client->get('sector_identifier_uri');
238
        }
239
240
        $data = parse_url($uri);
241
        if (!is_array($data) || !array_key_exists('host', $data)) {
242
            throw new \InvalidArgumentException(sprintf('Invalid Sector Identifier Uri "%s".', $uri));
243
        }
244
245
        return $data['host'];
246
    }
247
}
248