issue #11: adding login module, adding user authentication to user

module

issue #11: adding user authentication to installer
This commit is contained in:
Pepijn Over 2014-03-14 15:24:03 +01:00
parent f8f3b38708
commit 26f8fedffe
27 changed files with 2168 additions and 413 deletions

View File

@ -4,6 +4,7 @@
# ?, 2014
#
#########################
- User login system
- Switched from mysql_* to PDO.
- Added SMTP support.
- Updated PHPMailer package to v5.2.6.

View File

@ -98,15 +98,6 @@ The config file has actually been renamed since 2.0, but if you keep it there wh
* Remove the old config.inc.php file
## Security
By default the PHP Server Monitor does not (yet) come with any security methods. After uploading these files to
your public html folder these will be visible to everyone on the web. It is recommended to put a password
on this folder to prevent other people from playing with your tool. An example .htaccess login script can
be found in the example/ dir. To create your own username and password for the .htpasswd file, see
<http://www.htaccesstools.com/htpasswd-generator/>
## Setting up a cronjob
In order to keep the server monitor up to date, the monitor.php file has to run regularly.

View File

@ -29,13 +29,21 @@ require 'src/bootstrap.php';
psm_no_cache();
if(isset($_GET['action']) && $_GET['action'] == 'check') {
require 'cron/status.cron.php';
header('Location: index.php');
$type = (!isset($_GET['type'])) ? 'servers' : $_GET['type'];
// if user is not logged in, load login module
$user = new \psm\Service\User($db);
if(!$user->isUserLoggedIn()) {
$type = 'login';
}
$type = (!isset($_GET['type'])) ? 'servers' : $_GET['type'];
$allowed_types = array('servers', 'users', 'log', 'config', 'status');
if($type == 'update') {
require 'cron/status.cron.php';
header('Location: ' . psm_build_url());
die();
}
$allowed_types = array('servers', 'users', 'log', 'config', 'status', 'login');
// make sure user selected a valid type. if so, include the file and add to template
if(!in_array($type, $allowed_types)) {
@ -44,7 +52,9 @@ if(!in_array($type, $allowed_types)) {
$tpl = new \psm\Service\Template();
eval('$mod = new psm\Module\\'.ucfirst($type).'($db, $tpl);');
if($user->getUserLevel() > $mod->getMinUserLevelRequired()) {
die('You do not have the privileges to view this page.');
}
$mod->setUser($user);
// let the module prepare it's HTML code
$mod->initialize();
?>

View File

@ -29,11 +29,11 @@ define('PSM_INSTALL', true);
require 'src/bootstrap.php';
psm_no_cache();
$type = 'install';
$tpl = new \psm\Service\Template();
$user = new \psm\Service\User($db);
$mod = new psm\Module\Install($db, $tpl);
$mod->setUser($user);
$mod->initialize();
?>

View File

@ -21,12 +21,11 @@
* @author Pepijn Over <pep@neanderthal-technology.com>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: phpservermon 2.1.0
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.1.0
**/
define('PSM_VERSION', '2.2.0-dev');
// Include paths
define('PSM_PATH_SRC', dirname(__FILE__) . DIRECTORY_SEPARATOR);
define('PSM_PATH_VENDOR', PSM_PATH_SRC . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR);
@ -34,6 +33,11 @@ define('PSM_PATH_INC', PSM_PATH_SRC . 'includes' . DIRECTORY_SEPARATOR);
define('PSM_PATH_TPL', PSM_PATH_SRC . 'templates' . DIRECTORY_SEPARATOR);
define('PSM_PATH_LANG', PSM_PATH_SRC . 'lang' . DIRECTORY_SEPARATOR);
// user levels
define('PSM_USER_ADMIN', 10);
define('PSM_USER_USER', 20);
define('PSM_USER_ANONYMOUS', 30);
// find config file
$path_conf = PSM_PATH_SRC . '../config.php';
if(file_exists($path_conf)) {

View File

@ -369,6 +369,34 @@ function psm_build_url($params = array(), $urlencode = true) {
return $url;
}
/**
* Try existence of a GET var, if not return the alternative
* @param string $key
* @param string $alt
* @return mixed
*/
function psm_GET($key, $alt = null) {
if(isset($_GET[$key])) {
return $_GET[$key];
} else {
return $alt;
}
}
/**
* Try existence of a GET var, if not return the alternative
* @param string $key
* @param string $alt
* @return mixed
*/
function psm_POST($key, $alt = null) {
if(isset($_POST[$key])) {
return $_POST[$key];
} else {
return $alt;
}
}
###############################################
#
# Debug functions
@ -396,5 +424,3 @@ function psm_no_cache() {
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
}
?>

View File

@ -0,0 +1,246 @@
<?php
/**
* PHP Server Monitor
* Monitor your servers and websites.
*
* This file is part of PHP Server Monitor.
* PHP Server Monitor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PHP Server Monitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PHP Server Monitor. If not, see <http://www.gnu.org/licenses/>.
*
* @package phpservermon
* @author Anthony Ferrara <ircmaxell@php.net>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.2.0
**/
/**
* A Compatibility library with PHP 5.5's simplified password hashing API.
*
* @author Anthony Ferrara <ircmaxell@php.net>
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @copyright 2012 The Authors
*/
if (!defined('PASSWORD_DEFAULT')) {
define('PASSWORD_BCRYPT', 1);
define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
/**
* Hash the password using the specified algorithm
*
* @param string $password The password to hash
* @param int $algo The algorithm to use (Defined by PASSWORD_* constants)
* @param array $options The options for the algorithm to use
*
* @return string|false The hashed password, or false on error.
*/
function password_hash($password, $algo, array $options = array()) {
if (!function_exists('crypt')) {
trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
return null;
}
if (!is_string($password)) {
trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
return null;
}
if (!is_int($algo)) {
trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
return null;
}
switch ($algo) {
case PASSWORD_BCRYPT:
// Note that this is a C constant, but not exposed to PHP, so we don't define it here.
$cost = 10;
if (isset($options['cost'])) {
$cost = $options['cost'];
if ($cost < 4 || $cost > 31) {
trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
return null;
}
}
// The length of salt to generate
$raw_salt_len = 16;
// The length required in the final serialization
$required_salt_len = 22;
$hash_format = sprintf("$2y$%02d$", $cost);
break;
default:
trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
return null;
}
if (isset($options['salt'])) {
switch (gettype($options['salt'])) {
case 'NULL':
case 'boolean':
case 'integer':
case 'double':
case 'string':
$salt = (string) $options['salt'];
break;
case 'object':
if (method_exists($options['salt'], '__tostring')) {
$salt = (string) $options['salt'];
break;
}
case 'array':
case 'resource':
default:
trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
return null;
}
if (strlen($salt) < $required_salt_len) {
trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
return null;
} elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
$salt = str_replace('+', '.', base64_encode($salt));
}
} else {
$buffer = '';
$buffer_valid = false;
if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
$buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
if ($buffer) {
$buffer_valid = true;
}
}
if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
$buffer = openssl_random_pseudo_bytes($raw_salt_len);
if ($buffer) {
$buffer_valid = true;
}
}
if (!$buffer_valid && is_readable('/dev/urandom')) {
$f = fopen('/dev/urandom', 'r');
$read = strlen($buffer);
while ($read < $raw_salt_len) {
$buffer .= fread($f, $raw_salt_len - $read);
$read = strlen($buffer);
}
fclose($f);
if ($read >= $raw_salt_len) {
$buffer_valid = true;
}
}
if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
$bl = strlen($buffer);
for ($i = 0; $i < $raw_salt_len; $i++) {
if ($i < $bl) {
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
} else {
$buffer .= chr(mt_rand(0, 255));
}
}
}
$salt = str_replace('+', '.', base64_encode($buffer));
}
$salt = substr($salt, 0, $required_salt_len);
$hash = $hash_format . $salt;
$ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) <= 13) {
return false;
}
return $ret;
}
/**
* Get information about the password hash. Returns an array of the information
* that was used to generate the password hash.
*
* array(
* 'algo' => 1,
* 'algoName' => 'bcrypt',
* 'options' => array(
* 'cost' => 10,
* ),
* )
*
* @param string $hash The password hash to extract info from
*
* @return array The array of information about the hash.
*/
function password_get_info($hash) {
$return = array(
'algo' => 0,
'algoName' => 'unknown',
'options' => array(),
);
if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
$return['algo'] = PASSWORD_BCRYPT;
$return['algoName'] = 'bcrypt';
list($cost) = sscanf($hash, "$2y$%d$");
$return['options']['cost'] = $cost;
}
return $return;
}
/**
* Determine if the password hash needs to be rehashed according to the options provided
*
* If the answer is true, after validating the password using password_verify, rehash it.
*
* @param string $hash The hash to test
* @param int $algo The algorithm used for new password hashes
* @param array $options The options array passed to password_hash
*
* @return boolean True if the password needs to be rehashed.
*/
function password_needs_rehash($hash, $algo, array $options = array()) {
$info = password_get_info($hash);
if ($info['algo'] != $algo) {
return true;
}
switch ($algo) {
case PASSWORD_BCRYPT:
$cost = isset($options['cost']) ? $options['cost'] : 10;
if ($cost != $info['options']['cost']) {
return true;
}
break;
}
return false;
}
/**
* Verify a password against a hash using a timing attack resistant approach
*
* @param string $password The password to verify
* @param string $hash The hash to verify against
*
* @return boolean If the password matches the hash
*/
function password_verify($password, $hash) {
if (!function_exists('crypt')) {
trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
return false;
}
$ret = crypt($password, $hash);
if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
return false;
}
$status = 0;
for ($i = 0; $i < strlen($ret); $i++) {
$status |= (ord($ret[$i]) ^ ord($hash[$i]));
}
return $status === 0;
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* PHP Server Monitor
* Monitor your servers and websites.
*
* This file is part of PHP Server Monitor.
* PHP Server Monitor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PHP Server Monitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PHP Server Monitor. If not, see <http://www.gnu.org/licenses/>.
*
* @package phpservermon
* @author Pepijn Over <pep@neanderthal-technology.com>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.2.0
**/
/**
* Current PSM version
*/
define('PSM_VERSION', '2.2.0-dev');
/**
* Configuration for: Hashing strength
* This is the place where you define the strength of your password hashing/salting
*
* To make password encryption very safe and future-proof, the PHP 5.5 hashing/salting functions
* come with a clever so called COST FACTOR. This number defines the base-2 logarithm of the rounds of hashing,
* something like 2^12 if your cost factor is 12. By the way, 2^12 would be 4096 rounds of hashing, doubling the
* round with each increase of the cost factor and therefore doubling the CPU power it needs.
* Currently, in 2013, the developers of this functions have chosen a cost factor of 10, which fits most standard
* server setups. When time goes by and server power becomes much more powerful, it might be useful to increase
* the cost factor, to make the password hashing one step more secure. Have a look here
* (@see https://github.com/panique/php-login/wiki/Which-hashing-&-salting-algorithm-should-be-used-%3F)
* in the BLOWFISH benchmark table to get an idea how this factor behaves. For most people this is irrelevant,
* but after some years this might be very very useful to keep the encryption of your database up to date.
*
* Remember: Every time a user registers or tries to log in (!) this calculation will be done.
* Don't change this if you don't know what you do.
*
* To get more information about the best cost factor please have a look here
* @see http://stackoverflow.com/q/4443476/1114320
*
* This constant will be used in the login and the registration class.
*/
define("PSM_LOGIN_HASH_COST_FACTOR", "10");
/**
* Configuration for: Cookies
* Please note: The COOKIE_DOMAIN needs the domain where your app is,
* in a format like this: .mydomain.com
* Note the . in front of the domain. No www, no http, no slash here!
* For local development .127.0.0.1 or .localhost is fine, but when deploying you should
* change this to your real domain, like '.mydomain.com' ! The leading dot makes the cookie available for
* sub-domains too.
* @see http://stackoverflow.com/q/9618217/1114320
* @see http://www.php.net/manual/en/function.setcookie.php
*
* COOKIE_RUNTIME: How long should a cookie be valid ? 1209600 seconds = 2 weeks
* COOKIE_DOMAIN: The domain where the cookie is valid for, like '.mydomain.com'
* COOKIE_SECRET_KEY: Put a random value here to make your app more secure. When changed, all cookies are reset.
*/
define("PSM_LOGIN_COOKIE_RUNTIME", 1209600);
define("PSM_LOGIN_COOKIE_DOMAIN", null);
define("PSM_LOGIN_COOKIE_SECRET_KEY", "4w900de52e3ap7y77y8675jy6c594286");
/**
* Number of seconds the reset link is valid after sending it to the user.
*/
define('PSM_LOGIN_RESET_RUNTIME', 3600);

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Добави нов',
'update_available' => 'Налична е нова версия. Може да я свалите от <a href="http://phpservermon.sourceforge.net" target="_blank">тук</a>.',
'back_to_top' => 'Нагоре',
'go_back' => 'Go back',
),
'users' => array(
'user' => 'Потребител',
'name' => 'Име',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Мобилен телефон',
'email' => 'Имейл',
'updated' => 'Информацията за потребителя е обновена.',
'inserted' => 'Потребителят е добавен.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Записи в лога',
@ -149,6 +166,27 @@ $sm_lang = array(
'on_email_subject' => 'Връзката до \'%LABEL%\' е ВЪЗСТАНОВЕНА',
'on_email_body' => "Връзката до '%LABEL%' беше ВЪЗСТАНОВЕНА:<br/><br/>Сървър: %LABEL%<br/>IP адрес: %IP%<br/>Порт: %PORT%<br/>Днес: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Adicionar novo?',
'update_available' => 'Uma atualização disponível em <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Voltar ao topo',
'go_back' => 'Go back',
),
'users' => array(
'user' => 'usuário',
'name' => 'Nome',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Celular',
'email' => 'Email',
'updated' => 'Usuário atualizado.',
'inserted' => 'Usuário adicionado.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Entradas do Log',
@ -149,6 +166,27 @@ $sm_lang = array(
'on_email_subject' => 'IMPORTANTE: Servidor \'%LABEL%\' esta ONLINE',
'on_email_body' => "Servidor '%LABEL%' esta ONLINE novamente:<br/><br/>Servidor: %LABEL%<br/>IP: %IP%<br/>Porta: %PORT%<br/>Data: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Neuen Eintrag erstellen?',
'update_available' => 'Ein neues Update ist verf&uuml;gbar auf <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Back to top',
'go_back' => 'Go back',
),
'users' => array(
'user' => 'Benutzer',
'name' => 'Name',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Mobil',
'email' => 'Email',
'updated' => 'Benutzer bearbeitet.',
'inserted' => 'Benutzer eingetragen.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Log Eintr&auml;ge',
@ -149,6 +166,27 @@ $sm_lang = array(
'on_email_subject' => 'Wichtig: Server \'%LABEL%\' ist wieder Online',
'on_email_body' => "Server '%LABEL%' l&auml;uft wieder:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Datum: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Add new?',
'update_available' => 'A new update is available from <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Back to top',
'go_back' => 'Go back',
),
'users' => array(
'user' => 'user',
'name' => 'Name',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Mobile',
'email' => 'Email',
'updated' => 'User updated.',
'inserted' => 'User added.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Log entries',
@ -149,6 +166,27 @@ $sm_lang = array(
'on_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is RUNNING',
'on_email_body' => "Server '%LABEL%' is running again:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Date: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Rajouter un nouveau serveur?',
'update_available' => 'Une nouvelle version est disponible &agrave; l adresse <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Haut de page',
'go_back' => 'Go back',
),
'users' => array(
'user' => 'Utilisateur',
'name' => 'Nom',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Mobile',
'email' => 'Email',
'updated' => 'Utilisateur mis &agrave; jour.',
'inserted' => 'Utilisateur ajout&eacute;.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Evenements',
@ -122,7 +139,6 @@ $sm_lang = array(
'Vous recevrez une seule notification &agrave; 1 heure du matin et uniquement celle-ci.<br/>'.
'<br/><b>Toujours: </b>'.
'Vous recevrez une notification &agrave; chaque passage de la tache planifi&eacute;e si le site est en statut ETEINT ',
'alert_type_status' => 'Changement de statut',
'alert_type_offline' => 'Eteint',
'alert_type_always' => 'Toujours',
@ -149,6 +165,27 @@ $sm_lang = array(
'on_email_subject' => 'IMPORTANT: Le Serveur \'%LABEL%\' est OK',
'on_email_body' => "Le Serveur '%LABEL%' est de nouveau OK:<br/><br/>Serveur: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Date: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -23,7 +23,6 @@
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.1
**/
$sm_lang = array(
@ -51,14 +50,31 @@ $sm_lang = array(
'add_new' => '새계정 추가',
'update_available' => '새로운 업데이트가 있습니다. 다음사이트를 방문 해 주십시오. <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Back to top',
'go_back' => 'Go back',
),
'users' => array(
'user' => '사용자',
'name' => '이름',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => '휴대폰',
'email' => 'Email',
'updated' => '수정되었습니다.',
'inserted' => '추가되었습니다.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Log entries',
@ -148,6 +164,27 @@ $sm_lang = array(
'on_email_subject' => '중요: 서버(\'%LABEL%\')가 가동중입니다.',
'on_email_body' => "서버('%LABEL%')가 재가동됩니다.:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Port: %PORT%<br/>Date: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -50,14 +50,31 @@ $sm_lang = array(
'add_new' => 'Voeg toe?',
'update_available' => 'Een nieuwe update is beschikbaar op <a href="http://phpservermon.sourceforge.net" target="_blank">http://phpservermon.sourceforge.net</a>.',
'back_to_top' => 'Terug naar boven',
'go_back' => 'Terug',
),
'users' => array(
'user' => 'gebruiker',
'name' => 'Naam',
'user_name' => 'Username',
'password' => 'Password',
'password_repeat' => 'Password repeat',
'password_leave_blank' => 'Leave blank to keep unchanged',
'level' => 'Level',
'level_10' => 'Administrator',
'level_20' => 'User',
'mobile' => 'Mobiel',
'email' => 'Email',
'updated' => 'Gebruiker gewijzigd.',
'inserted' => 'Gebruiker toegevoegd.',
'error_user_name_bad_length' => 'Usernames must be between 2 and 64 characters.',
'error_user_name_invalid' => 'It may only contain alphabetic characters (a-z, A-Z), digits (0-9) and underscores (_).',
'error_user_name_exists' => 'The given username already exists in the database.',
'error_user_email_bad_length' => 'Email addresses must be between 5 and 255 characters.',
'error_user_email_invalid' => 'The email address is invalid.',
'error_user_level_invalid' => 'The given user level is invalid.',
'error_user_no_match' => 'The user could not be found in the database.',
'error_user_password_invalid' => 'The entered password is invalid.',
'error_user_password_no_match' => 'The entered passwords do not match.',
),
'log' => array(
'title' => 'Log entries',
@ -148,6 +165,27 @@ $sm_lang = array(
'on_email_subject' => 'BELANGRIJK: Server %LABEL% is RUNNING',
'on_email_body' => "Server %LABEL% is weer online:<br/><br/>Server: %LABEL%<br/>IP: %IP%<br/>Poort: %PORT%<br/>Datum: %DATE%",
),
'login' => array(
'welcome_usermenu' => 'Welcome, %user_name%',
'title_sign_in' => 'Please sign in',
'title_forgot' => 'Forgot your password?',
'title_reset' => 'Reset your password',
'submit' => 'Submit',
'remember_me' => 'Remember me',
'login' => 'Login',
'logout' => 'Logout',
'username' => 'Username',
'password' => 'Password',
'password_repeat' => 'Repeat password',
'password_forgot' => 'Forgot password?',
'password_reset' => 'Reset password',
'password_reset_email_subject' => 'Reset your password for PHP Server Monitor',
'password_reset_email_body' => 'Please use the following link to reset your password. Please note it expires in 1 hour.<br/><br/>%link%',
'error_user_incorrect' => 'The provided username could not be found.',
'error_login_incorrect' => 'The information is incorrect.',
'error_login_passwords_nomatch' => 'The provided passwords do not match.',
'error_reset_invalid_link' => 'The reset link you provided is invalid.',
'success_password_forgot' => 'An email has been sent to you with information how to reset your password.',
'success_password_reset' => 'Your password has been reset successfully. Please login.',
),
);
?>

View File

@ -76,7 +76,7 @@ abstract class AbstractModule implements ModuleInterface {
/**
* Messages to show the user
* @var array $messages
* @see getMessage()
* @see addMessage()
*/
protected $messages = array();
@ -99,6 +99,19 @@ abstract class AbstractModule implements ModuleInterface {
*/
protected $tpl_id;
/**
* User service
* @var \psm\Service\User $user
*/
protected $user;
/**
* Required user level for this module
* @var int $user_level_required
* @see setMinUserLevelRequired()
*/
protected $user_level_required = PSM_USER_USER;
function __construct(Database $db, Template $tpl) {
$this->db = $db;
$this->tpl = $tpl;
@ -153,13 +166,14 @@ abstract class AbstractModule implements ModuleInterface {
// user wants updates, lets see what we can do
$this->createHTMLUpdateAvailable();
}
$tpl_data = array(
'message' => (empty($this->messages)) ? '&nbsp' : implode('<br/>', $this->messages),
);
$tpl_data = array();
if(!empty($this->messages)) {
$this->tpl->addTemplateDataRepeat('main', 'messages', $this->messages);
}
// add menu to page?
if($this->add_menu) {
$this->tpl->newTemplate('main_menu', 'main.tpl.html');
$tpl_data['html_menu'] = $this->tpl->getTemplate('main_menu');
$tpl_data['html_menu'] = $this->createHTMLMenu();
}
// add footer to page?
if($this->add_footer) {
@ -181,15 +195,69 @@ abstract class AbstractModule implements ModuleInterface {
echo $this->tpl->display($this->getTemplateId());
}
/**
* Create HTML code for the menu
* @return string
*/
protected function createHTMLMenu() {
// @todo globals..? seriously..?
global $type;
$ulvl = ($this->user) ? $this->user->getUserLevel() : PSM_USER_ANONYMOUS;
$tpl_id = 'main_menu';
$this->tpl->newTemplate($tpl_id, 'main.tpl.html');
$tpl_data = array(
'label_help' => psm_get_lang('system', 'help'),
'label_logout' => psm_get_lang('login', 'logout'),
'url_logout' => psm_build_url(array('logout' => 1)),
);
switch($ulvl) {
case PSM_USER_ADMIN:
$items = array('servers', 'users', 'log', 'status', 'config', 'update');
break;
case PSM_USER_USER:
$items = array('servers', 'log', 'status', 'update');
break;
default:
$items = array();
break;
}
$menu = array();
foreach($items as $key) {
$menu[] = array(
'active' => ($key == $type) ? 'active' : '',
'url' => psm_build_url(array('type' => $key)),
'label' => psm_get_lang('system', $key),
);
}
if(!empty($menu)) {
$this->tpl->addTemplateDataRepeat($tpl_id, 'menu', $menu);
}
if($ulvl != PSM_USER_ANONYMOUS) {
$user = $this->user->getUser();
$tpl_data['label_usermenu'] = str_replace(
'%user_name%',
$user->name,
psm_get_lang('login', 'welcome_usermenu')
);
}
$this->tpl->addTemplateData($tpl_id, $tpl_data);
return $this->tpl->getTemplate($tpl_id);
}
/**
* First check if an update is available, if there is add a message
* to the main template
*/
protected function createHTMLUpdateAvailable() {
// check for updates?
if(psm_check_updates()) {
// yay, new update available =D
// @todo perhaps find a way to make the message more persistent?
$this->addMessage(psm_get_lang('system', 'update_available'));
}
}
@ -207,14 +275,6 @@ abstract class AbstractModule implements ModuleInterface {
array(
'title' => strtoupper(psm_get_lang('system', 'title')),
'subtitle' => psm_get_lang('system', $type),
'active_' . $type => 'active',
'label_servers' => psm_get_lang('system', 'servers'),
'label_users' => psm_get_lang('system', 'users'),
'label_log' => psm_get_lang('system', 'log'),
'label_status' => psm_get_lang('system', 'status'),
'label_config' => psm_get_lang('system', 'config'),
'label_update' => psm_get_lang('system', 'update'),
'label_help' => psm_get_lang('system', 'help'),
'label_back_to_top' => psm_get_lang('system', 'back_to_top'),
)
);
@ -298,15 +358,50 @@ abstract class AbstractModule implements ModuleInterface {
/**
* Add one or multiple message to the stack to be displayed to the user
* @param string|array $msg
* @param string $status success/warning/error
* @return \psm\Module\AbstractModule
*/
public function addMessage($msg) {
public function addMessage($msg, $status = 'info') {
if(!is_array($msg)) {
$msg = array($msg);
}
$this->messages = array_merge($this->messages, $msg);
if($status == 'error') {
$shortcode = 'important';
} else {
$shortcode = $status;
}
foreach($msg as $m) {
$this->messages[] = array(
'message' => $m,
'status' => ($status == null) ? '' : strtoupper($status),
'shortcode' => $shortcode,
);
}
return $this;
}
}
?>
/**
* Set user service
* @param \psm\Service\User $user
*/
public function setUser(\psm\Service\User $user) {
$this->user = $user;
}
/**
* Set the minimum required user level for this module
* @param int $level
*/
public function setMinUserLevelRequired($level) {
$this->user_level_required = intval($level);
}
/**
* Get the minimum required user level for this module
* @return int
*/
public function getMinUserLevelRequired() {
return $this->user_level_required;
}
}

View File

@ -32,13 +32,6 @@ use psm\Service\Template;
class Install extends AbstractModule {
/**
* Result messages to add to the main template
* @var array $install_results
* @see addResult()
*/
protected $install_results = array();
/**
* Full path to config file
* @var string $path_config
@ -54,6 +47,8 @@ class Install extends AbstractModule {
function __construct(Database $db, Template $tpl) {
parent::__construct($db, $tpl);
$this->addMenu(false);
$this->path_config = PSM_PATH_SRC . '../config.php';
$this->path_config_old = PSM_PATH_SRC . '../config.inc.php';
@ -63,18 +58,18 @@ class Install extends AbstractModule {
}
protected function createHTML() {
$tpl_id_custom = $this->getTemplateId();
$this->setTemplateId('install', 'install.tpl.html');
$html_install = ($tpl_id_custom) ? $this->tpl->getTemplate($tpl_id_custom) : '';
$html_results = '';
if(!empty($this->install_results)) {
if(!empty($this->messages)) {
$this->tpl->newTemplate('install_results', 'install.tpl.html');
$this->tpl->addTemplateDataRepeat('install_results', 'resultmsgs', $this->install_results);
$this->tpl->addTemplateDataRepeat('install_results', 'resultmsgs', $this->messages);
$html_results = $this->tpl->getTemplate('install_results');
$this->messages = array();
}
$tpl_id = $this->getTemplateId();
$this->setTemplateId('install', 'install.tpl.html');
$this->tpl->addTemplateData($this->getTemplateId(), array(
'html_install' => $html_install,
'html_install' => $this->tpl->getTemplate($tpl_id),
'html_results' => $html_results,
));
@ -82,11 +77,10 @@ class Install extends AbstractModule {
}
/**
* Generate the main install page with prerequisites
* Say hi to our new user
*/
protected function executeIndex() {
$this->addMenu(false);
$tpl_data = array();
$this->setTemplateId('install_index', 'install.tpl.html');
// build prerequisites
$errors = 0;
@ -94,30 +88,22 @@ class Install extends AbstractModule {
$phpv = phpversion();
if(version_compare($phpv, '5.3.0', '<')) {
$errors++;
$this->addResult('PHP 5.3+ is required to run PHP Server Monitor.', 'error');
$this->addMessage('PHP 5.3+ is required to run PHP Server Monitor.', 'error');
} else {
$this->addResult('PHP version: ' . $phpv);
$this->addMessage('PHP version: ' . $phpv, 'success');
}
if(!function_exists('curl_init')) {
$this->addResult('PHP is installed without the cURL module. Please install cURL.', 'warning');
$this->addMessage('PHP is installed without the cURL module. Please install cURL.', 'warning');
} else {
$this->addResult('cURL installed');
$this->addMessage('PHP cURL module found', 'success');
}
if(!in_array('mysql', \PDO::getAvailableDrivers())) {
$errors++;
$this->addResult('The PDO MySQL driver needs to be installed.', 'error');
$this->addMessage('The PDO MySQL driver needs to be installed.', 'error');
}
if($errors > 0) {
// cannot continue
$this->addResult($errors . ' error(s) have been encountered. Please fix them and refresh this page.', 'error');
} else {
if(defined('PSM_CONFIG')) {
$this->addResult('Configuration file found.');
return $this->executeInstall();
} else {
return $this->executeConfig();
}
$this->addMessage($errors . ' error(s) have been encountered. Please fix them and refresh this page.', 'error');
}
}
@ -125,110 +111,163 @@ class Install extends AbstractModule {
* Help the user create a new config file
*/
protected function executeConfig() {
if(defined('PSM_CONFIG')) {
return $this->executeInstall();
}
// first detect "old" config file (2.0)
if(file_exists($this->path_config_old)) {
// oldtimer huh
$this->addResult('Configuration file for v2.0 found.');
$this->addResult(
'The location of the config file has been changed since the previous version.<br/>' .
'We will attempt to create a new config file for you.'
, 'warning');
$values = $this->parseConfig20();
} else {
// fresh install
$values = $_POST;
}
$config = array(
'host' => 'localhost',
'name' => '',
'user' => '',
'pass' => '',
'prefix' => 'psm_',
);
$this->setTemplateId('install_config_new', 'install.tpl.html');
$tpl_data = array();
$changed = false;
foreach($config as $ckey => &$cvalue) {
if(isset($values[$ckey])) {
$changed = true;
$cvalue = $values[$ckey];
if(!defined('PSM_CONFIG')) {
// first detect "old" config file (2.0)
if(file_exists($this->path_config_old)) {
// oldtimer huh
$this->addMessage('Configuration file for v2.0 found.', 'success');
$this->addMessage(
'The location of the config file has been changed since v2.0.<br/>' .
'We will attempt to create a new config file for you.'
, 'warning');
$values = $this->parseConfig20();
} else {
// fresh install
$values = $_POST;
}
}
// add config to template data for prefilling the form
$tpl_data = $config;
if($changed) {
// test db connection
$this->db = new \psm\Service\Database(
$config['host'],
$config['user'],
$config['pass'],
$config['name']
$config = array(
'host' => 'localhost',
'name' => '',
'user' => '',
'pass' => '',
'prefix' => 'psm_',
);
if($this->db->status()) {
$this->addResult('Connection to MySQL successful.');
$config_php = $this->writeConfigFile($config);
if($config_php === true) {
$this->addResult('Configuration file written successfully.');
return $this->executeInstall();
} else {
$this->addResult('Config file is not writable, we cannot save it for you.', 'error');
$this->tpl->newTemplate('install_config_new_copy', 'install.tpl.html');
$tpl_data['html_config_copy'] = $this->tpl->getTemplate('install_config_new_copy');
$tpl_data['php_config'] = $config_php;
$changed = false;
foreach($config as $ckey => &$cvalue) {
if(isset($values[$ckey])) {
$changed = true;
$cvalue = $values[$ckey];
}
}
// add config to template data for prefilling the form
$tpl_data = $config;
if($changed) {
// test db connection
$this->db = new \psm\Service\Database(
$config['host'],
$config['user'],
$config['pass'],
$config['name']
);
if($this->db->status()) {
$this->addMessage('Connection to MySQL successful.', 'success');
$config_php = $this->writeConfigFile($config);
if($config_php === true) {
$this->addMessage('Configuration file written successfully.', 'success');
} else {
$this->addMessage('Config file is not writable, we cannot save it for you.', 'error');
$this->tpl->newTemplate('install_config_new_copy', 'install.tpl.html');
$tpl_data['html_config_copy'] = $this->tpl->getTemplate('install_config_new_copy');
$tpl_data['php_config'] = $config_php;
}
} else {
$this->addMessage('Unable to connect to MySQL. Please check your information.', 'error');
}
} else {
$this->addResult('Unable to connect to MySQL. Please check your information.', 'error');
}
}
if(defined('PSM_CONFIG')) {
if($this->db->status()) {
if($this->isUpgrade()) {
// upgrade
if(version_compare($version_from, '2.2.0', '<')) {
// upgrade from before 2.2, does not have passwords yet.. create new user first
$this->addMessage('PLEASE CREATE A USER!', 'warning');
$this->setTemplateId('install_config_new_user', 'install.tpl.html');
} else {
$this->setTemplateId('install_config_upgrade', 'install.tpl.html');
$tpl_data['version'] = PSM_VERSION;
}
} else {
// fresh install ahead
$this->setTemplateId('install_config_new_user', 'install.tpl.html');
$tpl_data['username'] = (isset($_POST['username'])) ? $_POST['username'] : '';
$tpl_data['email'] = (isset($_POST['email'])) ? $_POST['email'] : '';
}
} else {
$this->addMessage('Configuration file found, but unable to connect to MySQL. Please check your information.', 'error');
}
}
$this->tpl->addTemplateData($this->getTemplateId(), $tpl_data);
}
/**
* Parse the 2.0 config file for prefilling
* @return array
*/
protected function parseConfig20() {
$config_old = file_get_contents($this->path_config_old);
$vars = array(
'prefix' => '',
'user' => '',
'pass' => '',
'name' => '',
'host' => '',
);
$pattern = "/define\('SM_DB_{key}', '(.*?)'/u";
foreach($vars as $key => $value) {
$pattern_key = str_replace('{key}', strtoupper($key), $pattern);
preg_match($pattern_key, $config_old, $value_matches);
$vars[$key] = (isset($value_matches[1])) ? $value_matches[1] : '';
}
return $vars;
}
/**
* Execute the upgrade process to a newer version
* Execute the install and upgrade process to a newer version
*/
protected function executeInstall() {
if(!defined('PSM_CONFIG')) {
$this->addResult('No valid configuration found.', 'error');
if(!defined('PSM_CONFIG') || !$this->db->status()) {
return $this->executeConfig();
}
if(!$this->db->status()) {
$this->addResult('MySQL connection failed.', 'error');
return;
}
$logger = array($this, 'addResult');
// check if user submitted username + password in previous step
// this would only be the case for new installs, and install from
// before 2.2
$new_user = array(
'user_name' => psm_POST('username'),
'name' => psm_POST('username'),
'password' => psm_POST('password'),
'password_repeat' => psm_POST('password_repeat'),
'email' => psm_POST('email', ''),
'level' => PSM_USER_ADMIN,
);
$validator = new \psm\Util\User\UserValidator($this->user);
$logger = array($this, 'addMessage');
$installer = new \psm\Util\Install\Installer($this->db, $logger);
$installer->install();
if($this->isUpgrade()) {
$this->addMessage('Upgrade process started.', 'info');
$version_from = $this->getPreviousVersion();
if($version_from === false) {
$this->addMessage('Unable to locate your previous version. Please run a fresh install.', 'error');
} else {
if(version_compare($version_from, PSM_VERSION, '=')) {
$this->addMessage('Your installation is already at the latest version.', 'success');
} elseif(version_compare($version_from, PSM_VERSION, '>')) {
$this->addMessage('This installer does not support downgrading, sorry.', 'error');
} else {
$this->addMessage('Upgrading from ' . $version_from . ' to ' . PSM_VERSION, 'info');
$installer->upgrade($version_from, PSM_VERSION);
}
if(version_compare($version_from, '2.2.0', '<')) {
$add_user = true;
}
}
} else {
// validate the lot
try {
$validator->email($new_user['email']);
$validator->password($new_user['password'], $new_user['password_repeat']);
} catch(\InvalidArgumentException $e) {
$this->addMessage(psm_get_lang('users', 'error_' . $e->getMessage()), 'error');
return $this->executeConfig();
}
$this->addMessage('Installation process started.', 'success');
$installer->install();
// add user
$add_user = true;
}
if($add_user) {
unset($new_user['password_repeat']);
$user_id = $this->db->save(PSM_DB_PREFIX.'users', $new_user);
if(intval($user_id) > 0) {
$this->addMessage('User account has been created successfully.');
} else {
$this->addMessage('There was an error adding your user account.');
}
}
$this->setTemplateId('install_success', 'install.tpl.html');
}
@ -262,30 +301,63 @@ class Install extends AbstractModule {
}
/**
* Add install result to be added to the main template
* @param string|array $msg
* @param string $status success/warning/error
* @return \psm\Module\Install
* Parse the 2.0 config file for prefilling
* @return array
*/
public function addResult($msg, $status = 'success') {
if(!is_array($msg)) {
$msg = array($msg);
}
if($status == 'error') {
$shortcode = 'important';
} else {
$shortcode = $status;
protected function parseConfig20() {
$config_old = file_get_contents($this->path_config_old);
$vars = array(
'prefix' => '',
'user' => '',
'pass' => '',
'name' => '',
'host' => '',
);
$pattern = "/define\('SM_DB_{key}', '(.*?)'/u";
foreach($vars as $key => $value) {
$pattern_key = str_replace('{key}', strtoupper($key), $pattern);
preg_match($pattern_key, $config_old, $value_matches);
$vars[$key] = (isset($value_matches[1])) ? $value_matches[1] : '';
}
foreach($msg as $m) {
$this->install_results[] = array(
'message' => $m,
'status' => strtoupper($status),
'shortcode' => $shortcode,
);
return $vars;
}
/**
* Is it an upgrade or install?
*/
protected function isUpgrade() {
if(!$this->db->status()) {
return false;
}
$confExists = $this->db->query("SHOW TABLES LIKE '".PSM_DB_PREFIX."config';", false)->rowCount();
if($confExists > 0) {
return true;
} else {
return false;
}
}
/**
* Get the previous version from the config table
* @return boolean|string FALSE on failure, string otherwise
*/
protected function getPreviousVersion() {
if(!$this->isUpgrade()) {
return false;
}
$version_conf = $this->db->selectRow(PSM_DB_PREFIX . 'config', array('key' => 'version'), array('value'));
if(empty($version_conf)) {
return false;
} else {
$version_from = $version_conf['value'];
if(strpos($version_from, '.') === false) {
// yeah, my bad.. previous version did not follow proper naming scheme
$version_from = rtrim(chunk_split($version_from, 1, '.'), '.');
}
return $version_from;
}
return $this;
}
}
?>

View File

@ -0,0 +1,182 @@
<?php
/**
* PHP Server Monitor
* Monitor your servers and websites.
*
* This file is part of PHP Server Monitor.
* PHP Server Monitor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PHP Server Monitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PHP Server Monitor. If not, see <http://www.gnu.org/licenses/>.
*
* @package phpservermon
* @author Pepijn Over <pep@neanderthal-technology.com>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.2.0
**/
namespace psm\Module;
use psm\Service\Database;
use psm\Service\Template;
class Login extends AbstractModule {
function __construct(Database $db, Template $tpl) {
parent::__construct($db, $tpl);
$this->setMinUserLevelRequired(PSM_USER_ANONYMOUS);
$this->setActions(array(
'login', 'forgot', 'reset',
), 'login');
$this->addMenu(false);
}
protected function executeLogin() {
$this->setTemplateId('login', 'login.tpl.html');
if(isset($_POST['user_name']) && isset($_POST['user_password'])) {
$rememberme = (isset($_POST['user_rememberme'])) ? true : false;
$result = $this->user->loginWithPostData(
$_POST['user_name'],
$_POST['user_password'],
$rememberme
);
if($result) {
// success login, redirect
header('Location: ' . $_SERVER['REQUEST_URI']);
die();
} else {
$this->addMessage(psm_get_lang('login', 'error_login_incorrect'), 'error');
}
}
$tpl_data = array(
'title_sign_in' => psm_get_lang('login', 'title_sign_in'),
'label_username' => psm_get_lang('login', 'username'),
'label_password' => psm_get_lang('login', 'password'),
'label_remember_me' => psm_get_lang('login', 'remember_me'),
'label_login' => psm_get_lang('login', 'login'),
'label_password_forgot' => psm_get_lang('login', 'password_forgot'),
'value_user_name' => (isset($_POST['user_name'])) ? $_POST['user_name'] : '',
'value_rememberme' => (isset($rememberme) && $rememberme) ? 'checked="checked"' : '',
);
$this->tpl->addTemplateData($this->getTemplateId(), $tpl_data);
}
/**
* Show/process the password forgot form (before the mail)
*/
protected function executeForgot() {
$this->setTemplateId('login_forgot', 'login.tpl.html');
if(isset($_POST['user_name'])) {
$user = $this->user->getUserByUsername($_POST['user_name']);
if(!empty($user)) {
$token = $this->user->generatePasswordResetToken($user->user_id);
// we have a token, send it along
$this->sendPasswordForgotMail(
$user->user_id,
$user->email,
$token
);
$this->addMessage(psm_get_lang('login', 'success_password_forgot'), 'success');
return $this->executeLogin();
} else {
$this->addMessage(psm_get_lang('login', 'error_user_incorrect'), 'error');
}
}
$tpl_data = array(
'title_forgot' => psm_get_lang('login', 'title_forgot'),
'label_username' => psm_get_lang('login', 'username'),
'label_submit' => psm_get_lang('login', 'submit'),
'label_go_back' => psm_get_lang('system', 'go_back'),
);
$this->tpl->addTemplateData($this->getTemplateId(), $tpl_data);
}
/**
* Show/process the password reset form (after the mail)
*/
protected function executeReset() {
$this->setTemplateId('login_reset', 'login.tpl.html');
$user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : 0;
$token = (isset($_GET['token'])) ? $_GET['token'] : '';
if(!$this->user->verifyPasswordResetToken($user_id, $token)) {
$this->addMessage(psm_get_lang('login', 'error_reset_invalid_link'), 'error');
return $this->executeLogin();
}
if(!empty($_POST['user_password_new']) && !empty($_POST['user_password_repeat'])) {
if($_POST['user_password_new'] !== $_POST['user_password_repeat']) {
$this->addMessage(psm_get_lang('login', 'error_login_passwords_nomatch'), 'error');
} else {
$result = $this->user->changePassword($user_id, $_POST['user_password_new']);
if($result) {
$this->addMessage(psm_get_lang('login', 'success_password_reset'), 'success');
return $this->executeLogin();
} else {
$this->addMessage(psm_get_lang('login', 'error_login_incorrect'), 'error');
}
}
}
$user = $this->user->getUser($user_id);
$tpl_data = array(
'title_reset' => psm_get_lang('login', 'title_reset'),
'label_username' => psm_get_lang('login', 'username'),
'label_password' => psm_get_lang('login', 'password'),
'label_password_repeat' => psm_get_lang('login', 'password_repeat'),
'label_reset' => psm_get_lang('login', 'password_reset'),
'label_go_back' => psm_get_lang('system', 'go_back'),
'value_user_name' => $user->user_name,
);
$this->tpl->addTemplateData($this->getTemplateId(), $tpl_data);
}
/**
* Sends the password-reset-email.
* @param int $user_id
* @param string $user_email
* @param string $user_password_reset_hash
*/
protected function sendPasswordForgotMail($user_id, $user_email, $user_password_reset_hash) {
$mail = psm_build_mail();
$mail->Subject = psm_get_lang('login' ,'password_reset_email_subject');
$url = psm_build_url(array(
'action' => 'reset',
'user_id' => $user_id,
'token' => $user_password_reset_hash,
));
$body = psm_get_lang('login', 'password_reset_email_body');
$body = str_replace('%link%', $url, $body);
$mail->Body = $body;
$mail->AltBody = str_replace('<br/>', "\n", $body);
$mail->AddAddress($user_email);
$mail->Send();
}
}

View File

@ -36,77 +36,27 @@ use psm\Service\Template;
class Users extends AbstractModule {
public $servers;
/**
* User data validator
* @var \psm\Util\User\UserValidator $user_validator
*/
protected $user_validator;
function __construct(Database $db, Template $tpl) {
parent::__construct($db, $tpl);
$this->setMinUserLevelRequired(PSM_USER_ADMIN);
$this->setActions(array(
'index', 'edit', 'delete', 'save',
), 'index');
$this->servers = $this->db->select(PSM_DB_PREFIX.'servers', null, array('server_id', 'label'));
}
/**
* Prepare the template to show the update screen for a user
*/
protected function executeEdit() {
$this->setTemplateId('users_update', 'users.tpl.html');
public function initialize() {
$this->user_validator = new \psm\Util\User\UserValidator($this->user);
$this->servers = $this->db->select(PSM_DB_PREFIX.'servers', null, array('server_id', 'label'));
$user_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$tpl_data = array();
$servers_count = count($this->servers);
switch((int) $user_id) {
case 0:
// insert mode
$tpl_data['titlemode'] = psm_get_lang('system', 'insert');
$tpl_data['edit_user_id'] = '0';
// add inactive class to all servers
for ($i = 0; $i < $servers_count; $i++) {
$this->servers[$i]['class'] = 'inactive';
}
break;
default:
// edit mode
$edit_user = $this->db->selectRow(
PSM_DB_PREFIX.'users',
array('user_id' => $user_id)
);
if (empty($edit_user)) {
$this->addMessage('Invalid user.');
return $this->initializeAction('index');
}
$tpl_data = array_merge($tpl_data, array(
'titlemode' => psm_get_lang('system', 'edit') . ' ' . $edit_user['name'],
'edit_user_id' => $edit_user['user_id'],
'edit_value_name' => $edit_user['name'],
'edit_value_mobile' => $edit_user['mobile'],
'edit_value_email' => $edit_user['email'],
));
// select servers for this user
$user_servers = explode(',', $edit_user['server_id']);
for ($h = 0; $h < $servers_count; $h++) {
if(in_array($this->servers[$h]['server_id'], $user_servers)) {
$this->servers[$h]['edit_checked'] = 'checked="checked"';
$this->servers[$h]['class'] = 'active';
}
}
break;
}
$this->tpl->addTemplateData(
$this->getTemplateId(),
$tpl_data
);
// add servers to template for the edit form
$this->tpl->addTemplateDataRepeat('users_update', 'servers', $this->servers);
return parent::initialize();
}
/**
@ -121,66 +71,164 @@ class Users extends AbstractModule {
$servers_labels[$server['server_id']] = $server['label'];
}
// get users from database
$users = $this->db->select(
PSM_DB_PREFIX.'users',
null,
null,
array('user_id', 'user_name', 'level', 'server_id', 'name', 'mobile', 'email'),
null,
array('name')
);
$user_count = count($users);
foreach($users as $x => &$user) {
$user['class'] = ($x & 1) ? 'odd' : 'even';
for ($x = 0; $x < $user_count; $x++) {
$users[$x]['class'] = ($x & 1) ? 'odd' : 'even';
$users[$x]['emp_servers'] = '';
$user['emp_servers'] = '';
// fix server list
$user_servers = explode(',', $users[$x]['server_id']);
if (empty($user_servers)) continue;
$user_servers = explode(',', $user['server_id']);
if(empty($user_servers)) continue;
foreach ($user_servers as $server) {
foreach($user_servers as $server) {
if (!isset($servers_labels[$server])) continue;
$users[$x]['emp_servers'] .= $servers_labels[$server] . '<br/>';
$user['emp_servers'] .= $servers_labels[$server] . '<br/>';
}
$users[$x]['emp_servers'] = substr($users[$x]['emp_servers'], 0, -5);
$user['emp_servers'] = substr($user['emp_servers'], 0, -5);
}
// add servers to template
$this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'users', $users);
}
/**
* Prepare the template to show the update screen for a user
*/
protected function executeEdit() {
$this->setTemplateId('users_update', 'users.tpl.html');
$user_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$fields_prefill = array('name', 'user_name', 'mobile', 'email');
if($user_id == 0) {
// insert mode
$title = psm_get_lang('system', 'insert');
$placeholder_password = '';
$lvl_selected = PSM_USER_USER; // default level is regular user
// attempt to prefill previously posted fields
$edit_user = new \stdClass();
foreach($fields_prefill as $field) {
$edit_user->$field = (isset($_POST[$field])) ? $_POST[$field] : '';
}
// add inactive class to all servers
foreach($this->servers as &$server) {
$server['class'] = 'inactive';
}
} else {
// edit mode
try {
$this->user_validator->userId($user_id);
} catch(\InvalidArgumentException $e) {
$this->addMessage(psm_get_lang('users', 'error_' . $e->getMessage()), 'error');
return $this->executeIndex();
}
$edit_user = $this->user->getUser($user_id);
$title = psm_get_lang('system', 'edit') . ' ' . $edit_user->name;
$placeholder_password = psm_get_lang('users', 'password_leave_blank');
$lvl_selected = $edit_user->level;
// select servers for this user
$user_servers = explode(',', $edit_user->server_id);
foreach($this->servers as &$server) {
if(in_array($server['server_id'], $user_servers)) {
$server['edit_checked'] = 'checked="checked"';
$server['class'] = 'active';
}
}
}
$tpl_data = array(
'titlemode' => $title,
'placeholder_password' => $placeholder_password,
'edit_user_id' => $user_id,
);
foreach($fields_prefill as $field) {
if(isset($edit_user->$field)) {
$tpl_data['edit_value_' . $field] = $edit_user->$field;
}
}
$ulvls_tpl = array();
foreach($this->user_validator->getUserLevels() as $lvl) {
$ulvls_tpl[] = array(
'value' => $lvl,
'label' => psm_get_lang('users', 'level_' . $lvl),
'selected' => ($lvl == $lvl_selected) ? 'selected="selected"' : '',
);
}
$this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'levels', $ulvls_tpl);
$this->tpl->addTemplateDataRepeat($this->getTemplateId(), 'servers', $this->servers);
$this->tpl->addTemplateData($this->getTemplateId(), $tpl_data);
}
/**
* Executes the saving of a user
*/
protected function executeSave() {
// check for add/edit mode
if (isset($_POST['name']) && isset($_POST['mobile']) && isset($_POST['email'])) {
$clean = array(
'name' => $_POST['name'],
'mobile' => $_POST['mobile'],
'email' => $_POST['email'],
'server_id' => (isset($_POST['server_id'])) ? implode(',', $_POST['server_id']) : ''
);
$id = (isset($_GET['id'])) ? intval($_GET['id']) : 0;
if(empty($_POST)) {
// dont process anything if no data has been posted
return $this->executeIndex();
}
$user_id = (isset($_GET['id'])) ? intval($_GET['id']) : 0;
// check for edit or add
if ((int) $id > 0) {
// edit
$this->db->save(
PSM_DB_PREFIX.'users',
$clean,
array('user_id' => $id)
);
$this->addMessage(psm_get_lang('users', 'updated'));
$fields = array('name', 'user_name', 'password', 'password_repeat', 'level', 'mobile', 'email', 'server_id');
$clean = array();
foreach($fields as $field) {
if(isset($_POST[$field])) {
if(is_array($_POST[$field])) {
$_POST[$field] = implode(',', $_POST[$field]);
}
$clean[$field] = trim(strip_tags($_POST[$field]));
} else {
// add
$this->db->save(PSM_DB_PREFIX.'users', $clean);
$this->addMessage(psm_get_lang('users', 'inserted'));
$clean[$field] = '';
}
}
$this->initializeAction('index');
// validate the lot
try {
$this->user_validator->username($clean['user_name'], $user_id);
$this->user_validator->email($clean['email']);
$this->user_validator->level($clean['level']);
// always validate password for new users,
// but only validate it for existing users when they change it.
if($user_id == 0 || ($user_id > 0 && $clean['password'] != '')) {
$this->user_validator->password($clean['password'], $clean['password_repeat']);
}
if($user_id > 0) {
$this->user_validator->userId($user_id);
}
} catch(\InvalidArgumentException $e) {
$this->addMessage(psm_get_lang('users', 'error_' . $e->getMessage()), 'error');
return $this->executeEdit();
}
if(!empty($clean['password'])) {
$password = $clean['password'];
$clean['password'] = '';
}
unset($clean['password_repeat']);
if($user_id > 0) {
// edit user
$this->db->save(PSM_DB_PREFIX.'users', $clean, array('user_id' => $user_id));
$this->addMessage(psm_get_lang('users', 'updated'), 'success');
} else {
// add user
$user_id = $this->db->save(PSM_DB_PREFIX.'users', $clean);
$this->addMessage(psm_get_lang('users', 'inserted'), 'success');
}
if(isset($password)) {
$this->user->changePassword($user_id, $password);
}
return $this->executeIndex();
}
/**
@ -189,16 +237,19 @@ class Users extends AbstractModule {
protected function executeDelete() {
$id = (isset($_GET['id'])) ? intval($_GET['id']) : 0;
if($id > 0) {
try {
$this->user_validator->userId($id);
$this->db->delete(
PSM_DB_PREFIX . 'users',
array(
'user_id' => $id,
)
array('user_id' => $id,)
);
$this->addMessage(psm_get_lang('system', 'deleted'));
$this->addMessage(psm_get_lang('system', 'deleted'), 'success');
} catch(\InvalidArgumentException $e) {
$this->addMessage(psm_get_lang('users', 'error_' . $e->getMessage()), 'error');
}
$this->initializeAction('index');
return $this->executeIndex();
}
// override parent::createHTMLLabels()
@ -208,11 +259,19 @@ class Users extends AbstractModule {
array(
'label_users' => psm_get_lang('system', 'users'),
'label_name' => psm_get_lang('users', 'name'),
'label_user_name' => psm_get_lang('users', 'user_name'),
'label_password' => psm_get_lang('users', 'password'),
'label_password_repeat' => psm_get_lang('users', 'password_repeat'),
'label_level' => psm_get_lang('users', 'level'),
'label_level_10' => psm_get_lang('users', 'level_10'),
'label_level_20' => psm_get_lang('users', 'level_20'),
'label_level_30' => psm_get_lang('users', 'level_30'),
'label_mobile' => psm_get_lang('users', 'mobile'),
'label_email' => psm_get_lang('users', 'email'),
'label_servers' => psm_get_lang('system', 'servers'),
'label_action' => psm_get_lang('system', 'action'),
'label_save' => psm_get_lang('system', 'save'),
'label_go_back' => psm_get_lang('system', 'go_back'),
'label_edit' => psm_get_lang('system', 'edit') . ' ' . psm_get_lang('users', 'user'),
'label_delete' => psm_get_lang('system', 'delete') . ' ' . psm_get_lang('users', 'user'),
'label_add_new' => psm_get_lang('system', 'add_new'),
@ -222,5 +281,3 @@ class Users extends AbstractModule {
return parent::createHTMLLabels();
}
}
?>

View File

@ -0,0 +1,423 @@
<?php
/**
* PHP Server Monitor
* Monitor your servers and websites.
*
* This file is part of PHP Server Monitor.
* PHP Server Monitor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PHP Server Monitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PHP Server Monitor. If not, see <http://www.gnu.org/licenses/>.
*
* @package phpservermon
* @author Panique <https://github.com/panique/php-login-advanced/>
* @author Pepijn Over <pep@neanderthal-technology.com>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.2.0
**/
namespace psm\Service;
/**
* This is a heavily modified version of the php-login-advanced project by Panique.
*
* @author Panique
* @author Pepijn Over
* @link http://www.php-login.net
* @link https://github.com/panique/php-login-advanced/
* @license http://opensource.org/licenses/MIT MIT License
*/
class User {
/**
* The database connection
* @var \PDO $db_connection
*/
protected $db_connection = null;
/**
* Local cache of user data
* @var array $user_data
*/
protected $user_data = array();
/**
* Current user id
* @var int $user_id
*/
protected $user_id;
/**
* The user's login status
* @var boolean $user_is_logged_in
*/
protected $user_is_logged_in = false;
/**
* the function "__construct()" automatically starts whenever an object of this class is created,
* you know, when you do "$login = new Login();"
*/
public function __construct(Database $db) {
$this->db_connection = $db->pdo();
session_start();
// check the possible login actions:
// 1. logout (happen when user clicks logout button)
// 2. login via session data (happens each time user opens a page on your php project AFTER he has successfully logged in via the login form)
// 3. login via cookie
// if user has an active session on the server
if(!$this->loginWithSessionData()) {
$this->loginWithCookieData();
}
if(isset($_GET["logout"])) {
$this->doLogout();
// logged out, redirect to login
header('Location: ' . psm_build_url());
die();
}
}
/**
* Get user by id, or get current user.
* @return object|boolean FALSE if user not found, object otherwise
*/
public function getUser($user_id = null) {
if($user_id == null) {
if(!$this->isUserLoggedIn()) {
return false;
} else {
$user_id = $this->getUserId();
}
}
if(!isset($this->user_data[$user_id])) {
$query_user = $this->db_connection->prepare('SELECT * FROM '.PSM_DB_PREFIX.'users WHERE user_id = :user_id');
$query_user->bindValue(':user_id', $user_id, \PDO::PARAM_INT);
$query_user->execute();
// get result row (as an object)
$this->user_data[$user_id] = $query_user->fetchObject();
}
return $this->user_data[$user_id];
}
/**
* Search into database for the user data of user_name specified as parameter
* @return object|boolean user data as an object if existing user
*/
public function getUserByUsername($user_name) {
// database query, getting all the info of the selected user
$query_user = $this->db_connection->prepare('SELECT * FROM '.PSM_DB_PREFIX.'users WHERE user_name = :user_name');
$query_user->bindValue(':user_name', $user_name, \PDO::PARAM_STR);
$query_user->execute();
// get result row (as an object)
return $query_user->fetchObject();
}
/**
* Logs in with S_SESSION data.
*/
private function loginWithSessionData() {
if(empty($_SESSION) || !isset($_SESSION['user_id'])) {
return false;
}
$user = $this->getUser($_SESSION['user_id']);
if(!empty($user)) {
$this->setUserLoggedIn($user->user_id);
return true;
} else {
// user no longer exists in database
// call logout to clean up session vars
$this->doLogout();
return false;
}
}
/**
* Logs in via the Cookie
* @return bool success state of cookie login
*/
private function loginWithCookieData() {
if (isset($_COOKIE['rememberme'])) {
// extract data from the cookie
list ($user_id, $token, $hash) = explode(':', $_COOKIE['rememberme']);
// check cookie hash validity
if ($hash == hash('sha256', $user_id . ':' . $token . PSM_LOGIN_COOKIE_SECRET_KEY) && !empty($token)) {
// cookie looks good, try to select corresponding user
// get real token from database (and all other data)
$user = $this->getUser($user_id);
if(!empty($user) && $token === $user->rememberme_token) {
$this->setUserLoggedIn($user->user_id, true);
// Cookie token usable only once
$this->newRememberMeCookie();
return true;
}
}
// call logout to remove invalid cookie
$this->doLogout();
}
return false;
}
/**
* Logs in with the data provided in $_POST, coming from the login form
* @param string $user_name
* @param string $user_password
* @param boolean $user_rememberme
* @return boolean
*/
public function loginWithPostData($user_name, $user_password, $user_rememberme = false) {
$user_name = trim($user_name);
$user_password = trim($user_password);
if(empty($user_name) && empty($user_password)) {
return false;
}
$user = $this->getUserByUsername($user_name);
// using PHP 5.5's password_verify() function to check if the provided passwords fits to the hash of that user's password
if(!isset($user->user_id)) {
password_verify($user_password, 'dummy_call_against_timing');
return false;
} else if (! password_verify($user_password, $user->password)) {
return false;
}
$this->setUserLoggedIn($user->user_id, true);
// if user has check the "remember me" checkbox, then generate token and write cookie
if ($user_rememberme) {
$this->newRememberMeCookie();
}
// recalculate the user's password hash
// DELETE this if-block if you like, it only exists to recalculate users's hashes when you provide a cost factor,
// by default the script will use a cost factor of 10 and never change it.
// check if the have defined a cost factor in config/hashing.php
if (defined('PSM_LOGIN_HASH_COST_FACTOR')) {
// check if the hash needs to be rehashed
if (password_needs_rehash($user->password, PASSWORD_DEFAULT, array('cost' => PSM_LOGIN_HASH_COST_FACTOR))) {
$this->changePassword($user->user_id, $user_password);
}
}
return true;
}
/**
* Set the user logged in
* @param int $user_id
* @param boolean $regenerate regenerate session id against session fixation?
*/
protected function setUserLoggedIn($user_id, $regenerate = false) {
if($regenerate) {
session_regenerate_id();
}
$_SESSION['user_id'] = $user_id;
$_SESSION['user_logged_in'] = 1;
// declare user id, set the login status to true
$this->user_id = $user_id;
$this->user_is_logged_in = true;
}
/**
* Create all data needed for remember me cookie connection on client and server side
*/
private function newRememberMeCookie() {
// generate 64 char random string and store it in current user data
$random_token_string = hash('sha256', mt_rand());
$sth = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET rememberme_token = :user_rememberme_token WHERE user_id = :user_id');
$sth->execute(array(':user_rememberme_token' => $random_token_string, ':user_id' => $this->getUserId()));
// generate cookie string that consists of userid, randomstring and combined hash of both
$cookie_string_first_part = $this->getUserId() . ':' . $random_token_string;
$cookie_string_hash = hash('sha256', $cookie_string_first_part . PSM_LOGIN_COOKIE_SECRET_KEY);
$cookie_string = $cookie_string_first_part . ':' . $cookie_string_hash;
// set cookie
setcookie('rememberme', $cookie_string, time() + PSM_LOGIN_COOKIE_RUNTIME, "/", PSM_LOGIN_COOKIE_DOMAIN);
}
/**
* Delete all data needed for remember me cookie connection on client and server side
*/
private function deleteRememberMeCookie() {
// Reset rememberme token
if(isset($_SESSION['user_id'])) {
$sth = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET rememberme_token = NULL WHERE user_id = :user_id');
$sth->execute(array(':user_id' => $_SESSION['user_id']));
}
// set the rememberme-cookie to ten years ago (3600sec * 365 days * 10).
// that's obivously the best practice to kill a cookie via php
// @see http://stackoverflow.com/a/686166/1114320
setcookie('rememberme', false, time() - (3600 * 3650), '/', PSM_LOGIN_COOKIE_DOMAIN);
}
/**
* Perform the logout, resetting the session
*/
public function doLogout() {
$this->deleteRememberMeCookie();
$_SESSION = array();
session_destroy();
session_start();
session_regenerate_id();
$this->user_is_logged_in = false;
}
/**
* Simply return the current state of the user's login
* @return bool user's login status
*/
public function isUserLoggedIn() {
return $this->user_is_logged_in;
}
/**
* Sets a random token into the database (that will verify the user when he/she comes back via the link
* in the email) and returns it
* @param int $user_id
* @return string|boolean FALSE on error, string otherwise
*/
public function generatePasswordResetToken($user_id) {
$user_id = intval($user_id);
if($user_id == 0) {
return false;
}
// generate timestamp (to see when exactly the user (or an attacker) requested the password reset mail)
$temporary_timestamp = time();
// generate random hash for email password reset verification (40 char string)
$user_password_reset_hash = sha1(uniqid(mt_rand(), true));
$query_update = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET password_reset_hash = :user_password_reset_hash,
password_reset_timestamp = :user_password_reset_timestamp
WHERE user_id = :user_id');
$query_update->bindValue(':user_password_reset_hash', $user_password_reset_hash, \PDO::PARAM_STR);
$query_update->bindValue(':user_password_reset_timestamp', $temporary_timestamp, \PDO::PARAM_INT);
$query_update->bindValue(':user_id', $user_id, \PDO::PARAM_INT);
$query_update->execute();
// check if exactly one row was successfully changed:
if ($query_update->rowCount() == 1) {
return $user_password_reset_hash;
} else {
return false;
}
}
/**
* Checks if the verification string in the account verification mail is valid and matches to the user.
*
* Please note it is valid for 1 hour.
* @param int $user_id
* @param string $token
* @return boolean
*/
public function verifyPasswordResetToken($user_id, $token) {
$user_id = intval($user_id);
if(empty($user_id) || empty($token)) {
return false;
}
$user = $this->getUser($user_id);
if(isset($user->user_id) && $user->password_reset_hash == $token) {
$runtime = (defined('PSM_LOGIN_RESET_RUNTIME')) ? PSM_LOGIN_RESET_RUNTIME : 3600;
$timestamp_max_interval = time() - $runtime;
if($user->password_reset_timestamp > $timestamp_max_interval) {
return true;
}
}
return false;
}
/**
* Change the password of a user
* @param int $user_id
* @param string $password
* @return boolean TRUE on success, FALSE on failure
*/
public function changePassword($user_id, $password) {
$user_id = intval($user_id);
if(empty($user_id) || empty($password)) {
return false;
}
// now it gets a little bit crazy: check if we have a constant PSM_LOGIN_HASH_COST_FACTOR defined (in src/includes/psmconfig.inc.php),
// if so: put the value into $hash_cost_factor, if not, make $hash_cost_factor = null
$hash_cost_factor = (defined('PSM_LOGIN_HASH_COST_FACTOR') ? PSM_LOGIN_HASH_COST_FACTOR : null);
// crypt the user's password with the PHP 5.5's password_hash() function, results in a 60 character hash string
// the PASSWORD_DEFAULT constant is defined by the PHP 5.5, or if you are using PHP 5.3/5.4, by the password hashing
// compatibility library. the third parameter looks a little bit shitty, but that's how those PHP 5.5 functions
// want the parameter: as an array with, currently only used with 'cost' => XX.
$user_password_hash = password_hash($password, PASSWORD_DEFAULT, array('cost' => $hash_cost_factor));
// write users new hash into database
$query_update = $this->db_connection->prepare('UPDATE '.PSM_DB_PREFIX.'users SET password = :user_password_hash,
password_reset_hash = NULL, password_reset_timestamp = NULL
WHERE user_id = :user_id');
$query_update->bindValue(':user_password_hash', $user_password_hash, \PDO::PARAM_STR);
$query_update->bindValue(':user_id', $user_id, \PDO::PARAM_STR);
$query_update->execute();
// check if exactly one row was successfully changed:
if ($query_update->rowCount() == 1) {
return true;
} else {
return false;
}
}
/**
* Gets the user id
* @return int
*/
public function getUserId() {
return $this->user_id;
}
/**
* Gets the username
* @return string
*/
public function getUsername() {
$user = $this->getUser();
return (isset($user->user_name) ? $user->user_name : null);
}
/**
* Gets the user level
* @return int
*/
public function getUserLevel() {
$user = $this->getUser();
if(isset($user->level)) {
return $user->level;
} else {
return PSM_USER_ANONYMOUS;
}
}
}

View File

@ -100,23 +100,30 @@ class Installer {
public function install() {
$this->installTables();
$version_conf = $this->db->selectRow(PSM_DB_PREFIX . 'config', array('key' => 'version'), array('key', 'value'));
if(empty($version_conf)) {
// fresh install
$version_from = null;
} else {
// existing install
$version_from = $version_conf['value'];
if(strpos($version_from, '.') === false) {
// yeah, my bad.. previous version did not follow proper naming scheme
$version_from = rtrim(chunk_split($version_from, 1, '.'), '.');
}
}
$this->upgrade(PSM_VERSION, $version_from);
$this->log('Installation finished!');
$this->log('Populating database...');
$queries = array();
$queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "servers` (`ip`, `port`, `label`, `type`, `status`, `error`, `rtime`, `last_online`, `last_check`, `active`, `email`, `sms`) VALUES ('http://sourceforge.net/index.php', 80, 'SourceForge', 'website', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes'), ('smtp.gmail.com', 465, 'Gmail SMTP', 'service', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes')";
$queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "config` (`key`, `value`) VALUE
('language', 'en'),
('email_status', '1'),
('email_from_email', 'monitor@example.org'),
('email_from_name', 'Server Monitor'),
('sms_status', '1'),
('sms_gateway', 'mollie'),
('sms_gateway_username', 'username'),
('sms_gateway_password', 'password'),
('sms_from', '1234567890'),
('alert_type', 'status'),
('log_status', '1'),
('log_email', '1'),
('log_sms', '1'),
('version', '" . PSM_VERSION . "'),
('auto_refresh_servers', '0'),
('show_update', '1'),
('last_update_check', '0'),
('cron_running', '0'),
('cron_running_time', '0');";
$this->execSQL($queries);
}
/**
@ -124,14 +131,21 @@ class Installer {
*/
protected function installTables() {
$tables = array(
PSM_DB_PREFIX . 'users' => "CREATE TABLE `" . PSM_DB_PREFIX . "users` (
`user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`server_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`mobile` varchar(15) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;",
PSM_DB_PREFIX . 'users' => "CREATE TABLE IF NOT EXISTS `monitor_users` (
`user_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL COMMENT 'user''s name, unique',
`password` varchar(255) NOT NULL COMMENT 'user''s password in salted and hashed format',
`password_reset_hash` char(40) DEFAULT NULL COMMENT 'user''s password reset code',
`password_reset_timestamp` bigint(20) DEFAULT NULL COMMENT 'timestamp of the password reset request',
`rememberme_token` varchar(64) DEFAULT NULL COMMENT 'user''s remember-me cookie token',
`level` tinyint(2) unsigned NOT NULL DEFAULT '20',
`server_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`mobile` varchar(15) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `unique_username` (`user_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;",
PSM_DB_PREFIX . 'log' => "CREATE TABLE `" . PSM_DB_PREFIX . "log` (
`log_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`server_id` int(11) unsigned NOT NULL,
@ -179,50 +193,19 @@ class Installer {
/**
* Populate the tables and perform upgrades if necessary
* @param string $version
* @param string $version_from
* @param string $version_to
*/
public function upgrade($version, $version_from = null) {
if($version_from === null) {
$this->log('Populating database...');
$queries = array();
$queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "users` (`server_id`, `name`, `mobile`, `email`) VALUES ('1,2', 'example_user', '0123456789', 'user@example.com')";
$queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "servers` (`ip`, `port`, `label`, `type`, `status`, `error`, `rtime`, `last_online`, `last_check`, `active`, `email`, `sms`) VALUES ('http://sourceforge.net/index.php', 80, 'SourceForge', 'website', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes'), ('smtp.gmail.com', 465, 'Gmail SMTP', 'service', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes')";
$queries[] = "INSERT INTO `" . PSM_DB_PREFIX . "config` (`key`, `value`) VALUE
('language', 'en'),
('email_status', '1'),
('email_from_email', 'monitor@example.org'),
('email_from_name', 'Server Monitor'),
('sms_status', '1'),
('sms_gateway', 'mollie'),
('sms_gateway_username', 'username'),
('sms_gateway_password', 'password'),
('sms_from', '1234567890'),
('alert_type', 'status'),
('log_status', '1'),
('log_email', '1'),
('log_sms', '1'),
('version', '{$version}'),
('auto_refresh_servers', '0'),
('show_update', '1'),
('last_update_check', '0'),
('cron_running', '0'),
('cron_running_time', '0');";
$this->execSQL($queries);
} else {
if(version_compare($version_from, $version, '<')) {
$this->log('Upgrade detected, upgrading from ' . $version_from . ' to ' . $version);
if(version_compare($version_from, '2.1.0', '<')) {
// upgrade to 2.1.0
$this->upgrade210();
}
if(version_compare($version_from, '2.2.0', '<')) {
// upgrade to 2.2.0
$this->upgrade220();
}
}
$this->execSQL("UPDATE `" . PSM_DB_PREFIX . "config` SET `value` = '{$version}' WHERE `key` = 'version';");
public function upgrade($version_from, $version_to) {
if(version_compare($version_from, '2.1.0', '<')) {
// upgrade to 2.1.0
$this->upgrade210();
}
if(version_compare($version_from, '2.2.0', '<')) {
// upgrade to 2.2.0
$this->upgrade220();
}
$this->db->save(PSM_DB_PREFIX . 'config', array('value' => $version_from), array('key' => 'version'));
}
/**
@ -254,6 +237,17 @@ class Installer {
$queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "log` CHANGE `server_id` `server_id` INT( 11 ) UNSIGNED NOT NULL;";
$queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "servers` CHANGE `server_id` `server_id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT;";
$queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users` CHANGE `user_id` `user_id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT;";
$queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users`
ADD `user_name` varchar(64) COLLATE utf8_general_ci NOT NULL COMMENT 'user\'s name, unique' AFTER `user_id`,
ADD `password` varchar(255) COLLATE utf8_general_ci NOT NULL COMMENT 'user\'s password in salted and hashed format' AFTER `user_name`,
ADD `password_reset_hash` char(40) COLLATE utf8_general_ci DEFAULT NULL COMMENT 'user\'s password reset code' AFTER `password`,
ADD `password_reset_timestamp` bigint(20) DEFAULT NULL COMMENT 'timestamp of the password reset request' AFTER `password_reset_hash`,
ADD `rememberme_token` varchar(64) COLLATE utf8_general_ci DEFAULT NULL COMMENT 'user\'s remember-me cookie token' AFTER `password_reset_timestamp`,
ADD `level` TINYINT( 2 ) UNSIGNED NOT NULL DEFAULT '20' AFTER `rememberme_token`;";
// make sure all current users are admins (previously we didnt have non-admins):
$queries[] = "UPDATE `" . PSM_DB_PREFIX . "users` SET `user_name`=`email`, `level`=10;";
$queries[] = "ALTER TABLE `" . PSM_DB_PREFIX . "users` ADD UNIQUE `unique_username` ( `user_name` );";
$queries[] = "CREATE TABLE IF NOT EXISTS `" . PSM_DB_PREFIX . "uptime` (
`server_id` INT( 11 ) NOT NULL,
`date` DATETIME NOT NULL ,
@ -264,5 +258,3 @@ class Installer {
$this->execSQL($queries);
}
}
?>

View File

@ -0,0 +1,145 @@
<?php
/**
* PHP Server Monitor
* Monitor your servers and websites.
*
* This file is part of PHP Server Monitor.
* PHP Server Monitor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PHP Server Monitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PHP Server Monitor. If not, see <http://www.gnu.org/licenses/>.
*
* @package phpservermon
* @author Pepijn Over <pep@neanderthal-technology.com>
* @copyright Copyright (c) 2008-2014 Pepijn Over <pep@neanderthal-technology.com>
* @license http://www.gnu.org/licenses/gpl.txt GNU GPL v3
* @version Release: @package_version@
* @link http://www.phpservermonitor.org/
* @since phpservermon 2.2.0
**/
namespace psm\Util\User;
/**
* The UserValidator helps you to check input data for user accounts.
*/
class UserValidator {
/**
* Available editable user levels
* @var array $user_levels
*/
protected $user_levels = array(PSM_USER_ADMIN, PSM_USER_USER);
/**
* User service
* @var \psm\Service\User $user
*/
protected $user;
public function __construct(\psm\Service\User $user) {
$this->user = $user;
}
/**
* Check if the user id exists
* @param int $user_id
* @return boolean
* @throws \InvalidArgumentException
*/
public function userId($user_id) {
$user = $this->user->getUser($user_id);
if(empty($user)) {
throw new \InvalidArgumentException('user_no_match');
}
return true;
}
/**
* Check username on:
*
* - Length (2-64 chars)
* - Contents (alphabetic chars and digits only)
* - Unique
* @param string $username
* @param int $user_id to check whether the username is unique
* @return boolean
* @throws \InvalidArgumentException
*/
public function username($username, $user_id = 0) {
if(strlen($username) > 64 || strlen($username) < 2) {
throw new \InvalidArgumentException('user_name_bad_length');
}
if (!preg_match('/^[a-zA-Z\d_]{2,64}$/i', $username)) {
throw new \InvalidArgumentException('user_name_invalid');
}
$user_exists = $this->user->getUserByUsername($username);
if(!empty($user_exists) && ($user_id == 0 || $user_id != $user_exists->user_id)) {
throw new \InvalidArgumentException('user_name_exists');
}
return true;
}
/**
* Check user password
* @param string $password
* @param string $password_repeat
* @return boolean
* @throws \InvalidArgumentException
*/
public function password($password, $password_repeat) {
if(empty($password) || empty($password_repeat)) {
throw new \InvalidArgumentException('user_password_invalid');
}
if($password !== $password_repeat) {
throw new \InvalidArgumentException('user_password_no_match');
}
return true;
}
/**
* Check email
* @param string $email
* @return boolean
* @throws \InvalidArgumentException
*/
public function email($email) {
if(strlen($email) > 255 || strlen($email) < 5) {
throw new \InvalidArgumentException('user_email_bad_length');
}
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('user_email_invalid');
}
return true;
}
/**
* Check user level
* @param int $level
* @return boolean
* @throws \InvalidArgumentException
*/
public function level($level) {
if(!in_array($level, $this->user_levels)) {
throw new \InvalidArgumentException('user_level_invalid');
}
return true;
}
/**
* Get list of all available user levels
* @return array
*/
public function getUserLevels() {
return $this->user_levels;
}
}

View File

@ -1,25 +1,82 @@
<!--%tpl_install-->
<div class="hero-unit">
<h1><img class="pull-right" src="static/opensource.png" width="100" alt="" /> &nbsp;PHP Server Monitor</h1>
<h2><a href="http://www.phpservermonitor.org/"><img class="pull-right" src="static/opensource.png" width="100" alt="" /></a> &nbsp;PHP Server Monitor</h2>
<p>&nbsp;</p>
<p>
<a class="btn btn-primary btn-large" target="_blank" href="http://www.phpservermonitor.org/">PHP Server Monitor</a>
<a class="btn btn-large" target="_blank" href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a>
</p>
<p>PHP Server Monitor is a script that checks whether the servers on your list are up and running on the selected ports. It comes with a web based user interface where you can add and remove servers or websites from the MySQL database, and you can manage users for each server with a mobile number and email address.</p>
<p>To install PHP Server Monitor, please follow the instructions below.</p>
{html_install}
</div>
<div class="row-fluid">
<div class="span12">{html_results}</div>
</div>
{html_install}
<!--%%tpl_install-->
<!--%tpl_install_index-->
<p>Welcome to the installation of PHP Server Monitor. This page will guide you through the steps to install or upgrade your monitor.</p>
<p>Before we start, we need to verify your system meets the requirements.
If you see any errors in the list below, you may still continue, but PHP Server Monitor may not work correctly.
It is recommended you fix any errors before continuing.
</p>
{html_results}
<p>&nbsp;</p>
<p>
<a class="btn btn-primary btn-large" href="install.php?action=config">Let's go</a>
</p>
<!--%%tpl_install_index-->
<!--%tpl_install_config_upgrade-->
<p>We have discovered a previous version.</p>
<p>In the next step we will upgrade your database to the latest version.</p>
{html_results}
<p>&nbsp;</p>
<p><a class="btn btn-primary btn-large" href="install.php?action=install">Upgrade to {version}</a></p>
<!--%%tpl_install_config_upgrade-->
<!--%tpl_install_config_new_user-->
<p>Sweet, your database connection is up and running!</p>
<p>Next, please set up a new account to access your monitor:</p>
{html_results}
<p>&nbsp;</p>
<div class="row-fluid">
<div class="span6">
<form id="psm_config" class="form-horizontal" action="install.php?action=install" method="post">
<div class="control-group">
<label class="control-label" for="username">Username</label>
<div class="controls">
<input type="text" id="username" name="username" value="{username}">
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">Password</label>
<div class="controls">
<input type="password" id="password" name="password" value="">
</div>
</div>
<div class="control-group">
<label class="control-label" for="password_repeat">Password repeat</label>
<div class="controls">
<input type="password" id="password_repeat" name="password_repeat" value="">
</div>
</div>
<div class="control-group">
<label class="control-label" for="email">Email</label>
<div class="controls">
<input type="text" id="email" name="email" value="{email}" />
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn btn-primary btn-large">Install</button>
</div>
</div>
</form>
</div>
{html_config_copy}
</div>
<!--%%tpl_install_config_new_user-->
<!--%tpl_install_config_new-->
<div class="row-fluid">
<div class="span12">{html_results}</div>
</div>
<div class="row-fluid">
<div class="span6">
<form id="psm_config" class="form-horizontal" action="install.php" method="post">
<!--<input type="hidden" name="action" value="config" />-->
<form id="psm_config" class="form-horizontal" action="install.php?action=config" method="post">
<h3>Please enter your database info:</h3>
<div class="control-group">
<label class="control-label" for="host">Database host</label>
@ -66,7 +123,8 @@
<div class="span6">
<h3>Your config file:</h3>
<div class="alert alert-error">Unable to save your configuration.</div>
<p>Please create a new file in the project directory called "config.php" and copy the information below.</p>
<p>Your database information is valid, however we are unable to create the configuration file automatically.
Please create a new file in the project directory called "config.php" and copy the information below.</p>
<p>After you have copied the configuration, press the button to continue.</p>
<p class="pull-left"><textarea rows="10">{php_config}</textarea></p>
<p class="offset2"><input type="submit" class="btn btn-primary" value="I have saved the configuration" onclick="location.reload(true);" /></p>
@ -74,11 +132,19 @@
<!--%%tpl_install_config_new_copy-->
<!--%tpl_install_success-->
<div class="row-fluid">
<div class="span12">{html_results}</div>
</div>
<div class="row-fluid">
<div class="span12">
The installation is complete. Please check above if errors have occured.<br/>
If no errors have occurred, you are good to go.<br><br>
<a class="btn btn-primary" href="index.php">Click here to go to the monitor</a>
<p>&nbsp;</p>
<p>The installation is complete. Please check above if errors have occurred.<br>
If no errors have occurred, you are good to go.<br><br></p>
<p>
<a class="btn btn-primary btn-large" href="index.php">Go to your monitor</a>
<a class="btn btn-large" target="_blank" href="http://www.phpservermonitor.org/">PHP Server Monitor</a>
<a class="btn btn-large" target="_blank" href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a>
</p>
</div>
</div>
<!--%%tpl_install_success-->

View File

@ -0,0 +1,39 @@
<!--%tpl_login-->
<div class="container">
<form class="form-signin" method="post">
<h2 class="form-signin-heading">{title_sign_in}</h2>
<input type="text" name="user_name" class="input-block-level" placeholder="{label_username}" value="{value_user_name}" required>
<input type="password" name="user_password" class="input-block-level" placeholder="{label_password}" required>
<input type="hidden" name="action" value="login">
<label class="checkbox">
<input type="checkbox" name="user_rememberme" value="1" {value_rememberme}> {label_remember_me}
</label>
<button class="btn btn-large btn-primary" type="submit">{label_login}</button>
<a class="btn btn-large" href="?action=forgot">{label_password_forgot}</a>
</form>
</div>
<!--%%tpl_login-->
<!--%tpl_login_forgot-->
<div class="container">
<form class="form-signin" method="post">
<h2 class="form-signin-heading">{title_forgot}</h2>
<input type="text" name="user_name" class="input-block-level" placeholder="{label_username}" value="{value_user_name}" required>
<button class="btn btn-large btn-primary" type="submit">{label_submit}</button>
<a class="btn btn-large" href="?">{label_go_back}</a>
</form>
</div>
<!--%%tpl_login_forgot-->
<!--%tpl_login_reset-->
<div class="container">
<form class="form-signin" method="post">
<h2 class="form-signin-heading">{title_reset}</h2>
<input type="text" name="user_name" class="input-block-level" placeholder="{label_username}" value="{value_user_name}" required disabled="disabled">
<input type="password" name="user_password_new" class="input-block-level" placeholder="{label_password}" required autocomplete="off">
<input type="password" name="user_password_repeat" class="input-block-level" placeholder="{label_password_repeat}" required autocomplete="off">
<button class="btn btn-large btn-primary" type="submit">{label_reset}</button>
<a class="btn btn-large" href="?">{label_go_back}</a>
</form>
</div>
<!--%%tpl_login_reset-->

View File

@ -53,11 +53,18 @@
</div>
<div class="row-fluid">
<div class="span12">
<div id="flashmessage" class="alert alert-info hide">{message}</div>
<div id="flashmessage" class="alert alert-info hide">
<!--%tpl_repeat_messages-->
<div>
<p class="pull-left span1"><span class="label label-{shortcode}">{status}</span></p>
<p>{message}</p>
</div>
<!--%%tpl_repeat_messages-->
{messages}
</div>
</div>
</div>
<div class="row-fluid">{content}</div>
{html_footer}
</div>
<!-- /container -->
@ -67,25 +74,21 @@
<!--%tpl_main_menu-->
<div class="nav-collapse">
<ul class="nav pull-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{label_usermenu} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{url_logout}">{label_logout}</a></li>
</ul>
</li>
</ul>
<ul class="nav">
<li id="nav_option_servers" class="{active_servers}">
<a href="index.php?type=servers">{label_servers}</a>
</li>
<li id="nav_option_users" class="{active_users}">
<a href="index.php?type=users">{label_users}</a>
</li>
<li id="nav_option_log" class="{active_log}">
<a href="index.php?type=log">{label_log}</a>
</li>
<li id="nav_option_status" class="{active_status}">
<a href="index.php?type=status">{label_status}</a>
</li>
<li id="nav_option_config" class="{active_config}">
<a href="index.php?type=config">{label_config}</a>
</li>
<li id="nav_option_update">
<a href="index.php?action=check">{label_update}</a>
<!--%tpl_repeat_menu-->
<li id="nav_option_{key}" class="{active}">
<a href="{url}">{label}</a>
</li>
<!--%%tpl_repeat_menu-->
{menu}
<li id="nav_option_help">
<a href="http://www.phpservermonitor.org/" target="_blank">{label_help}</a>
</li>

View File

@ -10,6 +10,8 @@
<thead>
<tr>
<th>{label_name}</th>
<th>{label_user_name}</th>
<th>{label_level}</th>
<th>{label_mobile}</th>
<th>{label_email}</th>
<th>{label_servers}</th>
@ -20,6 +22,8 @@
<!--%tpl_repeat_users-->
<tr>
<td>{name}</td>
<td>{user_name}</td>
<td>{label_level_{level}}</td>
<td>{mobile}</td>
<td>{email}</td>
<td>{emp_servers}</td>
@ -47,23 +51,52 @@
<div class="control-group">
<label class="control-label" for="name">{label_name}</label>
<div class="controls">
<input type="text" name="name" value="{edit_value_name}" maxlength="255" />
<input type="text" id="name" name="name" value="{edit_value_name}" maxlength="255" required>
</div>
</div>
<div class="control-group">
<label class="control-label" for="user_name">{label_user_name}</label>
<div class="controls">
<input type="text" id="user_name" name="user_name" value="{edit_value_user_name}" maxlength="64" required>
</div>
</div>
<div class="control-group">
<label class="control-label" for="level">{label_level}</label>
<div class="controls">
<select id="level" name="level">
<!--%tpl_repeat_levels-->
<option value="{value}" {selected}>{label}</option>
<!--%%tpl_repeat_levels-->
{levels}
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">{label_password}</label>
<div class="controls">
<input type="password" id="password" name="password" maxlength="255" placeholder="{placeholder_password}" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="password_repeat">{label_password_repeat}</label>
<div class="controls">
<input type="password" id="password_repeat" name="password_repeat" maxlength="255" placeholder="{placeholder_password}" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="mobile">{label_mobile}</label>
<div class="controls">
<input type="text" name="mobile" value="{edit_value_mobile}" maxlength="15" />
<input type="text" id="mobile" name="mobile" value="{edit_value_mobile}" maxlength="15" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="name">{label_email}</label>
<label class="control-label" for="email">{label_email}</label>
<div class="controls">
<input type="text" name="email" value="{edit_value_email}" maxlength="255" />
<input type="text" id="email" name="email" value="{edit_value_email}" maxlength="255" required>
</div>
</div>
<div class="control-group">
<label class="control-label" for="name">{label_servers}</label>
<label class="control-label" for="servers[]">{label_servers}</label>
<div class="controls">
<!--%tpl_repeat_servers-->
<label class="checkbox">
@ -78,8 +111,8 @@
</div>
</div>
<div class="form-actions">
<button class="btn btn-success" type="submit">Save</button>
<button class="btn" onclick="history.back();return false;" >Cancel</button>
<button class="btn btn-success" type="submit">{label_save}</button>
<button class="btn" onclick="history.back();return false;" >{label_go_back}</button>
</div>
</fieldset>
</form>

View File

@ -1,8 +1,8 @@
body{
padding-top:70px;
}
}
body.install{
padding-top:20px;
padding-top:20px;
}
.label-status-on{
background-color: #468847;
@ -34,4 +34,29 @@ legend{
}
.form-actions {
background-color: transparent;
}
.form-signin {
max-width: 300px;
padding: 19px 29px 29px;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin input[type="text"],
.form-signin input[type="password"] {
font-size: 16px;
height: auto;
margin-bottom: 15px;
padding: 7px 9px;
}