-
Thijs Kinkhorst authoredThijs Kinkhorst authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ConfigController.php 15.83 KiB
<?php
namespace SimpleSAML\Module\admin;
use SimpleSAML\HTTP\RunnableResponse;
use SimpleSAML\Locale\Translate;
use SimpleSAML\Utils\HTTP;
use Symfony\Component\HttpFoundation\Request;
/**
* Controller class for the admin module.
*
* This class serves the configuration views available in the module.
*
* @package SimpleSAML\Module\admin
*/
class ConfigController
{
const LATEST_VERSION_STATE_KEY = 'core:latest_simplesamlphp_version';
const RELEASES_API = 'https://api.github.com/repos/simplesamlphp/simplesamlphp/releases/latest';
/** @var \SimpleSAML\Configuration */
protected $config;
/** @var Menu */
protected $menu;
/** @var \SimpleSAML\Session */
protected $session;
/**
* ConfigController constructor.
*
* @param \SimpleSAML\Configuration $config The configuration to use.
* @param \SimpleSAML\Session $session The current user session.
*/
public function __construct(\SimpleSAML\Configuration $config, \SimpleSAML\Session $session)
{
$this->config = $config;
$this->session = $session;
$this->menu = new Menu();
}
/**
* Display basic diagnostic information on hostname, port and protocol.
*
* @param Request $request The current request.
*
* @return \SimpleSAML\XHTML\Template
*/
public function diagnostics(Request $request)
{
\SimpleSAML\Utils\Auth::requireAdmin();
$t = new \SimpleSAML\XHTML\Template($this->config, 'admin:diagnostics.twig');
$t->data = [
'remaining' => $this->session->getAuthData('admin', 'Expire') - time(),
'logouturl' => \SimpleSAML\Utils\Auth::getAdminLogoutURL(),
'items' => [
'HTTP_HOST' => [$request->getHost()],
'HTTPS' => $request->isSecure() ? ['on'] : [],
'SERVER_PROTOCOL' => [$request->getProtocolVersion()],
'getBaseURL()' => [HTTP::getBaseURL()],
'getSelfHost()' => [HTTP::getSelfHost()],
'getSelfHostWithNonStandardPort()' => [HTTP::getSelfHostWithNonStandardPort()],
'getSelfURLHost()' => [HTTP::getSelfURLHost()],
'getSelfURLNoQuery()' => [HTTP::getSelfURLNoQuery()],
'getSelfHostWithPath()' => [HTTP::getSelfHostWithPath()],
'getFirstPathElement()' => [HTTP::getFirstPathElement()],
'getSelfURL()' => [HTTP::getSelfURL()],
],
];
$this->menu->addOption('logout', \SimpleSAML\Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out'));
return $this->menu->insert($t);
}
/**
* Display the main admin page.
*
* @return \SimpleSAML\XHTML\Template
*/
public function main()
{
\SimpleSAML\Utils\Auth::requireAdmin();
$t = new \SimpleSAML\XHTML\Template($this->config, 'admin:config.twig');
$t->data = [
'warnings' => $this->getWarnings(),
'directory' => $this->config->getBaseDir(),
'version' => $this->config->getVersion(),
'links' => [
[
'href' => \SimpleSAML\Module::getModuleURL('admin/diagnostics'),
'text' => Translate::noop('Diagnostics on hostname, port and protocol')
],
[
'href' => \SimpleSAML\Module::getModuleURL('admin/phpinfo'),
'text' => Translate::noop('Information on your PHP installation')
]
],
'enablematrix' => [
'saml20idp' => $this->config->getBoolean('enable.saml20-idp', false),
'shib13idp' => $this->config->getBoolean('enable.shib13-idp', false),
],
'funcmatrix' => $this->getPrerequisiteChecks(),
'logouturl' => \SimpleSAML\Utils\Auth::getAdminLogoutURL(),
];
\SimpleSAML\Module::callHooks('configpage', $t);
$this->menu->addOption('logout', \SimpleSAML\Utils\Auth::getAdminLogoutURL(), Translate::noop('Log out'));
return $this->menu->insert($t);
}
/**
* Display the output of phpinfo().
*
* @return RunnableResponse
*/
public function phpinfo()
{
return new RunnableResponse('phpinfo');
}
/**
* Perform a list of checks on the current installation, and return the results as an array.
*
* The elements in the array returned are also arrays with the following keys:
*
* - required: Whether this prerequisite is mandatory or not. One of "required" or "optional".
* - descr: A translatable text that describes the prerequisite. If the text uses parameters, the value must be an
* array where the first value is the text to translate, and the second is a hashed array containing the
* parameters needed to properly translate the text.
* - enabled: True if the prerequisite is met, false otherwise.
*
* @return array
*/
protected function getPrerequisiteChecks()
{
$matrix = [
[
'required' => 'required',
'descr' => [
Translate::noop('PHP %minimum% or newer is needed. You are running: %current%'),
[
'%minimum%' => '5.5',
'%current%' => explode('-', phpversion())[0]
]
],
'enabled' => version_compare(phpversion(), '5.5', '>=')
]
];
$store = $this->config->getString('store.type', '');
// check dependencies used via normal functions
$functions = [
'time' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('Date/Time Extension'),
]
],
'hash' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('Hashing function'),
]
],
'gzinflate' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('ZLib'),
]
],
'openssl_sign' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('OpenSSL'),
]
],
'dom_import_simplexml' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('XML DOM'),
]
],
'preg_match' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('Regular expression support'),
]
],
'json_decode' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('JSON support'),
]
],
'class_implements' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('Standard PHP library (SPL)'),
]
],
'mb_strlen' => [
'required' => 'required',
'descr' => [
'required' => Translate::noop('Multibyte String extension'),
]
],
'curl_init' => [
'required' => $this->config->getBoolean('admin.checkforupdates', true) ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop(
'cURL (might be required by some modules)'
),
'required' => Translate::noop(
'cURL (required if automatic version checks are used, also by some modules)'
),
]
],
'session_start' => [
'required' => $store === 'phpsession' ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop('Session extension (required if PHP sessions are used)'),
'required' => Translate::noop('Session extension'),
]
],
'pdo_drivers' => [
'required' => $store === 'sql' ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop('PDO Extension (required if a database backend is used)'),
'required' => Translate::noop('PDO extension'),
]
],
'ldap_bind' => [
'required' => \SimpleSAML\Module::isModuleEnabled('ldap') ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop('LDAP extension (required if an LDAP backend is used)'),
'required' => Translate::noop('LDAP extension'),
]
],
'radius_auth_open' => [
'required' => \SimpleSAML\Module::isModuleEnabled('radius') ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop('Radius extension (required if a radius backend is used)'),
'required' => Translate::noop('Radius extension'),
]
],
];
foreach ($functions as $function => $description) {
$matrix[] = [
'required' => $description['required'],
'descr' => $description['descr'][$description['required']],
'enabled' => function_exists($function),
];
}
// check object-oriented external libraries and extensions
$libs = [
[
'classes' => ['\Predis\Predis'],
'required' => $store === 'redis' ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop('predis/predis (required if the redis data store is used)'),
'required' => Translate::noop('predis/predis library'),
]
],
[
'classes' => ['\Memcache', '\Memcached'],
'required' => $store === 'memcache' ? 'required' : 'optional',
'descr' => [
'optional' => Translate::noop(
'Memcache or Memcached extension (required if the memcache backend is used)'
),
'required' => Translate::noop('Memcache or Memcached extension'),
]
]
];
foreach ($libs as $lib) {
$enabled = false;
foreach ($lib['classes'] as $class) {
$enabled |= class_exists($class);
}
$matrix[] = [
'required' => $lib['required'],
'descr' => $lib['descr'][$lib['required']],
'enabled' => $enabled,
];
}
// perform some basic configuration checks
$matrix[] = [
'required' => 'optional',
'descr' => Translate::noop('The <code>technicalcontact_email</code> configuration option should be set'),
'enabled' => $this->config->getString('technicalcontact_email', 'na@example.org') !== 'na@example.org',
];
$matrix[] = [
'required' => 'required',
'descr' => Translate::noop('The auth.adminpassword configuration option must be set'),
'enabled' => $this->config->getString('auth.adminpassword', '123') !== '123',
];
return $matrix;
}
/**
* Compile a list of warnings about the current deployment.
*
* The returned array can contain either strings that can be translated directly, or arrays. If an element is an
* array, the first value in that array is a string that can be translated, and the second value will be a hashed
* array that contains the substitutions that must be applied to the translation, with its corresponding value. This
* can be used in twig like this, assuming an element called "e":
*
* {{ e[0]|trans(e[1])|raw }}
*
* @return array
*/
protected function getWarnings()
{
$warnings = [];
// make sure we're using HTTPS
if (!\SimpleSAML\Utils\HTTP::isHTTPS()) {
$warnings[] = Translate::noop(
'<strong>You are not using HTTPS</strong> to protect communications with your users. HTTP works fine '.
'for testing purposes, but in a production environment you should use HTTPS. <a '.
'href="https://simplesamlphp.org/docs/stable/simplesamlphp-maintenance">Read more about the '.
'maintenance of SimpleSAMLphp</a>.'
);
}
// make sure we have a secret salt set
if ($this->config->getValue('secretsalt') === 'defaultsecretsalt') {
$warnings[] = Translate::noop(
'<strong>The configuration uses the default secret salt</strong>. Make sure to modify the <code>'.
'secretsalt</code> option in the SimpleSAMLphp configuration in production environments. <a '.
'href="https://simplesamlphp.org/docs/stable/simplesamlphp-install">Read more about the '.
'maintenance of SimpleSAMLphp</a>.'
);
}
// check for URL limitations
if (extension_loaded('suhosin')) {
$len = ini_get('suhosin.get.max_value_length');
if (empty($len) || $len < 2048) {
$warnings[] = Translate::noop(
'The length of query parameters is limited by the PHP Suhosin extension. Please increase the '.
'<code>suhosin.get.max_value_length</code> option in your php.ini to at least 2048 bytes.'
);
}
}
/*
* Check for updates. Store the remote result in the session so we don't need to fetch it on every access to
* this page.
*/
if ($this->config->getBoolean('admin.checkforupdates', true) && $this->config->getVersion() !== 'master') {
if (!function_exists('curl_init')) {
$warnings[] = Translate::noop(
'The cURL PHP extension is missing. Cannot check for SimpleSAMLphp updates.'
);
} else {
$latest = $this->session->getData(self::LATEST_VERSION_STATE_KEY, "version");
if (!$latest) {
$ch = curl_init(self::RELEASES_API);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, 'SimpleSAMLphp');
curl_setopt($ch, CURLOPT_TIMEOUT, 2);
curl_setopt($ch, CURLOPT_PROXY, $this->config->getString('proxy', null));
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->config->getValue('proxy.auth', null));
$response = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
$latest = json_decode($response, true);
$this->session->setData(self::LATEST_VERSION_STATE_KEY, 'version', $latest);
}
curl_close($ch);
}
if ($latest && version_compare($this->config->getVersion(), ltrim($latest['tag_name'], 'v'), 'lt')) {
$warnings[] = [
Translate::noop(
'You are running an outdated version of SimpleSAMLphp. Please update to <a href="'.
'%latest%">the latest version</a> as soon as possible.'
),
[
'%latest%' => $latest['html_url']
]
];
}
}
}
return $warnings;
}
}