From 790016bd966b70b2835a711ec6c46c3eb30c6872 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Tue, 10 Jun 2008 09:32:17 +0000
Subject: [PATCH] Added configuration checker.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@624 44740490-163a-0410-bde0-09ae8108e29a
---
 dictionaries/errors.php            |   8 +
 dictionaries/frontpage.php         |   4 +
 templates/default/admin-config.php |  87 ++++++++++
 www/admin/config.php               | 269 +++++++++++++++++++++++++++++
 www/index.php                      |   5 +
 5 files changed, 373 insertions(+)
 create mode 100644 templates/default/admin-config.php
 create mode 100644 www/admin/config.php

diff --git a/dictionaries/errors.php b/dictionaries/errors.php
index b9e9437f2..d31a6067c 100644
--- a/dictionaries/errors.php
+++ b/dictionaries/errors.php
@@ -828,6 +828,14 @@ $lang = array(
 		'hr' => 'Izvorna vrijednost administratorske zaporke (auth.adminpassword) u konfiguraciji nije promjenjena, molimo promjenite zaporku.',
 		'hu' => 'Még nem lett megváltoztatva a karbantartói jelszó (auth.adminpassword) a konfigurációs fájlban, kérjük, változtassa meg most! ',
 	),
+	'title_READCONFIGTEMPLATES' => array (
+		'en' => 'Error reading config-templates directory',
+		'no' => 'Feil ved lesing av config-templates katalogen',
+	),
+	'descr_READCONFIGTEMPLATES' => array (
+		'en' => 'An error occured while simpleSAMLphp was attempting to find the available files in the config-templates directory.',
+		'no' => 'En feil oppstod mens simpleSAMLphp prøvde å finne tilgjengelige filer i config-templates katalogen.',
+	),
 
 );
 
diff --git a/dictionaries/frontpage.php b/dictionaries/frontpage.php
index 7244b00f6..e9a08262f 100644
--- a/dictionaries/frontpage.php
+++ b/dictionaries/frontpage.php
@@ -481,6 +481,10 @@ $lang = array(
 		'hr' => 'OdrĹľavanje i konfiguriranje simpleSAMLphp-a',
 		'hu' => 'SimpleSAMLphp karbantartása és beállítása',
 	),
+	'link_configcheck' => array (
+		'no' => 'simpleSAMLphp konfigurasjonskontroll',
+		'en' => 'simpleSAMLphp configuraction check',
+	),
 
 );
 
