Completed
Push — master ( 1de13c...bf55ce )
by Alex
33:38
created

AbstractGrant::validateScopes()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 24
rs 8.9713
cc 3
eloc 14
nc 3
nop 2
1
<?php
2
/**
3
 * OAuth 2.0 Abstract grant.
4
 *
5
 * @author      Alex Bilbie <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
namespace League\OAuth2\Server\Grant;
12
13
use League\Event\EmitterAwareTrait;
14
use League\OAuth2\Server\CryptTrait;
15
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
16
use League\OAuth2\Server\Entities\ClientEntityInterface;
17
use League\OAuth2\Server\Entities\ScopeEntityInterface;
18
use League\OAuth2\Server\Exception\OAuthServerException;
19
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
20
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
21
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
22
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
23
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
24
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
25
use League\OAuth2\Server\RequestEvent;
26
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
27
use Psr\Http\Message\ServerRequestInterface;
28
29
/**
30
 * Abstract grant class.
31
 */
32
abstract class AbstractGrant implements GrantTypeInterface
33
{
34
    use EmitterAwareTrait, CryptTrait;
35
36
    const SCOPE_DELIMITER_STRING = ' ';
37
38
    /**
39
     * @var ServerRequestInterface
40
     */
41
    protected $request;
42
43
    /**
44
     * @var ClientRepositoryInterface
45
     */
46
    protected $clientRepository;
47
48
    /**
49
     * @var AccessTokenRepositoryInterface
50
     */
51
    protected $accessTokenRepository;
52
53
    /**
54
     * @var ScopeRepositoryInterface
55
     */
56
    protected $scopeRepository;
57
58
    /**
59
     * @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
60
     */
61
    protected $authCodeRepository;
62
63
    /**
64
     * @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
65
     */
66
    protected $refreshTokenRepository;
67
68
    /**
69
     * @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
70
     */
71
    protected $userRepository;
72
73
    /**
74
     * @var \DateInterval
75
     */
76
    protected $refreshTokenTTL;
77
78
    /**
79
     * @param ClientRepositoryInterface $clientRepository
80
     */
81
    public function setClientRepository(ClientRepositoryInterface $clientRepository)
82
    {
83
        $this->clientRepository = $clientRepository;
84
    }
85
86
    /**
87
     * @param AccessTokenRepositoryInterface $accessTokenRepository
88
     */
89
    public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
90
    {
91
        $this->accessTokenRepository = $accessTokenRepository;
92
    }
93
94
    /**
95
     * @param ScopeRepositoryInterface $scopeRepository
96
     */
97
    public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
98
    {
99
        $this->scopeRepository = $scopeRepository;
100
    }
101
102
    /**
103
     * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
104
     */
105
    public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
106
    {
107
        $this->refreshTokenRepository = $refreshTokenRepository;
108
    }
109
110
    /**
111
     * @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
112
     */
113
    public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
114
    {
115
        $this->authCodeRepository = $authCodeRepository;
116
    }
117
118
    /**
119
     * @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
120
     */
121
    public function setUserRepository(UserRepositoryInterface $userRepository)
122
    {
123
        $this->userRepository = $userRepository;
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
130
    {
131
        $this->refreshTokenTTL = $refreshTokenTTL;
132
    }
133
134
    /**
135
     * Validate the client.
136
     *
137
     * @param \Psr\Http\Message\ServerRequestInterface $request
138
     *
139
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
140
     *
141
     * @return \League\OAuth2\Server\Entities\ClientEntityInterface
142
     */
143
    protected function validateClient(ServerRequestInterface $request)
144
    {
145
        $clientId = $this->getRequestParameter(
146
            'client_id',
147
            $request,
148
            $this->getServerParameter('PHP_AUTH_USER', $request)
149
        );
150
        if (is_null($clientId)) {
151
            throw OAuthServerException::invalidRequest('client_id');
152
        }
153
154
        // If the client is confidential require the client secret
155
        $clientSecret = $this->getRequestParameter(
156
            'client_secret',
157
            $request,
158
            $this->getServerParameter('PHP_AUTH_PW', $request)
159
        );
160
161
        $client = $this->clientRepository->getClientEntity(
162
            $clientId,
163
            $this->getIdentifier(),
164
            $clientSecret
165
        );
166
167
        if (!$client instanceof ClientEntityInterface) {
168
            $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
169
            throw OAuthServerException::invalidClient();
170
        }
171
172
        // If a redirect URI is provided ensure it matches what is pre-registered
173
        $redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
174
        if ($redirectUri !== null) {
175
            if (
176
                is_string($client->getRedirectUri())
177
                && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
178
            ) {
179
                $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
180
                throw OAuthServerException::invalidClient();
181
            } elseif (
182
                is_array($client->getRedirectUri())
183
                && in_array($redirectUri, $client->getRedirectUri()) === false
184
            ) {
185
                $this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
186
                throw OAuthServerException::invalidClient();
187
            }
188
        }
189
190
        return $client;
191
    }
192
193
    /**
194
     * Validate scopes in the request.
195
     *
196
     * @param string $scopes
197
     * @param string $redirectUri
198
     *
199
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
200
     *
201
     * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
202
     */
203
    public function validateScopes(
204
        $scopes,
205
        $redirectUri = null
206
    ) {
207
        $scopesList = array_filter(
208
            explode(self::SCOPE_DELIMITER_STRING, trim($scopes)),
209
            function ($scope) {
210
                return !empty($scope);
211
            }
212
        );
213
214
        $scopes = [];
215
        foreach ($scopesList as $scopeItem) {
216
            $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
217
218
            if (!$scope instanceof ScopeEntityInterface) {
219
                throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
220
            }
221
222
            $scopes[] = $scope;
223
        }
224
225
        return $scopes;
226
    }
227
228
    /**
229
     * Retrieve request parameter.
230
     *
231
     * @param string                                   $parameter
232
     * @param \Psr\Http\Message\ServerRequestInterface $request
233
     * @param mixed                                    $default
234
     *
235
     * @return null|string
236
     */
237
    protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
238
    {
239
        $requestParameters = (array) $request->getParsedBody();
240
241
        return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
242
    }
243
244
    /**
245
     * Retrieve query string parameter.
246
     *
247
     * @param string                                   $parameter
248
     * @param \Psr\Http\Message\ServerRequestInterface $request
249
     * @param mixed                                    $default
250
     *
251
     * @return null|string
252
     */
253
    protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
254
    {
255
        return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
256
    }
257
258
    /**
259
     * Retrieve cookie parameter.
260
     *
261
     * @param string                                   $parameter
262
     * @param \Psr\Http\Message\ServerRequestInterface $request
263
     * @param mixed                                    $default
264
     *
265
     * @return null|string
266
     */
267
    protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
268
    {
269
        return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
270
    }
271
272
    /**
273
     * Retrieve server parameter.
274
     *
275
     * @param string                                   $parameter
276
     * @param \Psr\Http\Message\ServerRequestInterface $request
277
     * @param mixed                                    $default
278
     *
279
     * @return null|string
280
     */
281
    protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
282
    {
283
        return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
284
    }
285
286
    /**
287
     * Issue an access token.
288
     *
289
     * @param \DateInterval                                         $accessTokenTTL
290
     * @param \League\OAuth2\Server\Entities\ClientEntityInterface  $client
291
     * @param string                                                $userIdentifier
292
     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
293
     *
294
     * @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
295
     */
296
    protected function issueAccessToken(
297
        \DateInterval $accessTokenTTL,
298
        ClientEntityInterface $client,
299
        $userIdentifier,
300
        array $scopes = []
301
    ) {
302
        $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
303
        $accessToken->setClient($client);
304
        $accessToken->setUserIdentifier($userIdentifier);
305
        $accessToken->setIdentifier($this->generateUniqueIdentifier());
306
        $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
307
308
        foreach ($scopes as $scope) {
309
            $accessToken->addScope($scope);
310
        }
311
312
        $this->accessTokenRepository->persistNewAccessToken($accessToken);
313
314
        return $accessToken;
315
    }
316
317
    /**
318
     * Issue an auth code.
319
     *
320
     * @param \DateInterval                                         $authCodeTTL
321
     * @param \League\OAuth2\Server\Entities\ClientEntityInterface  $client
322
     * @param string                                                $userIdentifier
323
     * @param string                                                $redirectUri
324
     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
325
     *
326
     * @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
327
     */
328
    protected function issueAuthCode(
329
        \DateInterval $authCodeTTL,
330
        ClientEntityInterface $client,
331
        $userIdentifier,
332
        $redirectUri,
333
        array $scopes = []
334
    ) {
335
        $authCode = $this->authCodeRepository->getNewAuthCode();
336
        $authCode->setIdentifier($this->generateUniqueIdentifier());
337
        $authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
338
        $authCode->setClient($client);
339
        $authCode->setUserIdentifier($userIdentifier);
340
        $authCode->setRedirectUri($redirectUri);
341
342
        foreach ($scopes as $scope) {
343
            $authCode->addScope($scope);
344
        }
345
346
        $this->authCodeRepository->persistNewAuthCode($authCode);
347
348
        return $authCode;
349
    }
350
351
    /**
352
     * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
353
     *
354
     * @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
355
     */
356
    protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
357
    {
358
        $refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
359
        $refreshToken->setIdentifier($this->generateUniqueIdentifier());
360
        $refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
361
        $refreshToken->setAccessToken($accessToken);
362
363
        $this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
364
365
        return $refreshToken;
366
    }
367
368
    /**
369
     * Generate a new unique identifier.
370
     *
371
     * @param int $length
372
     *
373
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
374
     *
375
     * @return string
376
     */
377
    protected function generateUniqueIdentifier($length = 40)
378
    {
379
        try {
380
            return bin2hex(random_bytes($length));
381
            // @codeCoverageIgnoreStart
382
        } catch (\TypeError $e) {
383
            throw OAuthServerException::serverError('An unexpected error has occurred');
384
        } catch (\Error $e) {
385
            throw OAuthServerException::serverError('An unexpected error has occurred');
386
        } catch (\Exception $e) {
387
            // If you get this message, the CSPRNG failed hard.
388
            throw OAuthServerException::serverError('Could not generate a random string');
389
        }
390
        // @codeCoverageIgnoreEnd
391
    }
392
393
    /**
394
     * {@inheritdoc}
395
     */
396
    public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
397
    {
398
        $requestParameters = (array) $request->getParsedBody();
399
400
        return (
401
            array_key_exists('grant_type', $requestParameters)
402
            && $requestParameters['grant_type'] === $this->getIdentifier()
403
        );
404
    }
405
406
    /**
407
     * {@inheritdoc}
408
     */
409
    public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
410
    {
411
        return false;
412
    }
413
414
    /**
415
     * {@inheritdoc}
416
     */
417
    public function validateAuthorizationRequest(ServerRequestInterface $request)
418
    {
419
        throw new \LogicException('This grant cannot validate an authorization request');
420
    }
421
422
    /**
423
     * {@inheritdoc}
424
     */
425
    public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
426
    {
427
        throw new \LogicException('This grant cannot complete an authorization request');
428
    }
429
}
430