Newer
Older
'%s: The option %s must be a string or an array of strings.',
$this->location,
var_export($name, true),
),
);
return $ret;
}
/**
* This function retrieves an optional configuration option with a string or an array of strings.
*
* If the configuration option is a string, it will be converted to an array with a single string
*
* @param string $name The name of the option.
* @param string[]|null $default A default value which will be returned if the option isn't found.
* The default value can be null or an array of strings.
*
* @return string[]|null The option with the given name, or $default if the option isn't found
* and $default is specified.
* @psalm-return ($default is null ? array|null : array)
*
* @throws \SimpleSAML\Assert\AssertionFailedException If the option is not a string or an array of strings.
*/
public function getOptionalArrayizeString(string $name, ?array $default): ?array
{
Assert::nullOrAllString(
$ret,
sprintf(
'%s: The option %s must be null, a string or an array of strings.',
$this->location,
var_export($name, true),
),
);
return $ret;
* Retrieve an array as a \SimpleSAML\Configuration object.
* This function will load the value of an option into a \SimpleSAML\Configuration object.
* The option must contain an array.
* An exception will be thrown if this option isn't an array, or if this option isn't found.
*
* @param string $name The name of the option.
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
* @return \SimpleSAML\Configuration The option with the given name,
*
* @throws \SimpleSAML\Assert\AssertionFailedException If the option is not an array.
*/
public function getConfigItem(string $name): Configuration
{
$ret = $this->getArray($name);
return self::loadFromArray($ret, $this->location . '[' . var_export($name, true) . ']');
}
/**
* Retrieve an optional array as a \SimpleSAML\Configuration object.
*
* This function will load the optional value of an option into a \SimpleSAML\Configuration object.
* The option must contain an array.
*
* An exception will be thrown if this option isn't an array, or if this option isn't found.
*
* @param string $name The name of the option.
* @param array|null $default A default value which will be used if the option isn't found. An empty Configuration
* object will be returned if this parameter isn't given and the option doesn't exist.
* This function will only return null if $default is set to null and the option doesn't exist.
* @return \SimpleSAML\Configuration|null The option with the given name,
* or $default, converted into a Configuration object.
* @psalm-return ($default is array ? \SimpleSAML\Configuration : \SimpleSAML\Configuration|null)
* @throws \SimpleSAML\Assert\AssertionFailedException If the option is not an array.
public function getOptionalConfigItem(string $name, ?array $default): ?Configuration
if ($ret !== null) {
return self::loadFromArray($ret, $this->location . '[' . var_export($name, true) . ']');
}
return null;
}
/**
* Retrieve list of options.
*
* This function returns the name of all options which are defined in this
* configuration file, as an array of strings.
*
* @return string[] Name of all options defined in this configuration file.
{
return array_keys($this->configuration);
}
/**
* Convert this configuration object back to an array.
*
* @return array An associative array with all configuration options and values.
*/
{
return $this->configuration;
}
/**
* Retrieve the default binding for the given endpoint type.
*
* This function combines the current metadata type (SAML 2 / SAML 1.1)
* with the endpoint type to determine which binding is the default.
*
* @param string $endpointType The endpoint type.
*
* @return string The default binding.
* @throws \Exception If the default binding is missing for this endpoint type.
private function getDefaultBinding(string $endpointType): string
{
$set = $this->getString('metadata-set');
case 'saml20-idp-remote:SingleSignOnService':
case 'saml20-idp-remote:SingleLogoutService':
case 'saml20-sp-remote:SingleLogoutService':
case 'saml20-sp-remote:AssertionConsumerService':
case 'saml20-idp-remote:ArtifactResolutionService':
throw new Exception('Missing default binding for ' . $endpointType . ' in ' . $set);
}
}
/**
* Helper function for dealing with metadata endpoints.
*
* @param string $endpointType The endpoint type.
*
* @return array Array of endpoints of the given type.
* @throws \Exception If any element of the configuration options for this endpoint type is incorrect.
public function getEndpoints(string $endpointType): array
$loc = $this->location . '[' . var_export($endpointType, true) . ']:';
if (!array_key_exists($endpointType, $this->configuration)) {
// no endpoints of the given type
}
$eps = $this->configuration[$endpointType];
if (is_string($eps)) {
throw new Exception($loc . ': Expected array or string.');
}
foreach ($eps as $i => &$ep) {
$iloc = $loc . '[' . var_export($i, true) . ']';
'Location' => $ep,
'Binding' => $this->getDefaultBinding($endpointType),
$responseLocation = $this->getOptionalString($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.
* @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.
public function getEndpointPrioritizedByBinding(
string $endpointType,
array $bindings,
$default = self::REQUIRED_OPTION
$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.
* @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.
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.
* @return array Associative array with language => string pairs.
* @throws \SimpleSAML\Assert\AssertionFailedException
* If the translation is not an array or a string, or its index or value are not strings.
Assert::isArray($ret, sprintf('%s: Must be an array or a string.', $this->location));
foreach ($ret as $k => $v) {
Assert::string($k, sprintf('%s: Invalid language code: %s', $this->location, var_export($k, true)));
Assert::string($v, sprintf('%s[%s]: Must be a string.', $this->location, var_export($v, true)));
}
return $ret;
}
/**
* Retrieve an optional string which may be localized into many languages.
*
* The default language returned is always 'en'.
*
* @param string $name The name of the option.
* @param array|null $default The default value.
*
* @return array|null Associative array with language => string pairs, or the provided default value.
* @psalm-return ($default is array ? array : array|null)
*
* @throws \SimpleSAML\Assert\AssertionFailedException
* If the translation is not an array or a string, or its index or value are not strings.
*/
public function getOptionalLocalizedString(string $name, ?array $default): ?array
{
if (!$this->hasValue($name)) {
// the option wasn't found, or it matches the default value. In any case, return this value
return $default;
}
return $this->getLocalizedString($name);
}
/**
* Get public key from metadata.
*
* @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 ''.
*
* @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 location.
* @throws \SimpleSAML\Error\Exception If the location does not contain a valid PEM-encoded certificate, or there
* is no certificate in the metadata.
public function getPublicKeys(?string $use = null, bool $required = false, string $prefix = ''): array
foreach ($this->getArray($prefix . 'keys') as $key) {
if ($use !== null && isset($key[$use]) && !$key[$use]) {
continue;
}
if (isset($key['X509Certificate'])) {
$key['X509Certificate'] = preg_replace('/\s+/', '', $key['X509Certificate']);
}
$ret[] = $key;
}
} elseif ($this->hasValue($prefix . 'certData')) {
$certData = $this->getString($prefix . 'certData');
$certData = preg_replace('/\s+/', '', $certData);
$keyName = $this->getOptionalString($prefix . 'key_name', null);
'encryption' => true,
'signing' => true,
'type' => 'X509Certificate',
'X509Certificate' => $certData,
} elseif ($this->hasValue($prefix . 'certificate')) {
$location = $this->getString($prefix . 'certificate');
$cryptoUtils = new Utils\Crypto();
$data = $cryptoUtils->retrieveCertificate($location);
if ($data === null) {
$this->location . ': Unable to load certificate/public key from location "' . $location . '".'
// extract certificate data (if this is a certificate)
$pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
if (!preg_match($pattern, $data, $matches)) {
throw new Error\Exception(
$this->location . ': Could not find PEM encoded certificate in "' . $location . '".'
);
}
$certData = preg_replace('/\s+/', '', $matches[1]);
$keyName = $this->getOptionalString($prefix . 'key_name', null);
'encryption' => true,
'signing' => true,
'type' => 'X509Certificate',
'X509Certificate' => $certData,
} elseif ($required === true) {
throw new 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
public static function clearInternalState(): void
self::$configDirs = [];
self::$instance = [];
self::$loadedConfigs = [];