Failed Conditions
Push — master ( 2b4927...4e5b1e )
by Florent
03:58
created

UserInfo::calculateSubjectIdentifier()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 3
nop 3
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\ClaimSource\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
     * @return string[]
53
     */
54
    public function getSupportedClaims(): array
55
    {
56
        $claimsSupported = [];
57
        foreach ($this->userinfoScopeSupportManager->all() as $infoScopeSupport) {
58
            $claimsSupported += $infoScopeSupport->getClaims();
59
        }
60
61
        return array_unique($claimsSupported);
62
    }
63
64
    /**
65
     * @param Client      $client
66
     * @param UserAccount $userAccount
67
     * @param string      $redirectUri
68
     * @param array       $requestClaims
69
     * @param string      $scope
70
     * @param string|null $claimsLocales
71
     *
72
     * @return array
73
     */
74
    public function getUserinfo(Client $client, UserAccount $userAccount, string $redirectUri, array $requestClaims, string $scope, ? string $claimsLocales): array
75
    {
76
        $requestClaims = array_merge(
77
            $this->getClaimsFromClaimScope($scope),
78
            $requestClaims
79
        );
80
        $claims = $this->getClaimValues($userAccount, $requestClaims, $claimsLocales);
81
        $claims = array_merge(
82
            $claims,
83
            $this->claimSourceManager->getUserInfo($userAccount, $scope, [])
84
        );
85
        $claims['sub'] = $this->calculateSubjectIdentifier($client, $userAccount, $redirectUri);
86
87
        return $claims;
88
    }
89
90
    /**
91
     * @param string $scope
92
     *
93
     * @return array
94
     */
95
    private function getClaimsFromClaimScope(string $scope): array
96
    {
97
        $result = [];
98
99
        foreach (explode(' ', $scope) as $scp) {
100
            if ($this->userinfoScopeSupportManager->has($scp)) {
101
                $scope_claims = $this->userinfoScopeSupportManager->get($scp)->getClaims();
102
                foreach ($scope_claims as $scope_claim) {
103
                    $result[$scope_claim] = null;
104
                }
105
            }
106
        }
107
108
        return $result;
109
    }
110
111
    /**
112
     * @param UserAccount $userAccount
113
     * @param string|null $claimsLocales
114
     * @param array       $claims
115
     *
116
     * @return array
117
     */
118
    private function getClaimValues(UserAccount $userAccount, array $claims, ? string $claimsLocales): array
119
    {
120
        $result = [];
121
        if (null === $claimsLocales) {
122
            $claimsLocales = [];
123
        } elseif (true === is_string($claimsLocales)) {
124
            $claimsLocales = explode(' ', $claimsLocales);
125
        }
126
        $claimsLocales[] = '';
127
        foreach ($claims as $claim => $config) {
128
            foreach ($claimsLocales as $claims_locale) {
129
                $claim_locale = $this->computeClaimWithLocale($claim, $claims_locale);
130
                $claim_value = $this->getUserClaim($userAccount, $claim_locale, $config);
131
                if (null !== $claim_value) {
132
                    $result[$claim_locale] = $claim_value;
133
134
                    break;
135
                }
136
            }
137
        }
138
139
        return $result;
140
    }
141
142
    /**
143
     * @param string $claim
144
     * @param string $locale
145
     *
146
     * @return string
147
     */
148
    private function computeClaimWithLocale($claim, $locale): string
149
    {
150
        if (empty($locale)) {
151
            return $claim;
152
        }
153
154
        return sprintf('%s#%s', $claim, $locale);
155
    }
156
157
    /**
158
     * @param UserAccount $userAccount
159
     * @param string      $claimName
160
     * @param null|array  $config
161
     *
162
     * @return null|mixed
163
     */
164
    private function getUserClaim(UserAccount $userAccount, string $claimName, ?array $config)
165
    {
166
        if ($userAccount->has($claimName)) {
167
            $claim = $userAccount->get($claimName);
168
            switch (true) {
169
                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...
170
                    if ($claim === $config['value']) {
171
                        return $claim;
172
                    }
173
                    break;
174
                case is_array($config) && array_key_exists('values', $config) && is_array($config['values']):
175
                    if (in_array($claim, $config['values'])) {
176
                        return $claim;
177
                    }
178
                    break;
179
                default:
180
                    return $claim;
181
            }
182
        }
183
184
        return null;
185
    }
186
187
    /**
188
     * @param PairwiseSubjectIdentifierAlgorithm $pairwiseAlgorithm
189
     */
190
    public function enablePairwiseSubject(PairwiseSubjectIdentifierAlgorithm $pairwiseAlgorithm)
191
    {
192
        $this->pairwiseAlgorithm = $pairwiseAlgorithm;
193
    }
194
195
    /**
196
     * @return bool
197
     */
198
    public function isPairwiseSubjectIdentifierSupported(): bool
199
    {
200
        return null !== $this->pairwiseAlgorithm;
201
    }
202
203
    /**
204
     * @return PairwiseSubjectIdentifierAlgorithm|null
205
     */
206
    public function getPairwiseSubjectIdentifierAlgorithm(): ? PairwiseSubjectIdentifierAlgorithm
207
    {
208
        return $this->pairwiseAlgorithm;
209
    }
210
211
    /**
212
     * @param Client      $client
213
     * @param UserAccount $userAccount
214
     * @param string      $redirectUri
215
     *
216
     * @return string
217
     */
218
    private function calculateSubjectIdentifier(Client $client, UserAccount $userAccount, string $redirectUri): string
219
    {
220
        $sub = $userAccount->getUserAccountId()->getValue();
221
        if (false === $this->isPairwiseSubjectIdentifierSupported()) {
222
            return $sub;
223
        }
224
        if (($client->has('subject_type') && ('pairwise' === $client->get('subject_type')))) {
225
            $sectorIdentifierHost = $this->getSectorIdentifierHost($client, $redirectUri);
226
227
            return $this->pairwiseAlgorithm->calculateSubjectIdentifier(
228
                $userAccount,
229
                $sectorIdentifierHost
230
            );
231
        }
232
233
        return $sub;
234
    }
235
236
    /**
237
     * @param Client $client
238
     * @param string $redirectUri
239
     *
240
     * @return string
241
     */
242
    private function getSectorIdentifierHost(Client $client, string $redirectUri): string
243
    {
244
        $uri = $redirectUri;
245
246
        if (true === $client->has('sector_identifier_uri')) {
247
            $uri = $client->get('sector_identifier_uri');
248
        }
249
250
        $data = parse_url($uri);
251
        if (!is_array($data) || !array_key_exists('host', $data)) {
252
            throw new \InvalidArgumentException(sprintf('Invalid Sector Identifier Uri "%s".', $uri));
253
        }
254
255
        return $data['host'];
256
    }
257
}
258