diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 69a306bf..bb765523 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -22,6 +22,7 @@ require_once(INCLUDE_DIR . '/database.inc.php'); require_once(INCLUDE_DIR . '/smarty.inc.php'); // Load classes that need the above as dependencies require_once(CLASS_DIR . '/base.class.php'); +require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/tokentype.class.php'); require_once(CLASS_DIR . '/token.class.php'); require_once(CLASS_DIR . '/block.class.php'); @@ -32,7 +33,6 @@ require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/worker.class.php'); require_once(CLASS_DIR . '/statistics.class.php'); require_once(CLASS_DIR . '/transaction.class.php'); -require_once(CLASS_DIR . '/mail.class.php'); require_once(CLASS_DIR . '/notification.class.php'); require_once(CLASS_DIR . '/news.class.php'); require_once(INCLUDE_DIR . '/lib/Michelf/Markdown.php'); diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php index fa472849..c65da13c 100644 --- a/public/include/classes/token.class.php +++ b/public/include/classes/token.class.php @@ -22,7 +22,7 @@ class Token Extends Base { * Insert a new token * @param name string Name of the variable * @param value string Variable value - * @return mixed Insert ID on success, false on failure + * @return mixed Token string on success, false on failure **/ public function createToken($strType, $account_id=NULL) { $strToken = hash('sha256', $account_id.$strType.microtime()); @@ -35,7 +35,7 @@ class Token Extends Base { VALUES (?, ?, ?) "); if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) - return $stmt->insert_id; + return $strToken; $this->setErrorMessage('Unable to create new token'); $this->debug->append('Failed to create new token in database: ' . $this->mysqli->error); return false; @@ -54,7 +54,7 @@ class Token Extends Base { } } -$token = new Token(); -$token->setDebug($debug); -$token->setMysql($mysqli); -$token->setTokenType($tokentype); +$oToken = new Token(); +$oToken->setDebug($debug); +$oToken->setMysql($mysqli); +$oToken->setTokenType($tokentype); diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 36b409c5..9927ac30 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -10,16 +10,21 @@ class User { private $table = 'accounts'; private $user = array(); - public function __construct($debug, $mysqli, $token, $salt, $config) { + public function __construct($debug, $mysqli, $salt, $config) { $this->debug = $debug; $this->mysqli = $mysqli; - $this->token = $token; $this->salt = $salt; $this->config = $config; $this->debug->append("Instantiated User class", 2); } // get and set methods + public function setMail($mail) { + $this->mail = $mail; + } + public function setToken($token) { + $this->token= $token; + } private function setErrorMessage($msg) { $this->sError = $msg; } @@ -484,14 +489,16 @@ class User { return false; } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { + $this->config['accounts']['confirm_email'] ? $is_locked = 1 : $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) VALUES (?, ?, ?, ?, ?, ?) "); } else { + $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) - VALUES (?, ?, ?, ?, ?, 1, 0) + VALUES (?, ?, ?, ?, ?, 1, ?) "); } @@ -501,17 +508,29 @@ class User { $apikey_hash = $this->getHash($username); $username_clean = strip_tags($username); - // - $this->config['confirm_email'] ? $is_locked = 1 : $is_locked = 0; - if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { - if ($this->config['confirm_email']) { - $this->token->createToken('confirm_email', $stmt->insert_id); + if ($this->config['accounts']['confirm_email']) { + if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { + $aData['username'] = $username_clean; + $aData['token'] = $token; + $aData['email'] = $email1; + $aData['subject'] = 'E-Mail verification'; + if (!$this->mail->sendMail('register/confirm_email', $aData)) { + $this->setErrorMessage('Unable to request email confirmation'); + return false; + } + return true; + } else { + $this->setErrorMessage('Failed to create confirmation token'); + $this->debug->append('Unable to create confirm_email token: ' . $this->token->getError()); + return false; + } } else { return true; } } else { $this->setErrorMessage( 'Unable to register' ); + $this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error); if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); return false; } @@ -548,7 +567,7 @@ class User { $this->setErrorMessage('Unable to set new password'); } } else { - $this->setErrorMessage('Unable find user for your token'); + $this->setErrorMessage('Invalid token'); } $this->debug->append('Failed to update password:' . $this->mysqli->error); return false; @@ -557,35 +576,26 @@ class User { /** * Reset a password by sending a password reset mail * @param username string Username to reset password for - * @param smarty object Smarty object for mail templating * @return bool **/ - public function initResetPassword($username, $smarty) { + public function initResetPassword($username) { $this->debug->append("STA " . __METHOD__, 4); // Fetch the users mail address if (empty($username)) { $this->serErrorMessage("Username must not be empty"); return false; } - if (!$email = $this->getUserEmail($username)) { + if (!$aData['email'] = $this->getUserEmail($username)) { $this->setErrorMessage("Unable to find a mail address for user $username"); return false; } - if (!$token = $this->token->getToken($this->token->createToken('password_reset', $this->getUserId($username)))) { - $this->setErrorMessage("Unable to setup token for password reset"); + if (!$aData['token'] = $this->token->createToken('password_reset', $this->getUserId($username))) { + $this->setErrorMessage('Unable to setup token for password reset'); return false; } - $smarty->assign('TOKEN', $token['token']); - $smarty->assign('USERNAME', $username); - $smarty->assign('SUBJECT', 'Password Reset Request'); - $smarty->assign('WEBSITENAME', $this->config['website']['name']); - $headers = 'From: Website Administration <' . $this->config['website']['email'] . ">\n"; - $headers .= "MIME-Version: 1.0\n"; - $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n"; - if (mail($email, - $smarty->fetch('templates/mail/subject.tpl'), - $smarty->fetch('templates/mail/body.tpl'), - $headers)) { + $aData['username'] = $username; + $aData['subject'] = 'Password Reset Request'; + if ($this->mail->sendMail('password/reset', $aData)) { return true; } else { $this->setErrorMessage("Unable to send mail to your address"); @@ -614,4 +624,6 @@ class User { } // Make our class available automatically -$user = new User($debug, $mysqli, $token, SALT, $config); +$user = new User($debug, $mysqli, SALT, $config); +$user->setMail($mail); +$user->setToken($oToken); diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 25667017..4c7595cc 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -121,6 +121,23 @@ $config['website']['theme'] = 'mmcFE'; $config['website']['mobile'] = true; $config['website']['mobile_theme'] = 'mobile'; +/** + * Account specific settings + * + * Explanation + * You can change some defaults on how accounts are created or registered + * By default, all newly created accounts will require an email verificaiton. + * Only after acitivating an account the user will be able to login + * + * Options: + * confirm_email : Send confirmation mail to user after registration + * + * Defaults: + * confirm_email : true + * + **/ +$config['accounts']['confirm_email'] = true; + /** * Some basic access restrictions on some pages * diff --git a/public/include/pages/account/confirm.inc.php b/public/include/pages/account/confirm.inc.php new file mode 100644 index 00000000..3611c5de --- /dev/null +++ b/public/include/pages/account/confirm.inc.php @@ -0,0 +1,17 @@ + 'Missing token', 'TYPE' => 'errormsg'); +} else if (!$aToken = $oToken->getToken($_GET['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to activate your account. Invalid token', 'TYPE' => 'errormsg'); +} else { + $user->changeLocked($aToken['account_id']); + $oToken->deleteToken($aToken['token']); + $_SESSION['POPUP'][] = array('CONTENT' => 'Account activated. Please login.'); +} +$smarty->assign('CONTENT', 'default.tpl'); +?> diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index b8115c69..b45b3ee4 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -11,7 +11,7 @@ if ($_POST['do'] == 'resetPassword') { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); } } - // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); + ?> diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 01a27e10..526ef247 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -20,7 +20,7 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } @@ -37,7 +37,7 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO if ($setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); } else if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2']) && !$setting->getValue('lock_registration')) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); + $config['accounts']['confirm_email'] ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); } diff --git a/public/include/smarty.inc.php b/public/include/smarty.inc.php index f95180d7..dfd1f4f6 100644 --- a/public/include/smarty.inc.php +++ b/public/include/smarty.inc.php @@ -18,6 +18,7 @@ $smarty = new Smarty; $debug->append('Define Smarty Paths', 3); $smarty->template_dir = BASEPATH . 'templates/' . THEME . '/'; $smarty->compile_dir = BASEPATH . 'templates/compile/'; +$smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id'])); // Optional smarty caching, check Smarty documentation for details if ($config['smarty']['cache']) { @@ -26,6 +27,5 @@ if ($config['smarty']['cache']) { $smarty->cache_lifetime = $config['smarty']['cache_lifetime']; $smarty->cache_dir = BASEPATH . "templates/cache"; $smarty->use_sub_dirs = true; - $smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id'])); } ?> diff --git a/public/templates/mail/body.tpl b/public/templates/mail/password/reset.tpl similarity index 80% rename from public/templates/mail/body.tpl rename to public/templates/mail/password/reset.tpl index a80b579a..fe488dca 100644 --- a/public/templates/mail/body.tpl +++ b/public/templates/mail/password/reset.tpl @@ -1,8 +1,8 @@
-Hello {$USERNAME},
Hello {$DATA.username},
You have requested a password reset through our online form. In order to complete the request please follow this link:
-http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$TOKEN}
+http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=password&action=change&token={$DATA.token}
You will be asked to change your password. You can then use this new password to login to your account.
Cheers,
Website Administration
diff --git a/public/templates/mail/register/confirm_email.tpl b/public/templates/mail/register/confirm_email.tpl new file mode 100644 index 00000000..67ea72c5 --- /dev/null +++ b/public/templates/mail/register/confirm_email.tpl @@ -0,0 +1,10 @@ + + +Hello {$DATA.username},
You have create a new account. In order to complete the registration process please follow this link:
+http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=account&action=confirm&token={$DATA.token}
+ +Cheers,
+Website Administration
+ + diff --git a/public/templates/mmcFE/account/confirm/default.tpl b/public/templates/mmcFE/account/confirm/default.tpl new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/public/templates/mmcFE/account/confirm/default.tpl @@ -0,0 +1 @@ + diff --git a/sql/004_tokens.sql b/sql/004_tokens.sql new file mode 100644 index 00000000..439c3ab5 --- /dev/null +++ b/sql/004_tokens.sql @@ -0,0 +1,47 @@ +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `tokens` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `account_id` int(11) NOT NULL, + `token` varchar(65) NOT NULL, + `type` tinyint(4) NOT NULL, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `token` (`token`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + + +CREATE TABLE IF NOT EXISTS `token_types` ( + `id` tinyint(4) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(25) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; + +INSERT INTO `token_types` (`id`, `name`) VALUES +(1, 'password_reset'), +(2, 'confirm_email'); + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; + +ALTER TABLE `accounts` DROP `token`;