Passed
Pull Request — master (#1298)
by
unknown
31:36
created

OAuthServerException::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 11
c 0
b 0
f 0
nc 2
nop 8
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 2
rs 9.9

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @author      Alex Bilbie <[email protected]>
4
 * @copyright   Copyright (c) Alex Bilbie
5
 * @license     http://mit-license.org/
6
 *
7
 * @link        https://github.com/thephpleague/oauth2-server
8
 */
9
10
namespace League\OAuth2\Server\Exception;
11
12
use Exception;
13
use Psr\Http\Message\ResponseInterface;
14
use Psr\Http\Message\ServerRequestInterface;
15
use Throwable;
16
17
class OAuthServerException extends Exception
18
{
19
    /**
20
     * @var int
21
     */
22
    private $httpStatusCode;
23
24
    /**
25
     * @var string
26
     */
27
    private $errorType;
28
29
    /**
30
     * @var null|string
31
     */
32
    private $hint;
33
34
    /**
35
     * @var null|string
36
     */
37
    private $redirectUri;
38
39
    /**
40
     * @var string
41
     */
42
    private $queryDelimiter;
43
44
    /**
45
     * @var array
46
     */
47
    private $payload;
48
49
    /**
50
     * @var ServerRequestInterface
51
     */
52
    private $serverRequest;
53
54
    /**
55
     * Throw a new exception.
56
     *
57
     * @param string      $message        Error message
58
     * @param int         $code           Error code
59
     * @param string      $errorType      Error type
60 83
     * @param int         $httpStatusCode HTTP status code to send (default = 400)
61
     * @param null|string $hint           A helper hint
62 83
     * @param null|string $redirectUri    A HTTP URI to redirect the user back to
63 83
     * @param Throwable   $previous       Previous exception
64 83
     * @param string      $queryDelimiter Query delimiter of the redirect URI
65 83
     */
66 83
    public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null, Throwable $previous = null, $queryDelimiter = '?')
67 83
    {
68 83
        parent::__construct($message, $code, $previous);
69 83
        $this->httpStatusCode = $httpStatusCode;
70
        $this->errorType = $errorType;
71 83
        $this->hint = $hint;
72 52
        $this->redirectUri = $redirectUri;
73
        $this->queryDelimiter = $queryDelimiter;
74 83
        $this->payload = [
75
            'error'             => $errorType,
76
            'error_description' => $message,
77
        ];
78
        if ($hint !== null) {
79
            $this->payload['hint'] = $hint;
80
        }
81 10
    }
82
83 10
    /**
84
     * Returns the current payload.
85
     *
86
     * @return array
87 10
     */
88 10
    public function getPayload()
89
    {
90
        $payload = $this->payload;
91 10
92
        // The "message" property is deprecated and replaced by "error_description"
93
        // TODO: remove "message" property
94
        if (isset($payload['error_description']) && !isset($payload['message'])) {
95
            $payload['message'] = $payload['error_description'];
96
        }
97
98
        return $payload;
99
    }
100
101
    /**
102
     * Updates the current payload.
103
     *
104
     * @param array $payload
105
     */
106
    public function setPayload(array $payload)
107
    {
108
        $this->payload = $payload;
109 18
    }
110
111 18
    /**
112 18
     * Set the server request that is responsible for generating the exception
113
     *
114
     * @param ServerRequestInterface $serverRequest
115
     */
116
    public function setServerRequest(ServerRequestInterface $serverRequest)
117
    {
118
        $this->serverRequest = $serverRequest;
119 2
    }
120
121 2
    /**
122 2
     * Unsupported grant type error.
123
     *
124 2
     * @return static
125
     */
126
    public static function unsupportedGrantType()
127
    {
128
        $errorMessage = 'The authorization grant type is not supported by the authorization server.';
129
        $hint = 'Check that all required parameters have been provided';
130
131
        return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
132
    }
133
134
    /**
135
     * Invalid request error.
136 26
     *
137
     * @param string      $parameter The invalid parameter
138
     * @param null|string $hint
139 26
     * @param Throwable   $previous  Previous exception
140 26
     *
141
     * @return static
142 26
     */
143
    public static function invalidRequest($parameter, $hint = null, Throwable $previous = null)
144
    {
145
        $errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
146
            'includes a parameter more than once, or is otherwise malformed.';
147
        $hint = ($hint === null) ? \sprintf('Check the `%s` parameter', $parameter) : $hint;
148
149
        return new static($errorMessage, 3, 'invalid_request', 400, $hint, null, $previous);
150
    }
151
152 18
    /**
153
     * Invalid client error.
154 18
     *
155
     * @param ServerRequestInterface $serverRequest
156 18
     *
157
     * @return static
158 18
     */
159
    public static function invalidClient(ServerRequestInterface $serverRequest)
160
    {
161
        $exception = new static('Client authentication failed', 4, 'invalid_client', 401);
162
163
        $exception->setServerRequest($serverRequest);
164
165
        return $exception;
166
    }
167
168
    /**
169 5
     * Invalid scope error.
170
     *
171 5
     * @param string      $scope          The bad scope
172
     * @param null|string $redirectUri    A HTTP URI to redirect the user back to
173 5
     * @param string      $queryDelimiter Query delimiter of the redirect URI
174
     *
175
     * @return static
176 5
     */
177
    public static function invalidScope($scope, $redirectUri = null, $queryDelimiter = '?')
178 5
    {
179
        $errorMessage = 'The requested scope is invalid, unknown, or malformed';
180
181
        if (empty($scope)) {
182 5
            $hint = 'Specify a scope in the request or set a default scope';
183
        } else {
184
            $hint = \sprintf(
185
                'Check the `%s` scope',
186
                \htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
187
            );
188
        }
189
190 2
        return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri, null, $queryDelimiter);
191
    }
