Completed
Push — master ( 008050...e7801a )
by
unknown
11s
created

ServiceValidator::validateServiceIsLegal()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
c 1
b 0
f 0
dl 0
loc 25
rs 9.4555
cc 5
nc 7
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Module\casserver\Cas;
6
7
use SimpleSAML\Configuration;
8
use SimpleSAML\Logger;
9
use SimpleSAML\Module\casserver\Codebooks\OverrideConfigPropertiesEnum;
10
11
/**
12
 * Validates if a CAS service can use server
13
 * @package SimpleSAML\Module\casserver\Cas
14
 */
15
class ServiceValidator
16
{
17
    /**
18
     * @var \SimpleSAML\Configuration
19
     */
20
    private Configuration $mainConfig;
21
22
    /**
23
     * ServiceValidator constructor.
24
     * @param Configuration $mainConfig
25
     */
26
    public function __construct(Configuration $mainConfig)
27
    {
28
        $this->mainConfig = $mainConfig;
29
    }
30
31
    /**
32
     * Check that the $service is allowed, and if so return the configuration to use.
33
     *
34
     * @param   string  $service  The service url. Assume to already be url decoded
35
     *
36
     * @return Configuration|null Return the configuration to use for this service, or null if service is not allowed
37
     * @throws \ErrorException
38
     */
39
    public function checkServiceURL(string $service): ?Configuration
40
    {
41
        $isValidService = false;
42
        $legalUrl = 'undefined';
43
        $configOverride = null;
44
        $legalServiceUrlsConfig = $this->mainConfig->getOptionalArray('legal_service_urls', []);
45
46
        foreach ($legalServiceUrlsConfig as $index => $value) {
47
            // Support two styles:  0 => 'https://example' and 'https://example' => [ extra config ]
48
            $legalUrl = \is_int($index) ? $value : $index;
49
            if (empty($legalUrl)) {
50
                Logger::warning("Ignoring empty CAS legal service url '$legalUrl'.");
51
                continue;
52
            }
53
54
            $configOverride = \is_int($index) ? null : $value;
55
56
            if ($isValidService = $this->validateServiceIsLegal($legalUrl, $service)) {
57
                break;
58
            }
59
        }
60
61
        if (!$isValidService) {
0 ignored issues
show
introduced by
The condition $isValidService is always false.
Loading history...
62
            return null;
63
        }
64
65
        $serviceConfig = $this->mainConfig->toArray();
66
        // Return contextual information about which url rule triggered the validation
67
        $serviceConfig['casService'] = [
68
            'matchingUrl' => $legalUrl,
69
            'serviceUrl'  => $service,
70
        ];
71
        if ($configOverride !== null) {
72
            // We need to remove all the unsupported configuration keys
73
            $supportedProperties = array_column(OverrideConfigPropertiesEnum::cases(), 'value');
74
            $configOverride = array_filter(
75
                $configOverride,
76
                static fn($property) => \in_array($property, $supportedProperties, true),
77
                ARRAY_FILTER_USE_KEY,
78
            );
79
            // Merge the configurations
80
            $serviceConfig = array_merge($serviceConfig, $configOverride);
81
        }
82
        return Configuration::loadFromArray($serviceConfig);
83
    }
84
85
    /**
86
     * @param string $legalUrl The string or regex to use for comparison
87
     * @param string $service  The service to compare
88
     *
89
     * @return bool Whether the service is legal
90
     * @throws \ErrorException
91
     */
92
    protected function validateServiceIsLegal(string $legalUrl, string $service): bool
93
    {
94
        $isValid = false;
95
        if (!ctype_alnum($legalUrl[0])) {
96
            // Since "If the regex pattern passed does not compile to a valid regex, an E_WARNING is emitted. "
97
            // we will throw an exception if the warning is emitted and use try-catch to handle it
98
            set_error_handler(static function ($severity, $message, $file, $line) {
99
                throw new \ErrorException($message, $severity, $severity, $file, $line);
100
            }, E_WARNING);
101
102
            try {
103
                if (preg_match($legalUrl, $service) === 1) {
104
                    $isValid = true;
105
                }
106
            } catch (\ErrorException $e) {
107
                // do nothing
108
                Logger::warning("Invalid CAS legal service url '$legalUrl'. Error " . preg_last_error_msg());
109
            } finally {
110
                restore_error_handler();
111
            }
112
        } elseif (str_starts_with($service, $legalUrl)) {
113
            $isValid = true;
114
        }
115
116
        return $isValid;
117
    }
118
}
119