RedirectUriValidator::matchExactUri()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 1
c 1
b 1
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * @author      Sebastiano Degan <[email protected]>
5
 * @copyright   Copyright (c) Alex Bilbie
6
 * @license     http://mit-license.org/
7
 *
8
 * @link        https://github.com/thephpleague/oauth2-server
9
 */
10
11
declare(strict_types=1);
12
13
namespace League\OAuth2\Server\RedirectUriValidators;
14
15
use League\Uri\Exceptions\SyntaxError;
16
use League\Uri\Uri;
17
18
use function in_array;
19
use function is_string;
20
21
class RedirectUriValidator implements RedirectUriValidatorInterface
22
{
23
    /**
24
     * @var string[]
25
     */
26
    private array $allowedRedirectUris;
27
28
    /**
29
     * New validator instance for the given uri
30
     *
31
     * @param string[]|string $allowedRedirectUris
32
     */
33 23
    public function __construct(array|string $allowedRedirectUris)
34
    {
35 23
        if (is_string($allowedRedirectUris)) {
0 ignored issues
show
introduced by
The condition is_string($allowedRedirectUris) is always false.
Loading history...
36 16
            $this->allowedRedirectUris = [$allowedRedirectUris];
37
        } else {
38 7
            $this->allowedRedirectUris = $allowedRedirectUris;
39
        }
40
    }
41
42
    /**
43
     * Validates the redirect uri.
44
     *
45
     * @return bool Return true if valid, false otherwise
46
     */
47 23
    public function validateRedirectUri(string $redirectUri): bool
48
    {
49 23
        if ($this->isLoopbackUri($redirectUri)) {
50 3
            return $this->matchUriExcludingPort($redirectUri);
51
        }
52
53 20
        return $this->matchExactUri($redirectUri);
54
    }
55
56
    /**
57
     * According to section 7.3 of rfc8252, loopback uris are:
58
     *   - "http://127.0.0.1:{port}/{path}" for IPv4
59
     *   - "http://[::1]:{port}/{path}" for IPv6
60
     */
61 23
    private function isLoopbackUri(string $redirectUri): bool
62
    {
63
        try {
64 23
            $uri = Uri::new($redirectUri);
65
        } catch (SyntaxError $e) {
66
            return false;
67
        }
68
69 23
        return $uri->getScheme() === 'http'
70 23
            && (in_array($uri->getHost(), ['127.0.0.1', '[::1]'], true));
71
    }
72
73
    /**
74
     * Find an exact match among allowed uris
75
     */
76 20
    private function matchExactUri(string $redirectUri): bool
77
    {
78 20
        return in_array($redirectUri, $this->allowedRedirectUris, true);
79
    }
80
81
    /**
82
     * Find a match among allowed uris, allowing for different port numbers
83
     */
84 3
    private function matchUriExcludingPort(string $redirectUri): bool
85
    {
86 3
        $parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
87
88 3
        foreach ($this->allowedRedirectUris as $allowedRedirectUri) {
89 3
            if ($parsedUrl === $this->parseUrlAndRemovePort($allowedRedirectUri)) {
90 2
                return true;
91
            }
92
        }
93
94 1
        return false;
95
    }
96
97
    /**
98
     * Parse an url like \parse_url, excluding the port
99
     */
100 3
    private function parseUrlAndRemovePort(string $url): string
101
    {
102 3
        $uri = Uri::new($url);
103
104 3
        return (string) $uri->withPort(null);
105
    }
106
}
107