diff --git a/templates/default/admin-config.php b/templates/default/admin-config.php
new file mode 100644
index 000000000..183a2d201
--- /dev/null
+++ b/templates/default/admin-config.php
@@ -0,0 +1,87 @@
+<?php
+
+$this->data['header'] = 'Configuration check';
+
+if(array_key_exists('file', $this->data)) {
+	$this->data['header'] .= ' - ' . htmlspecialchars($this->data['file']);
+}
+
+$this->includeAtTemplateBase('includes/header.php');
+
+?>
+
+<div id="content">
+<h2><?php echo $this->data['header']; ?></h2>
+
+<?php
+if(array_key_exists('files', $this->data)) {
+	/* File list. */
+	echo('<p>Select configuration file to check:</p>');
+
+	echo('<ul>');
+	foreach($this->data['files'] as $file) {
+		$fileName = htmlspecialchars($file['name']);
+		if($file['available']) {
+			$fileUrl = htmlspecialchars($this->data['url'] . '?file=' . urlencode($file['name']));
+			echo('<li><a href="' . $fileUrl . '">' . $fileName . '</a></li>');
+		} else {
+			$reason = htmlspecialchars($file['reason']);
+			echo('<li>' . $fileName . ' - ' . $reason . '</li>');
+		}
+	}
+	echo('</ul>');
+
+} else {
+	/* File details. */
+
+	$notices = $this->data['notices'];
+	$missing = $this->data['missing'];
+	$superfluous = $this->data['superfluous'];
+
+	if(count($notices) > 0) {
+		echo('<h3>Notices</h3>');
+		echo('<ul>');
+		foreach($notices as $i) {
+			$type = $i['type'];
+			if($type === 'error') {
+				$image = 'bomb.png';
+			} elseif($type === 'warning') {
+				$image = 'caution.png';
+			}
+			$imageUrl = '/' . $this->data['baseurlpath'] . 'resources/icons/' . $image;
+
+			echo('<p>');
+			echo('<img style="display: inline; float: left; width: 1.7em; height: 1.7em;" src="' . htmlspecialchars($imageUrl) . '" alt="' . htmlspecialchars($type) . '" />');
+			echo(htmlspecialchars($i['message']));
+			echo('</p>');
+		}
+		echo('</ul>');
+	}
+
+	if(count($missing) > 0) {
+		echo('<h3>Options missing from config file</h3>');
+		echo('<ul>');
+		foreach($missing as $i) {
+			echo('<li>' . htmlspecialchars($i) . '</li>');
+		}
+		echo('</ul>');
+	}
+
+	if(count($superfluous) > 0) {
+		echo('<h3>Superfluous options in config file</h3>');
+		echo('<ul>');
+		foreach($superfluous as $i) {
+			echo('<li>' . htmlspecialchars($i) . '</li>');
+		}
+		echo('</ul>');
+	}
+
+	if(count($notices) === 0 && count($missing) === 0 && count($superfluous) === 0) {
+		echo('<p>No errors found.</p>');
+	}
+
+	echo('<p><a href="' . htmlspecialchars($this->data['url']) . '">Go back to the file list</a></p>');
+}
+
+$this->includeAtTemplateBase('includes/footer.php');
+?>
diff --git a/www/admin/config.php b/www/admin/config.php
new file mode 100644
index 000000000..94af3fbf2
--- /dev/null
+++ b/www/admin/config.php
@@ -0,0 +1,269 @@
+<?php
+
+require_once('../_include.php');
+
+
+/* A list of notices which will be shown to the user - only used during file verification. */
+$notices = array();
+
+
+/**
+ * This function adds a warning to the list of notices.
+ *
+ * @param $message  The warning message.
+ */
+function warning($message) {
+	global $notices;
+
+	$notices[] = array(
+		'type' => 'warning',
+		'message' => $message,
+		);
+}
+
+
+/**
+ * This function adds an error to the list of notices.
+ *
+ * @param $message  The error message.
+ */
+function error($message) {
+	global $notices;
+
+	$notices[] = array(
+		'type' => 'error',
+		'message' => $message,
+		);
+}
+
+
+
+/**
+ * Load a config file
+ *
+ * This function loads a configuration file. If the file isn't a standard configuration file
+ * it will return NULL. Parse errors will result in FALSE being returned.
+ *
+ * @param $file  Full path to the configuration file.
+ * @return  The array with the configuration values if successfull. NULL if $file isn't a standard
+ *          configuration file. FALSE if there was an parse error.
+ */
+function loadConfigFile($file) {
+	assert('file_exists($file)');
+
+	/* Cache of loaded configuration files - to avoid loading and parsing the same file twice. */
+	static $cache = array();
+
+	/* Check for file in cache. */
+	if(array_key_exists($file, $cache)) {
+		return $cache[$file];
+	}
+
+	/* Load the file. */
+	$data = file_get_contents($file);
+
+	/* Set $config to a known value. This is used to detect whether $file updates the $config variable. */
+	$config = FALSE;
+
+	/* Strip out the php start and end tags. */
+	$matches = array();
+	if(!preg_match('/\s*<\?php(.*)\?>\s*/s', $data, $matches)) {
+		/* File doesn't start with <?php and end with ?>. */
+		return FALSE;
+	}
+	$data = $matches[1];
+
+	/* Process the file. */
+	$res = eval($data);
+
+	if($res === FALSE) {
+		/* Parse error in file. */
+		return FALSE;
+	}
+
+	if($config === FALSE) {
+		/* $config not updated - this is not a standard config file. */
+		$cache[$file] = NULL;
+		return NULL;
+	}
+
+	$cache[$file] = $config;
+	return $config;
+}
+
+
+/**
+ * Determine whether the specified configuration file can be checked.
+ *
+ * @return TRUE if it can be checked, of a string with the reason if it can't be checked.
+ */
+function canCheckFile($file) {
+	global $configFiles;
+	global $configDir;
+	global $configTemplateDir;
+
+	if(!in_array($file, $configFiles)) {
+		return 'not in the list of available config files';
+	}
+
+	if(!file_exists($configDir . $file)) {
+		return 'not added to config directory';
+	}
+
+	$configTemplateData = loadConfigFile($configTemplateDir . $file);
+	if($configTemplateData === FALSE) {
+		return 'parse error in template file';
+	}
+	if($configTemplateData === NULL) {
+		return 'not a standard configuration file';
+	}
+
+	$configData = loadConfigFile($configDir . $file);
+	if($configData === FALSE) {
+		return 'parse error in configuration file';
+	}
+	if($configData === NULL) {
+		return 'invalid configuration file - $config not defined in file';
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * Does some checks on config.php
+ *
+ * @param $config  The configuration data.
+ */
+function validate_config($config, &$errors, &$warnings) {
+
+	if($config['auth.adminpassword'] === '123') {
+		error('auth.adminpassword should be changed from the default value.');
+	}
+
+	if($config['technicalcontact_email'] === 'na') {
+		warning('technicalcontact_email should be set to a email address users can contact for support.');
+	}
+
+}
+
+/* Load configuration and session information. */
+$config = SimpleSAML_Configuration::getInstance();
+$session = SimpleSAML_Session::getInstance();
+
+/* Check if the user is logged in with admin access. */
+if (!$session->isValid('login-admin') ) {
+	SimpleSAML_Utilities::redirect('/' . $config->getBaseURL() . 'auth/login-admin.php',
+		array('RelayState' => SimpleSAML_Utilities::selfURL())
+	);
+}
+
+/* Find config directories. */
+$configDir = $config->getBaseDir() . 'config/';
+$configTemplateDir = $config->getBaseDir() . 'config-templates/';
+
+/* Find all available configuration files. */
+
+$configFiles = array();
+
+$dirHandle = opendir($configTemplateDir);
+if($dirHandle === FALSE) {
+	SimpleSAML_Utilities::fatalError($session->getTrackId(), 'READCONFIGTEMPLATES');
+}
+while(($configFile = readdir($dirHandle)) !== FALSE) {
+	/* We are only interrested in .php-files in the directory. */
+	if(substr($configFile, -4) !== '.php') {
+		continue;
+	}
+
+	$configFiles[] = $configFile;
+}
+
+closedir($dirHandle);
+
+if(array_key_exists('file', $_GET)) {
+	/* The user has selected a file. */
+	$file = $_GET['file'];
+
+	/* Can we check this file? */
+	if(canCheckFile($file) !== TRUE) {
+		$file = NULL;
+	}
+} else {
+	/* The user needs to select a file. */
+	$file = NULL;
+}
+
+
+/* Initialize template page. */
+$et = new SimpleSAML_XHTML_Template($config, 'admin-config.php');
+$et->data['url'] = SimpleSAML_Utilities::selfURLNoQuery();
+
+if($file === NULL) {
+	/* No file selected by user - find and show list of available config files. */
+	$files = array();
+	foreach($configFiles as $configFile) {
+		$canCheck = canCheckFile($configFile);
+		if($canCheck === TRUE) {
+			$available = TRUE;
+			$reason = '';
+		} else {
+			$available = FALSE;
+			$reason = $canCheck;
+		}
+		$files[] = array(
+			'name' => $configFile,
+			'available' => $available,
+			'reason' => $reason,
+			);
+	}
+
+	/* Show page. */
+	$et->data['files'] = $files;
+	$et->show();
+
+	/* We are done - exit. */
+	exit();
+}
+
+/* User has selected a configuration file. */
+$et->data['file'] = $file;
+
+/* If the code reaches this point, parse errors and such in $file should be handled. */
+
+/* Load the configuration. */
+$templateData = loadConfigFile($configTemplateDir . $file);
+$configData = loadConfigFile($configDir . $file);
+
+
+/* Check if we have a validation function for this config file, and use it if we do. */
+$funcName = 'validate_' . substr($file, 0, -4);
+if(is_callable($funcName)) {
+	call_user_func($funcName, $configData);
+}
+$et->data['notices'] = $notices;
+
+
+/* Find keys in the template file which are missing in the config file. */
+$missing = array();
+foreach($templateData as $key => $value) {
+	if(!array_key_exists($key, $configData)) {
+		$missing[] = $key;
+	}
+}
+$et->data['missing'] = $missing;
+
+
+/* Find keys in the config file which are missing in the template file. */
+$superfluous = array();
+foreach($configData as $key => $value) {
+	if(!array_key_exists($key, $templateData)) {
+		$superfluous[] = $key;
+	}
+}
+$et->data['superfluous'] = $superfluous;
+
+
+$et->show();
+
+?>
\ No newline at end of file
diff --git a/www/index.php b/www/index.php
index bc2812b18..ea431fcbd 100644
--- a/www/index.php
+++ b/www/index.php
@@ -48,6 +48,11 @@ $links[] = array(
 	'href' => 'admin/phpinfo.php', 
 	'text' => 'link_phpinfo');
 
+$links[] = array(
+	'href' => 'admin/config.php',
+	'text' => 'link_configcheck',
+	);
+
 
 
 $linksmeta = array();
-- 
GitLab