Newer
Older
Jaime Perez Crespo
committed
namespace SimpleSAML;
use SimpleSAML\Utils\System;
/**
* Configuration of SimpleSAMLphp
Andreas Åkre Solberg
committed
*
Andreas Åkre Solberg
committed
* @author Andreas Aakre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
* @package SimpleSAMLphp
class Configuration implements Utils\ClearableState
{
/**
* A default value which means that the given option is required.
*
* @var string
*/
const REQUIRED_OPTION = '___REQUIRED_OPTION___';
/**
* Associative array with mappings from instance-names to configuration objects.
*/
private static $instance = array();
/**
* Configuration directories.
*
* This associative array contains the mappings from configuration sets to
* configuration directories.
*/
private static $configDirs = array();
/**
* Cache of loaded configuration files.
*
* The index in the array is the full path to the file.
*/
private static $loadedConfigs = array();
/**
* The configuration array.
*/
private $configuration;
/**
* The location which will be given when an error occurs.
*
* @var string
*/
private $location;
/**
* The file this configuration was loaded from.
*
* @var string|null
*/
private $filename = null;
Jaime Perez Crespo
committed
/**
* Temporary property that tells if the deprecated getBaseURL() method has been called or not.
*
* @var bool
*/
private $deprecated_base_url_used = false;
/**
* Initializes a configuration from the given array.
*
* @param array $config The configuration array.
* @param string $location The location which will be given when an error occurs.
*/
public function __construct($config, $location)
{
assert(is_array($config));
assert(is_string($location));
$this->configuration = $config;
$this->location = $location;
}
/**
* Load the given configuration file.
*
* @param string $filename The full path of the configuration file.
* @param bool $required Whether the file is required.
* @return \SimpleSAML\Configuration The configuration file. An exception will be thrown if the
* configuration file is missing.
* @throws \Exception If the configuration file is invalid or missing.
*/
private static function loadFromFile($filename, $required)
{
assert(is_string($filename));
assert(is_bool($required));
if (array_key_exists($filename, self::$loadedConfigs)) {
return self::$loadedConfigs[$filename];
}
if (file_exists($filename)) {
$config = 'UNINITIALIZED';
// the file initializes a variable named '$config'
Jaime Perez Crespo
committed
ob_start();
if (interface_exists('Throwable', false)) {
} catch (\ParseError $e) {
self::$loadedConfigs[$filename] = self::loadFromArray(array(), '[ARRAY]', 'simplesaml');
throw new Error\ConfigurationError($e->getMessage(), $filename, array());
}
} else {
require($filename);
}
Jaime Perez Crespo
committed
$spurious_output = ob_get_length() > 0;
ob_end_clean();
Jaime Perez Crespo
committed
// check that $config exists
if (!isset($config)) {
throw new Error\ConfigurationError(
Jaime Perez Crespo
committed
'$config is not defined in the configuration file.',
$filename
);
}
// check that $config is initialized to an array
throw new Error\ConfigurationError(
Jaime Perez Crespo
committed
'$config is not an array.',
$filename
);
}
// check that $config is not empty
if (empty($config)) {
throw new Error\ConfigurationError(
Jaime Perez Crespo
committed
'$config is empty.',
$filename
);
// file does not exist, but is required
throw new Error\ConfigurationError('Missing configuration file', $filename);
Jaime Perez Crespo
committed
// file does not exist, but is optional, so return an empty configuration object without saving it
$cfg = new Configuration(array(), $filename);
Jaime Perez Crespo
committed
$cfg->filename = $filename;
return $cfg;
$cfg = new Configuration($config, $filename);
$cfg->filename = $filename;
self::$loadedConfigs[$filename] = $cfg;
Jaime Perez Crespo
committed
if ($spurious_output) {
Logger::warning(
Jaime Perez Crespo
committed
"The configuration file '$filename' generates output. Please review your configuration."
);
}
return $cfg;
}
/**
* Set the directory for configuration files for the given configuration set.
*
* @param string $path The directory which contains the configuration files.
* @param string $configSet The configuration set. Defaults to 'simplesaml'.
*/
public static function setConfigDir($path, $configSet = 'simplesaml')
{
assert(is_string($path));
assert(is_string($configSet));
self::$configDirs[$configSet] = $path;
}
/**
* Store a pre-initialized configuration.
*
* Allows consumers to create configuration objects without having them
* loaded from a file.
*
* @param \SimpleSAML\Configuration $config The configuration object to store
* @param string $filename The name of the configuration file.
* @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
*/
public static function setPreLoadedConfig(Configuration $config, $filename = 'config.php', $configSet = 'simplesaml')
{
assert(is_string($filename));
assert(is_string($configSet));
if (!array_key_exists($configSet, self::$configDirs)) {
if ($configSet !== 'simplesaml') {
throw new \Exception('Configuration set \'' . $configSet . '\' not initialized.');
} else {
self::$configDirs['simplesaml'] = dirname(dirname(dirname(__FILE__))) . '/config';
}
}
$dir = self::$configDirs[$configSet];
$filePath = $dir . '/' . $filename;
self::$loadedConfigs[$filePath] = $config;
}
* Load a configuration file from a configuration set.
*
* @param string $filename The name of the configuration file.
* @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
* @return \SimpleSAML\Configuration The Configuration object.
* @throws \Exception If the configuration set is not initialized.
*/
public static function getConfig($filename = 'config.php', $configSet = 'simplesaml')
{
assert(is_string($filename));
assert(is_string($configSet));
if (!array_key_exists($configSet, self::$configDirs)) {
if ($configSet !== 'simplesaml') {
throw new \Exception('Configuration set \''.$configSet.'\' not initialized.');
self::$configDirs['simplesaml'] = Utils\Config::getConfigDir();
}
}
$dir = self::$configDirs[$configSet];
$filePath = $dir.'/'.$filename;
return self::loadFromFile($filePath, true);
}
/**
* Load a configuration file from a configuration set.
*
* This function will return a configuration object even if the file does not exist.
*
* @param string $filename The name of the configuration file.
* @param string $configSet The configuration set. Optional, defaults to 'simplesaml'.
*
* @return \SimpleSAML\Configuration A configuration object.
* @throws \Exception If the configuration set is not initialized.
*/
public static function getOptionalConfig($filename = 'config.php', $configSet = 'simplesaml')
{
assert(is_string($filename));
assert(is_string($configSet));
if (!array_key_exists($configSet, self::$configDirs)) {
if ($configSet !== 'simplesaml') {
throw new \Exception('Configuration set \''.$configSet.'\' not initialized.');
self::$configDirs['simplesaml'] = Utils\Config::getConfigDir();
}
}
$dir = self::$configDirs[$configSet];
$filePath = $dir.'/'.$filename;
return self::loadFromFile($filePath, false);
}
/**
* Loads a configuration from the given array.
*
* @param array $config The configuration array.
* @param string $location The location which will be given when an error occurs. Optional.
* @param string|null $instance The name of this instance. If specified, the configuration will be loaded and an
* instance with that name will be kept for it to be retrieved later with getInstance($instance). If null, the
* configuration will not be kept for later use. Defaults to null.
* @return \SimpleSAML\Configuration The configuration object.
public static function loadFromArray($config, $location = '[ARRAY]', $instance = null)
assert(is_array($config));
assert(is_string($location));
$c = new Configuration($config, $location);
if ($instance !== null) {
self::$instance[$instance] = $c;
}
return $c;
}
/**
* Get a configuration file by its instance name.
*
* This function retrieves a configuration file by its instance name. The instance
* name is initialized by the init function, or by copyFromBase function.
*
* If no configuration file with the given instance name is found, an exception will
* be thrown.
*
* @param string $instancename The instance name of the configuration file. Deprecated.
* @return \SimpleSAML\Configuration The configuration object.
* @throws \Exception If the configuration with $instancename name is not initialized.
*/
public static function getInstance($instancename = 'simplesaml')
{
// check if the instance exists already
if (array_key_exists($instancename, self::$instance)) {
return self::$instance[$instancename];
}
if ($instancename === 'simplesaml') {
Jaime Perez Crespo
committed
try {
return self::getConfig();
} catch (Error\ConfigurationError $e) {
throw Error\CriticalConfigurationError::fromException($e);
Jaime Perez Crespo
committed
}
throw new Error\CriticalConfigurationError(
Jaime Perez Crespo
committed
'Configuration with name '.$instancename.' is not initialized.'
);
}
/**
* Initialize a instance name with the given configuration file.
*
* TODO: remove.
*
* @param string $path
* @param string $instancename
* @param string $configfilename
*
* @deprecated This function is superseeded by the setConfigDir function.
*/
public static function init($path, $instancename = 'simplesaml', $configfilename = 'config.php')
{
assert(is_string($path));
assert(is_string($instancename));
assert(is_string($configfilename));
if ($instancename === 'simplesaml') {
self::setConfigDir($path, 'simplesaml');
}
// check if we already have loaded the given config - return the existing instance if we have
if (array_key_exists($instancename, self::$instance)) {
return self::$instance[$instancename];
}
self::$instance[$instancename] = self::loadFromFile($path.'/'.$configfilename, true);
return self::$instance[$instancename];
}
/**
* Load a configuration file which is located in the same directory as this configuration file.
*
* TODO: remove.
*
* @param string $instancename
* @param string $filename
*
* @deprecated This function is superseeded by the getConfig() function.
*/
public function copyFromBase($instancename, $filename)
{
assert(is_string($instancename));
assert(is_string($filename));
assert($this->filename !== null);
// check if we already have loaded the given config - return the existing instance if we have
if (array_key_exists($instancename, self::$instance)) {
return self::$instance[$instancename];
}
$dir = dirname($this->filename);
self::$instance[$instancename] = self::loadFromFile($dir.'/'.$filename, true);
return self::$instance[$instancename];
}
/**
* Retrieve the current version of SimpleSAMLphp.
*
* @return string
*/
public function getVersion()
{
}
/**
* Retrieve a configuration option set in config.php.
*
* @param string $name Name of the configuration option.
* @param mixed $default Default value of the configuration option. This parameter will default to null if not
* specified. This can be set to \SimpleSAML\Configuration::REQUIRED_OPTION, which will
* cause an exception to be thrown if the option isn't found.
*
* @return mixed The configuration option with name $name, or $default if the option was not found.
* @throws \Exception If the required option cannot be retrieved.
*/
public function getValue($name, $default = null)
{
// return the default value if the option is unset
if (!array_key_exists($name, $this->configuration)) {
if ($default === self::REQUIRED_OPTION) {
throw new \Exception(
$this->location.': Could not retrieve the required option '.
var_export($name, true)
);
}
return $default;
}
return $this->configuration[$name];
}
/**
*
* @param string $name The key in the configuration to look for.
*
* @return boolean If the value is set in this configuration.
*/
public function hasValue($name)
{
return array_key_exists($name, $this->configuration);
}
/**
* Check whether any key of the set given exists in the configuration.
*
* @param array $names An array of options to look for.
*
* @return boolean If any of the keys in $names exist in the configuration
*/
public function hasValueOneOf($names)
{
if ($this->hasValue($name)) {
return true;
}
}
return false;
}
/**
* Retrieve the absolute path of the SimpleSAMLphp installation, relative to the root of the website.
*
* For example: simplesaml/
*
* The path will always end with a '/' and never have a leading slash.
*
* @return string The absolute path relative to the root of the website.
*
* @throws \SimpleSAML\Error\CriticalConfigurationError If the format of 'baseurlpath' is incorrect.
Jaime Perez Crespo
committed
*
* @deprecated This method will be removed in SimpleSAMLphp 2.0. Please use getBasePath() instead.
*/
public function getBaseURL()
{
Jaime Perez Crespo
committed
if (!$this->deprecated_base_url_used) {
$this->deprecated_base_url_used = true;
Logger::warning(
"\SimpleSAML\Configuration::getBaseURL() is deprecated, please use getBasePath() instead."
Jaime Perez Crespo
committed
);
}
if (preg_match('/^\*(.*)$/D', $this->getString('baseurlpath', 'simplesaml/'), $matches)) {
// deprecated behaviour, will be removed in the future
return Utils\HTTP::getFirstPathElement(false).$matches[1];
Jaime Perez Crespo
committed
return ltrim($this->getBasePath(), '/');
}
/**
* Retrieve the absolute path pointing to the SimpleSAMLphp installation.
*
* The path is guaranteed to start and end with a slash ('/'). E.g.: /simplesaml/
*
* @return string The absolute path where SimpleSAMLphp can be reached in the web server.
*
* @throws \SimpleSAML\Error\CriticalConfigurationError If the format of 'baseurlpath' is incorrect.
Jaime Perez Crespo
committed
*/
public function getBasePath()
{
$baseURL = $this->getString('baseurlpath', 'simplesaml/');
Jaime Perez Crespo
committed
if (preg_match('#^https?://[^/]*(?:/(.+/?)?)?$#', $baseURL, $matches)) {
// we have a full url, we need to strip the path
Jaime Perez Crespo
committed
if (!array_key_exists(1, $matches)) {
Jaime Perez Crespo
committed
// absolute URL without path
return '/';
Jaime Perez Crespo
committed
}
Jaime Perez Crespo
committed
return '/'.rtrim($matches[1], '/')."/";
} elseif ($baseURL === '' || $baseURL === '/') {
Jaime Perez Crespo
committed
// root directory of site
Jaime Perez Crespo
committed
return '/';
} elseif (preg_match('#^/?((?:[^/\s]+/?)+)#', $baseURL, $matches)) {
Jaime Perez Crespo
committed
return '/'.rtrim($matches[1], '/').'/';
Jaime Perez Crespo
committed
/*
* Invalid 'baseurlpath'. We cannot recover from this, so throw a critical exception and try to be graceful
* with the configuration. Use a guessed base path instead of the one provided.
*/
$c = $this->toArray();
$c['baseurlpath'] = Utils\HTTP::guessBasePath();
throw new Error\CriticalConfigurationError(
'Incorrect format for option \'baseurlpath\'. Value is: "'.
$this->getString('baseurlpath', 'simplesaml/').'". Valid format is in the form'.
Jaime Perez Crespo
committed
' [(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/].',
$this->filename,
$c
* This function resolves a path which may be relative to the SimpleSAMLphp base directory.
*
* The path will never end with a '/'.
*
* @param string|null $path The path we should resolve. This option may be null.
* @return string|null $path if $path is an absolute path, or $path prepended with the base directory of this
* SimpleSAMLphp installation. We will return NULL if $path is null.
*/
public function resolvePath($path)
{
if ($path === null) {
return null;
}
return System::resolvePath($path, $this->getBaseDir());
}
/**
* Retrieve a path configuration option set in config.php.
*
* The function will always return an absolute path unless the option is not set. It will then return the default
* value.
* It checks if the value starts with a slash, and prefixes it with the value from getBaseDir if it doesn't.
* @param string $name Name of the configuration option.
* @param string|null $default Default value of the configuration option. This parameter will default to null if
* not specified.
*
* @return string|null The path configuration option with name $name, or $default if the option was not found.
*/
public function getPathValue($name, $default = null)
{
// return the default value if the option is unset
if (!array_key_exists($name, $this->configuration)) {
$path = $default;
} else {
$path = $this->configuration[$name];
}
if ($path === null) {
return null;
}
return $this->resolvePath($path).'/';
}
/**
* Retrieve the base directory for this SimpleSAMLphp installation.
*
* This function first checks the 'basedir' configuration option. If this option is undefined or null, then we
* fall back to looking at the current filename.
* @return string The absolute path to the base directory for this SimpleSAMLphp installation. This path will
* always end with a slash.
*/
public function getBaseDir()
{
// check if a directory is configured in the configuration file
$dir = $this->getString('basedir', null);
if ($dir !== null) {
// add trailing slash if it is missing
if (substr($dir, -1) !== DIRECTORY_SEPARATOR) {
$dir .= DIRECTORY_SEPARATOR;
}
return $dir;
}
// the directory wasn't set in the configuration file, path is <base directory>/lib/SimpleSAML/Configuration.php
assert(basename($dir) === 'Configuration.php');
assert(basename($dir) === 'SimpleSAML');
// Add trailing directory separator
$dir .= DIRECTORY_SEPARATOR;
return $dir;
}
/**
* This function retrieves a boolean configuration option.
*
* An exception will be thrown if this option isn't a boolean, or if this option isn't found, and no default value
* is given.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return boolean|mixed The option with the given name, or $default if the option isn't found and $default is
* specified.
* @throws \Exception If the option is not boolean.
*/
public function getBoolean($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
return $ret;
}
if (!is_bool($ret)) {
throw new \Exception(
$this->location.': The option '.var_export($name, true).
' is not a valid boolean value.'
);
}
return $ret;
}
/**
* This function retrieves a string configuration option.
*
* An exception will be thrown if this option isn't a string, or if this option isn't found, and no default value
* is given.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return string|mixed The option with the given name, or $default if the option isn't found and $default is
* specified.
* @throws \Exception If the option is not a string.
*/
public function getString($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
return $ret;
}
if (!is_string($ret)) {
throw new \Exception(
$this->location.': The option '.var_export($name, true).
' is not a valid string value.'
);
}
return $ret;
}
/**
* This function retrieves an integer configuration option.
*
* An exception will be thrown if this option isn't an integer, or if this option isn't found, and no default value
* is given.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return int|mixed The option with the given name, or $default if the option isn't found and $default is
* specified.
*
* @throws \Exception If the option is not an integer.
*/
public function getInteger($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
return $ret;
}
if (!is_int($ret)) {
throw new \Exception(
$this->location.': The option '.var_export($name, true).
' is not a valid integer value.'
);
}
return $ret;
}
/**
* This function retrieves an integer configuration option where the value must be in the specified range.
*
* An exception will be thrown if:
* - the option isn't an integer
* - the option isn't found, and no default value is given
* - the value is outside of the allowed range
*
* @param string $name The name of the option.
* @param int $minimum The smallest value which is allowed.
* @param int $maximum The largest value which is allowed.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return int|mixed The option with the given name, or $default if the option isn't found and $default is
* specified.
* @throws \Exception If the option is not in the range specified.
*/
public function getIntegerRange($name, $minimum, $maximum, $default = self::REQUIRED_OPTION)
{
assert(is_string($name));
assert(is_int($minimum));
assert(is_int($maximum));
$ret = $this->getInteger($name, $default);
if ($ret === $default) {
// the option wasn't found, or it matches the default value. In any case, return this value
return $ret;
}
if ($ret < $minimum || $ret > $maximum) {
throw new \Exception(
$this->location.': Value of option '.var_export($name, true).
' is out of range. Value is '.$ret.', allowed range is ['
.$minimum.' - '.$maximum.']'
);
}
return $ret;
}
/**
* Retrieve a configuration option with one of the given values.
*
* This will check that the configuration option matches one of the given values. The match will use
* strict comparison. An exception will be thrown if it does not match.
*
* The option can be mandatory or optional. If no default value is given, it will be considered to be
* mandatory, and an exception will be thrown if it isn't provided. If a default value is given, it
* is considered to be optional, and the default value is returned. The default value is automatically
* included in the list of allowed values.
*
* @param string $name The name of the option.
* @param array $allowedValues The values the option is allowed to take, as an array.
* @param mixed $default The default value which will be returned if the option isn't found. If this parameter
* isn't given, the option will be considered to be mandatory. The default value can be
* any value, including null.
* @return mixed The option with the given name, or $default if the option isn't found and $default is given.
* @throws \Exception If the option does not have any of the allowed values.
*/
public function getValueValidate($name, $allowedValues, $default = self::REQUIRED_OPTION)
{
assert(is_string($name));
assert(is_array($allowedValues));
$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
return $ret;
}
if (!in_array($ret, $allowedValues, true)) {
$strValues = array();
foreach ($allowedValues as $av) {
$strValues[] = var_export($av, true);
}
$strValues = implode(', ', $strValues);
throw new \Exception(
$this->location.': Invalid value given for the option '.
var_export($name, true).'. It should have one of the following values: '.
$strValues.'; but it had the following value: '.var_export($ret, true)
);
}
return $ret;
}
/**
* This function retrieves an array configuration option.
*
* An exception will be thrown if this option isn't an array, or if this option isn't found, and no
* default value is given.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
* @return array|mixed The option with the given name, or $default if the option isn't found and $default is
* specified.
*
* @throws \Exception If the option is not an array.
*/
public function getArray($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
return $ret;
}
if (!is_array($ret)) {
throw new \Exception($this->location.': The option '.var_export($name, true).' is not an array.');
}
return $ret;
}
/**
* This function retrieves an array configuration option.
*
* If the configuration option isn't an array, it will be converted to an array.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return array The option with the given name, or $default if the option isn't found and $default is specified.
*/
public function getArrayize($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
return $ret;
}
if (!is_array($ret)) {
$ret = array($ret);
}
return $ret;
}
/**
* This function retrieves a 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 mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return array The option with the given name, or $default if the option isn't found and $default is specified.
* @throws \Exception If the option is not a string or an array of strings.
*/
public function getArrayizeString($name, $default = self::REQUIRED_OPTION)
{
$ret = $this->getArrayize($name, $default);
if ($ret === $default) {
// the option wasn't found, or it matches the default value. In any case, return this value
return $ret;
}
foreach ($ret as $value) {
if (!is_string($value)) {
throw new \Exception(
$this->location.': The option '.var_export($name, true).
' must be a string or an array of strings.'
);
}
}
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 exception will be thrown if this option isn't an array, or if this option isn't found, and no default value
* is given.
*
* @param string $name The name of the option.
* @param mixed $default A default value which will be returned if the option isn't found. The option will be
* required if this parameter isn't given. The default value can be any value, including
*
* @return mixed The option with the given name, or $default if the option isn't found and $default is specified.
* @throws \Exception If the option is not an array.
*/
public function getConfigItem($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
return $ret;
}
if (!is_array($ret)) {
throw new \Exception(
$this->location.': The option '.var_export($name, true).
' is not an array.'
);
}
return self::loadFromArray($ret, $this->location.'['.var_export($name, true).']');
}