Skip to content
Snippets Groups Projects
Configuration.php 43.5 KiB
Newer Older
                $responseLocation = $this->getString($endpointType . 'Response', null);
                if ($responseLocation !== null) {
                    $ep['ResponseLocation'] = $responseLocation;
                }
            } elseif (!is_array($ep)) {
                throw new \Exception($iloc . ': Expected a string or an array.');
            }

            if (!array_key_exists('Location', $ep)) {
                throw new \Exception($iloc . ': Missing Location.');
            }
            if (!is_string($ep['Location'])) {
                throw new \Exception($iloc . ': Location must be a string.');
            }

            if (!array_key_exists('Binding', $ep)) {
                throw new \Exception($iloc . ': Missing Binding.');
            }
            if (!is_string($ep['Binding'])) {
                throw new \Exception($iloc . ': Binding must be a string.');
            }

            if (array_key_exists('ResponseLocation', $ep)) {
                if (!is_string($ep['ResponseLocation'])) {
                    throw new \Exception($iloc . ': ResponseLocation must be a string.');
                }
            }

            if (array_key_exists('index', $ep)) {
                if (!is_int($ep['index'])) {
                    throw new \Exception($iloc . ': index must be an integer.');
                }
            }
        }

        return $eps;
    }


    /**
     * Find an endpoint of the given type, using a list of supported bindings as a way to prioritize.
     *
     * @param string $endpointType The endpoint type.
Tim van Dijen's avatar
Tim van Dijen committed
     * @param string[] $bindings Sorted array of acceptable bindings.
     * @param mixed  $default The default value to return if no matching endpoint is found. If no default is provided,
     *     an exception will be thrown.
     *
     * @return mixed|null The default endpoint.
     * @throws \Exception If no supported endpoint is found.
Tim van Dijen's avatar
Tim van Dijen committed
    public function getEndpointPrioritizedByBinding(
        string $endpointType,
        array $bindings,
        $default = self::REQUIRED_OPTION
Tim van Dijen's avatar
Tim van Dijen committed
    ) {
        $endpoints = $this->getEndpoints($endpointType);

        foreach ($bindings as $binding) {
            foreach ($endpoints as $ep) {
                if ($ep['Binding'] === $binding) {
                    return $ep;
                }
            }
        }

        if ($default === self::REQUIRED_OPTION) {
            $loc = $this->location . '[' . var_export($endpointType, true) . ']:';
            throw new \Exception($loc . 'Could not find a supported ' . $endpointType . ' endpoint.');
        }

        return $default;
    }


    /**
     * Find the default endpoint of the given type.
     *
     * @param string $endpointType The endpoint type.
Tim van Dijen's avatar
Tim van Dijen committed
     * @param string[]|null $bindings Array with acceptable bindings. Can be null if any binding is allowed.
     * @param mixed  $default The default value to return if no matching endpoint is found. If no default is provided,
     *     an exception will be thrown.
     *
     * @return mixed The default endpoint, or the $default parameter if no acceptable endpoints are used.
     * @throws \Exception If no supported endpoint is found and no $default parameter is specified.
Tim van Dijen's avatar
Tim van Dijen committed
    public function getDefaultEndpoint(string $endpointType, array $bindings = null, $default = self::REQUIRED_OPTION)
    {
        $endpoints = $this->getEndpoints($endpointType);

        $defaultEndpoint = Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
        if ($defaultEndpoint !== null) {
            return $defaultEndpoint;
        }

        if ($default === self::REQUIRED_OPTION) {
            $loc = $this->location . '[' . var_export($endpointType, true) . ']:';
            throw new \Exception($loc . 'Could not find a supported ' . $endpointType . ' endpoint.');
        }

        return $default;
    }


    /**
     * Retrieve a string which may be localized into many languages.
     *
     * The default language returned is always 'en'.
     *
     * @param string $name The name of the option.
     * @param mixed  $default The default value. If no default is given, and the option isn't found, an exception will
     *     be thrown.
     *
     * @return mixed Associative array with language => string pairs, or the provided default value.
     * @throws \Exception If the translation is not an array or a string, or its index or value are not strings.
Tim van Dijen's avatar
Tim van Dijen committed
    public function getLocalizedString(string $name, $default = self::REQUIRED_OPTION)
    {
        $ret = $this->getValue($name, $default);
        if ($ret === $default) {
            // the option wasn't found, or it matches the default value. In any case, return this value
        $loc = $this->location . '[' . var_export($name, true) . ']';

        if (is_string($ret)) {
Tim van Dijen's avatar
Tim van Dijen committed
            $ret = ['en' => $ret];
        }

        if (!is_array($ret)) {
            throw new \Exception($loc . ': Must be an array or a string.');
        }

        foreach ($ret as $k => $v) {
            if (!is_string($k)) {
                throw new \Exception($loc . ': Invalid language code: ' . var_export($k, true));
            }
            if (!is_string($v)) {
                throw new \Exception($loc . '[' . var_export($v, true) . ']: Must be a string.');
     * @param string|null $use The purpose this key can be used for. (encryption or signing).
     * @param bool $required Whether the public key is required. If this is true, a
     *                       missing key will cause an exception. Default is false.
     * @param string $prefix The prefix which should be used when reading from the metadata
     *                       array. Defaults to ''.
     *
Tim van Dijen's avatar
Tim van Dijen committed
     * @return array Public key data, or empty array if no public key or was found.
     * @throws \Exception If the certificate or public key cannot be loaded from a file.
     * @throws \SimpleSAML\Error\Exception If the file does not contain a valid PEM-encoded certificate, or there is no
     * certificate in the metadata.
Tim van Dijen's avatar
Tim van Dijen committed
    public function getPublicKeys(?string $use = null, bool $required = false, string $prefix = ''): array
        if ($this->hasValue($prefix . 'keys')) {
            foreach ($this->getArray($prefix . 'keys') as $key) {
                if ($use !== null && isset($key[$use]) && !$key[$use]) {
                    continue;
                }
                if (isset($key['X509Certificate'])) {
                    // Strip whitespace from key
                    $key['X509Certificate'] = preg_replace('/\s+/', '', $key['X509Certificate']);
                }
                $ret[] = $key;
            }
Tim van Dijen's avatar
Tim van Dijen committed
            return $ret;
        } elseif ($this->hasValue($prefix . 'certData')) {
            $certData = $this->getString($prefix . 'certData');
            $certData = preg_replace('/\s+/', '', $certData);
            $keyName = $this->getString($prefix . 'key_name', null);
                    'encryption'      => true,
                    'signing'         => true,
                    'type'            => 'X509Certificate',
                    'X509Certificate' => $certData,
        } elseif ($this->hasValue($prefix . 'certificate')) {
            $configUtils = new Utils\Config();

            $file = $this->getString($prefix . 'certificate');
            $file = $configUtils->getCertPath($file);
            $data = @file_get_contents($file);

            if ($data === false) {
                throw new \Exception(
                    $this->location . ': Unable to load certificate/public key from file "' . $file . '".'
                );
            // extract certificate data (if this is a certificate)
            $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
            if (!preg_match($pattern, $data, $matches)) {
                throw new \SimpleSAML\Error\Exception(
                    $this->location . ': Could not find PEM encoded certificate in "' . $file . '".'
                );
            }
            $certData = preg_replace('/\s+/', '', $matches[1]);
            $keyName = $this->getString($prefix . 'key_name', null);
                    'encryption'      => true,
                    'signing'         => true,
                    'type'            => 'X509Certificate',
                    'X509Certificate' => $certData,
        } elseif ($required === true) {
            throw new \SimpleSAML\Error\Exception($this->location . ': Missing certificate in metadata.');
    /**
     * Clear any configuration information cached.
     * Allows for configuration files to be changed and reloaded during a given request. Most useful
     * when running phpunit tests and needing to alter config.php between test cases
Tim van Dijen's avatar
Tim van Dijen committed
    public static function clearInternalState(): void
        self::$configDirs = [];
        self::$instance = [];
        self::$loadedConfigs = [];