192 2
193
    /**
194
     * Invalid credentials error.
195
     *
196
     * @return static
197
     */
198
    public static function invalidCredentials()
199
    {
200
        return new static('The user credentials were incorrect.', 6, 'invalid_grant', 400);
201
    }
202
203
    /**
204
     * Server error.
205
     *
206
     * @param string    $hint
207
     * @param Throwable $previous
208
     *
209
     * @return static
210
     *
211
     * @codeCoverageIgnore
212
     */
213
    public static function serverError($hint, Throwable $previous = null)
214
    {
215
        return new static(
216
            'The authorization server encountered an unexpected condition which prevented it from fulfilling'
217
            . ' the request: ' . $hint,
218
            7,
219
            'server_error',
220
            500,
221
            null,
222
            null,
223
            $previous
224
        );
225
    }
226
227 4
    /**
228
     * Invalid refresh token.
229 4
     *
230
     * @param null|string $hint
231
     * @param Throwable   $previous
232
     *
233
     * @return static
234
     */
235
    public static function invalidRefreshToken($hint = null, Throwable $previous = null)
236
    {
237
        return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint, null, $previous);
238
    }
239
240
    /**
241 16
     * Access denied.
242
     *
243 16
     * @param null|string $hint
244 16
     * @param null|string $redirectUri
245 16
     * @param Throwable   $previous
246 16
     * @param string      $queryDelimiter
247 16
     *
248
     * @return static
249
     */
250
    public static function accessDenied($hint = null, $redirectUri = null, Throwable $previous = null, $queryDelimiter = '?')
251
    {
252
        return new static(
253
            'The resource owner or authorization server denied the request.',
254
            9,
255
            'access_denied',
256
            401,
257
            $hint,
258
            $redirectUri,
259
            $previous,
260
            $queryDelimiter
261 1
        );
262
    }
263 1
264 1
    /**
265
     * Invalid grant.
266 1
     *
267 1
     * @param string $hint
268 1
     *
269 1
     * @return static
270
     */
271
    public static function invalidGrant($hint = '')
272
    {
273
        return new static(
274
            'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
275
                . 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
276
                . 'or was issued to another client.',
277 3
            10,
278
            'invalid_grant',
279 3
            400,
280
            $hint
281
        );
282
    }
283
284
    /**
285
     * @return string
286
     */
287
    public function getErrorType()
288
    {
289
        return $this->errorType;
290
    }
291 10
292
    /**
293 10
     * Generate a HTTP response.
294
     *
295 10
     * @param ResponseInterface $response
296
     * @param bool              $useFragment True if errors should be in the URI fragment instead of query string
297 10
     * @param int               $jsonOptions options passed to json_encode
298 3
     *
299 1
     * @return ResponseInterface
300
     */
301 2
    public function generateHttpResponse(ResponseInterface $response, $useFragment = false, $jsonOptions = 0)
302
    {
303
        $headers = $this->getHttpHeaders();
304 3
305
        $payload = $this->getPayload();
306
307 7
        if ($this->redirectUri !== null) {
308 7
            $queryDelimiter = $useFragment === true ? '#' : $this->queryDelimiter;
309
            $this->redirectUri .= (\strstr($this->redirectUri, $queryDelimiter) === false) ? $queryDelimiter : '&';
310
311 7
            return $response->withStatus(302)->withHeader('Location', $this->redirectUri . \http_build_query($payload));
312
        }
313 7
314
        foreach ($headers as $header => $content) {
315 7
            $response = $response->withHeader($header, $content);
316
        }
317
318
        $responseBody = \json_encode($payload, $jsonOptions) ?: 'JSON encoding of payload failed';
319
320
        $response->getBody()->write($responseBody);
321
322
        return $response->withStatus($this->getHttpStatusCode());
323 10
    }
324
325
    /**
326 10
     * Get all headers that have to be send with the error response.
327
     *
328
     * @return array Array with header values
329
     */
330
    public function getHttpHeaders()
331
    {
332
        $headers = [
333
            'Content-type' => 'application/json',
334
        ];
335
336
        // Add "WWW-Authenticate" header
337 10
        //
338 2
        // RFC 6749, section 5.2.:
339
        // "If the client attempted to authenticate via the 'Authorization'
340 2
        // request header field, the authorization server MUST
341
        // respond with an HTTP 401 (Unauthorized) status code and
342
        // include the "WWW-Authenticate" response header field
343 10
        // matching the authentication scheme used by the client.
344
        if ($this->errorType === 'invalid_client' && $this->requestHasAuthorizationHeader()) {
345
            $authScheme = \strpos($this->serverRequest->getHeader('Authorization')[0], 'Bearer') === 0 ? 'Bearer' : 'Basic';
346
347
            $headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
348
        }
349
350
        return $headers;
351
    }
352
353
    /**
354
     * Check if the exception has an associated redirect URI.
355
     *
356 2
     * Returns whether the exception includes a redirect, since
357
     * getHttpStatusCode() doesn't return a 302 when there's a
358 2
     * redirect enabled. This helps when you want to override local
359
     * error pages but want to let redirects through.
360
     *
361
     * @return bool
362
     */
363
    public function hasRedirect()
364
    {
365
        return $this->redirectUri !== null;
366 1
    }
367
368 1
    /**
369
     * Returns the Redirect URI used for redirecting.
370
     *
371
     * @return string|null
372
     */
373
    public function getRedirectUri()
374
    {
375
        return $this->redirectUri;
376 9
    }
377
378 9
    /**
379
     * Returns the HTTP status code to send when the exceptions is output.
380
     *
381
     * @return int
382
     */
383
    public function getHttpStatusCode()
384 15
    {
385
        return $this->httpStatusCode;
386 15
    }
387
388
    /**
389
     * @return null|string
390
     */
391
    public function getHint()
392
    {
393
        return $this->hint;
394
    }
395
396
    /**
397 5
     * Check if the request has a non-empty 'Authorization' header value.
398
     *
399 5
     * Returns true if the header is present and not an empty string, false
400 2
     * otherwise.
401
     *
402
     * @return bool
403 3
     */
404
    private function requestHasAuthorizationHeader()
405
    {
406
        if (!$this->serverRequest->hasHeader('Authorization')) {
407
            return false;
408
        }
409
410 3
        $authorizationHeader = $this->serverRequest->getHeader('Authorization');
411 1
412
        // Common .htaccess configurations yield an empty string for the
413
        // 'Authorization' header when one is not provided by the client.
414 2
        // For practical purposes that case should be treated as though the
415
        // header isn't present.
416
        // See https://github.com/thephpleague/oauth2-server/issues/1162
417
        if (empty($authorizationHeader) || empty($authorizationHeader[0])) {
418
            return false;
419
        }
420
421
        return true;
422
    }
423
}
424