From 29d5d36a7e31858a4ab2751110ccebd6383b79ee Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 14 Jul 2013 21:06:26 +0200 Subject: [PATCH 01/13] WiP for one time tokens * Added token type class * Storing Token Type as ID not varchar * Added new system to user class and fixed issues with it * Started on mail verification process in user class * Updated autoloader * Updated change password template Addresses #330 --- public/include/autoloader.inc.php | 2 + public/include/classes/base.class.php | 3 + public/include/classes/token.class.php | 60 +++++++++++++++++ public/include/classes/tokentype.class.php | 21 ++++++ public/include/classes/user.class.php | 64 ++++++++++--------- public/include/pages/password/change.inc.php | 4 +- public/include/pages/password/reset.inc.php | 2 +- .../mmcFE/password/change/default.tpl | 2 +- 8 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 public/include/classes/token.class.php create mode 100644 public/include/classes/tokentype.class.php diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 281b39c6..69a306bf 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -22,6 +22,8 @@ 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 . '/tokentype.class.php'); +require_once(CLASS_DIR . '/token.class.php'); require_once(CLASS_DIR . '/block.class.php'); require_once(CLASS_DIR . '/setting.class.php'); require_once(CLASS_DIR . '/monitoring.class.php'); diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php index 6b3c2389..316b245e 100644 --- a/public/include/classes/base.class.php +++ b/public/include/classes/base.class.php @@ -24,6 +24,9 @@ class Base { public function setConfig($config) { $this->config = $config; } + public function setTokenType($tokentype) { + $this->tokentype = $tokentype; + } public function setErrorMessage($msg) { $this->sError = $msg; } diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php new file mode 100644 index 00000000..fa472849 --- /dev/null +++ b/public/include/classes/token.class.php @@ -0,0 +1,60 @@ +mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return false; + } + + /** + * 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 + **/ + public function createToken($strType, $account_id=NULL) { + $strToken = hash('sha256', $account_id.$strType.microtime()); + if (!$iToken_id = $this->tokentype->getTypeId($strType)) { + $this->setErrorMessage('Invalid token type: ' . $strType); + return false; + } + $stmt = $this->mysqli->prepare(" + INSERT INTO $this->table (token, type, account_id) + VALUES (?, ?, ?) + "); + if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) + return $stmt->insert_id; + $this->setErrorMessage('Unable to create new token'); + $this->debug->append('Failed to create new token in database: ' . $this->mysqli->error); + return false; + } + + /** + * Delete a used token + * @param token string Token name + * @return bool + **/ + public function deleteToken($token) { + $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1"); + if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute()) + return true; + return false; + } +} + +$token = new Token(); +$token->setDebug($debug); +$token->setMysql($mysqli); +$token->setTokenType($tokentype); diff --git a/public/include/classes/tokentype.class.php b/public/include/classes/tokentype.class.php new file mode 100644 index 00000000..d33356cb --- /dev/null +++ b/public/include/classes/tokentype.class.php @@ -0,0 +1,21 @@ +getSingle($strName, 'id', 'name', 's'); + } +} + +$tokentype = new Token_Type(); +$tokentype->setDebug($debug); +$tokentype->setMysql($mysqli); diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index bdd87d1e..36b409c5 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -9,11 +9,11 @@ class User { private $userID = false; private $table = 'accounts'; private $user = array(); - private $tableAccountBalance = 'accountBalance'; - public function __construct($debug, $mysqli, $salt, $config) { + public function __construct($debug, $mysqli, $token, $salt, $config) { $this->debug = $debug; $this->mysqli = $mysqli; + $this->token = $token; $this->salt = $salt; $this->config = $config; $this->debug->append("Instantiated User class", 2); @@ -485,13 +485,13 @@ class User { } if ($this->mysqli->query("SELECT id FROM $this->table LIMIT 1")->num_rows > 0) { $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, pin, api_key) - VALUES (?, ?, ?, ?, ?) + INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) + VALUES (?, ?, ?, ?, ?, ?) "); } else { $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin) - VALUES (?, ?, ?, ?, ?, 1) + INSERT INTO $this->table (username, pass, email, pin, api_key, is_admin, is_locked) + VALUES (?, ?, ?, ?, ?, 1, 0) "); } @@ -501,14 +501,19 @@ class User { $apikey_hash = $this->getHash($username); $username_clean = strip_tags($username); - if ($this->checkStmt($stmt) && $stmt->bind_param('sssss', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash)) { - if (!$stmt->execute()) { - $this->setErrorMessage( 'Unable to register' ); - if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); - return false; + // + $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); + } else { + return true; } - $stmt->close(); - return true; + } else { + $this->setErrorMessage( 'Unable to register' ); + if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); + return false; } return false; } @@ -520,9 +525,9 @@ class User { * @param new2 string New password verification * @return bool **/ - public function useToken($token, $new1, $new2) { + public function resetPassword($token, $new1, $new2) { $this->debug->append("STA " . __METHOD__, 4); - if ($id = $this->getIdFromToken($token)) { + if ($token = $this->token->getToken($token)) { if ($new1 !== $new2) { $this->setErrorMessage( 'New passwords do not match' ); return false; @@ -532,14 +537,20 @@ class User { return false; } $new_hash = $this->getHash($new1); - $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ?, token = NULL WHERE id = ? AND token = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('sis', $new_hash, $id, $token) && $stmt->execute() && $stmt->affected_rows === 1) { - return true; + $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $token['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { + if ($this->token->deleteToken($token)) { + return true; + } else { + $this->setErrorMessage('Unable to invalidate used token'); + } + } else { + $this->setErrorMessage('Unable to set new password'); } } else { - $this->setErrorMessage("Unable find user for your token"); - return false; + $this->setErrorMessage('Unable find user for your token'); } + $this->debug->append('Failed to update password:' . $this->mysqli->error); return false; } @@ -549,7 +560,7 @@ class User { * @param smarty object Smarty object for mail templating * @return bool **/ - public function resetPassword($username, $smarty) { + public function initResetPassword($username, $smarty) { $this->debug->append("STA " . __METHOD__, 4); // Fetch the users mail address if (empty($username)) { @@ -560,16 +571,11 @@ class User { $this->setErrorMessage("Unable to find a mail address for user $username"); return false; } - if (!$this->setUserToken($this->getUserId($username))) { + if (!$token = $this->token->getToken($this->token->createToken('password_reset', $this->getUserId($username)))) { $this->setErrorMessage("Unable to setup token for password reset"); return false; } - // Send password reset link - if (!$token = $this->getUserToken($this->getUserId($username))) { - $this->setErrorMessage("Unable fetch token for password reset"); - return false; - } - $smarty->assign('TOKEN', $token); + $smarty->assign('TOKEN', $token['token']); $smarty->assign('USERNAME', $username); $smarty->assign('SUBJECT', 'Password Reset Request'); $smarty->assign('WEBSITENAME', $this->config['website']['name']); @@ -608,4 +614,4 @@ class User { } // Make our class available automatically -$user = new User($debug, $mysqli, SALT, $config); +$user = new User($debug, $mysqli, $token, SALT, $config); diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index 8b5f4064..b8115c69 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -4,8 +4,8 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($_POST['do'] == 'useToken') { - if ($user->useToken($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { +if ($_POST['do'] == 'resetPassword') { + if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.'); } else { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/include/pages/password/reset.inc.php b/public/include/pages/password/reset.inc.php index 796f5811..f7334fe8 100644 --- a/public/include/pages/password/reset.inc.php +++ b/public/include/pages/password/reset.inc.php @@ -5,7 +5,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); // Process password reset request -if ($user->resetPassword($_POST['username'], $smarty)) { +if ($user->initResetPassword($_POST['username'], $smarty)) { $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mail account to finish your password reset'); } else { $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/templates/mmcFE/password/change/default.tpl b/public/templates/mmcFE/password/change/default.tpl index 0d254677..ae3aab55 100644 --- a/public/templates/mmcFE/password/change/default.tpl +++ b/public/templates/mmcFE/password/change/default.tpl @@ -3,7 +3,7 @@ - + From 0ede05a6fd732e7efb801a513bbfabd96c1eebc1 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 14 Jul 2013 22:17:54 +0200 Subject: [PATCH 02/13] Adding email verification * Adding mail verification during account registration * Added new dist file option for mail verification * Added account confirmation page using tokens * Added mail class into user class for password resets * Moved password reset template * Adjusted account registration page * Adjusted user class for email confirmation Also fixed a bug with smarty_cache_key not being used properly if smarty is disabled. Key still needs to be available even if caching is disabled Addresses #330 and prepare the ticket for invitation only system. --- public/include/autoloader.inc.php | 2 +- public/include/classes/token.class.php | 12 ++-- public/include/classes/user.class.php | 64 +++++++++++-------- public/include/config/global.inc.dist.php | 17 +++++ public/include/pages/account/confirm.inc.php | 17 +++++ public/include/pages/password/change.inc.php | 2 +- .../include/pages/register/register.inc.php | 4 +- public/include/smarty.inc.php | 2 +- .../mail/{body.tpl => password/reset.tpl} | 4 +- .../templates/mail/register/confirm_email.tpl | 10 +++ .../mmcFE/account/confirm/default.tpl | 1 + sql/004_tokens.sql | 47 ++++++++++++++ 12 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 public/include/pages/account/confirm.inc.php rename public/templates/mail/{body.tpl => password/reset.tpl} (80%) create mode 100644 public/templates/mail/register/confirm_email.tpl create mode 100644 public/templates/mmcFE/account/confirm/default.tpl create mode 100644 sql/004_tokens.sql 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`; From be9a8d3fda49d79e5aa8a85cca6b4627b5688b6e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 09:05:44 +0200 Subject: [PATCH 03/13] Go through activiation even for admin accounts Fixes an issue with accounts being locked after trying to activate it. Addresses #330 --- public/include/classes/user.class.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 9927ac30..ef640098 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -421,7 +421,7 @@ class User { * @param userID int User ID * return data array Database fields as used in SELECT **/ - public function getUserData($userID) { + public function getUserData($userID) e $this->debug->append("STA " . __METHOD__, 4); $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" @@ -495,7 +495,6 @@ class User { 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, ?) From 7cc1e2543c71221c000429c1328185796fecda10 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 09:06:54 +0200 Subject: [PATCH 04/13] fixing syntax error --- public/include/classes/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index ef640098..52d19c44 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -421,7 +421,7 @@ class User { * @param userID int User ID * return data array Database fields as used in SELECT **/ - public function getUserData($userID) e + public function getUserData($userID) { $this->debug->append("STA " . __METHOD__, 4); $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" From bd32dfa9f8f6f3a9d7108d7185ea73e525948011 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:25:16 +0200 Subject: [PATCH 05/13] Adding invitation system to mmcfe-ng core This will allow users to send invitations to other people via email. Each account will still need to confirm the email address if the option is enabled. Addresses #330, will need to allow pool operators to enable this feature even with registrations turned off. --- public/include/autoloader.inc.php | 1 + public/include/classes/base.class.php | 6 + public/include/classes/invitation.class.php | 106 ++++++++++++++++++ public/include/classes/user.class.php | 23 +++- public/include/config/global.inc.dist.php | 17 ++- .../include/pages/account/invitations.inc.php | 25 +++++ .../include/pages/register/register.inc.php | 8 +- public/include/smarty_globals.inc.php | 1 + public/templates/mail/invitations/body.tpl | 11 ++ .../mmcFE/account/invitations/default.tpl | 43 +++++++ public/templates/mmcFE/global/navigation.tpl | 1 + public/templates/mmcFE/register/default.tpl | 3 + ..._tokens.sql => 004_tokens_invitations.sql} | 29 ++++- 13 files changed, 261 insertions(+), 13 deletions(-) create mode 100644 public/include/classes/invitation.class.php create mode 100644 public/include/pages/account/invitations.inc.php create mode 100644 public/templates/mail/invitations/body.tpl create mode 100644 public/templates/mmcFE/account/invitations/default.tpl rename sql/{004_tokens.sql => 004_tokens_invitations.sql} (60%) diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index bb765523..a5b21621 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -29,6 +29,7 @@ require_once(CLASS_DIR . '/block.class.php'); require_once(CLASS_DIR . '/setting.class.php'); require_once(CLASS_DIR . '/monitoring.class.php'); require_once(CLASS_DIR . '/user.class.php'); +require_once(CLASS_DIR . '/invitation.class.php'); require_once(CLASS_DIR . '/share.class.php'); require_once(CLASS_DIR . '/worker.class.php'); require_once(CLASS_DIR . '/statistics.class.php'); diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php index 316b245e..836ee3b3 100644 --- a/public/include/classes/base.class.php +++ b/public/include/classes/base.class.php @@ -15,6 +15,9 @@ class Base { public function setMysql($mysqli) { $this->mysqli = $mysqli; } + public function setMail($mail) { + $this->mail = $mail; + } public function setSmarty($smarty) { $this->smarty = $smarty; } @@ -24,6 +27,9 @@ class Base { public function setConfig($config) { $this->config = $config; } + public function setToken($token) { + $this->token = $token; + } public function setTokenType($tokentype) { $this->tokentype = $tokentype; } diff --git a/public/include/classes/invitation.class.php b/public/include/classes/invitation.class.php new file mode 100644 index 00000000..ef640420 --- /dev/null +++ b/public/include/classes/invitation.class.php @@ -0,0 +1,106 @@ +debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + $this->setErrorMessage('Unable to fetch invitiations send from your account'); + $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); + return false; + } + + public function getCountInvitations($account_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); + if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch()) + return $total; + $this->setErrorMessage('Unable to fetch invitiations send from your account'); + $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); + return false; + } + public function getByEmail($strEmail) { + $this->debug->append("STA " . __METHOD__, 4); + return $this->getSingle($strEmail, 'id', 'email', 's'); + } + + public function getByTokenId($token_id) { + $this->debug->append("STA " . __METHOD__, 4); + return $this->getSingle($token_id, 'id', 'token_id'); + } + public function setActivated($token_id) { + if (!$iInvitationId = $this->getByTokenId($token_id)) { + $this->setErrorMessage('Unable to convert token ID to invitation ID'); + return false; + } + $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); + return $this->updateSingle($iInvitationId, $field); + } + public function createInvitation($account_id, $email, $token_id) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); + if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute()) + return true; + return false; + } + public function sendInvitation($account_id, $aData) { + $this->debug->append("STA " . __METHOD__, 4); + // Check data input + if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) { + $this->setErrorMessage( 'Invalid e-mail address' ); + return false; + } + if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) { + $this->setErrorMessage('Message may only contain alphanumeric characters'); + return false; + } + // Ensure this invitation does not exist yet nor do we have an account with that email + if ($this->user->getEmail($aData['email'])) { + $this->setErrorMessage('This email is already registered as an account'); + return false; + } + if ($this->getByEmail($aData['email'])) { + $this->setErrorMessage('A pending invitation for this address already exists'); + return false; + } + if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) { + $this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError()); + return false; + } + $aData['username'] = $this->user->getUserName($account_id); + $aData['subject'] = 'Pending Invitation'; + if ($this->mail->sendMail('invitations/body', $aData)) { + $aToken = $this->token->getToken($aData['token']); + if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) { + $this->setErrorMessage('Unable to create invitation record'); + return false; + } + return true; + } else { + $this->setErrorMessage('Unable to send email to recipient'); + } + $this->setErrorMessage('Unable to send invitation'); + return false; + } +} + +$invitation = new invitation(); +$invitation->setDebug($debug); +$invitation->setMysql($mysqli); +$invitation->setMail($mail); +$invitation->setUser($user); +$invitation->setToken($oToken); +$invitation->setConfig($config); + +?> diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 52d19c44..64427486 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -454,7 +454,7 @@ class User { * @param email2 string Email confirmation * @return bool **/ - public function register($username, $password1, $password2, $pin, $email1='', $email2='') { + public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') { $this->debug->append("STA " . __METHOD__, 4); if (strlen($username > 40)) { $this->setErrorMessage('Username exceeding character limit'); @@ -488,8 +488,25 @@ class User { $this->setErrorMessage( 'Invalid PIN' ); return false; } + if (isset($strToken)) { + $aToken = $this->token->getToken($strToken); + // Circle dependency, so we create our own object here + $invitation = new Invitation(); + $invitation->setMysql($this->mysqli); + $invitation->setDebug($this->debug); + $invitation->setUser($this); + $invitation->setConfig($this->config); + if (!$invitation->setActivated($aToken['id'])) { + $this->setErrorMessage('Unable to activate your invitation'); + return false; + } + if (!$this->token->deleteToken($strToken)) { + $this->setErrorMessage('Unable to remove used token'); + 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; + $this->config['accounts']['confirm_email']['enabled'] ? $is_locked = 1 : $is_locked = 0; $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (username, pass, email, pin, api_key, is_locked) VALUES (?, ?, ?, ?, ?, ?) @@ -508,7 +525,7 @@ class User { $username_clean = strip_tags($username); if ($this->checkStmt($stmt) && $stmt->bind_param('sssssi', $username_clean, $password_hash, $email1, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { - if ($this->config['accounts']['confirm_email']) { + if ($this->config['accounts']['confirm_email']['enabled']) { if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { $aData['username'] = $username_clean; $aData['token'] = $token; diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 4c7595cc..deeba5b9 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -129,14 +129,27 @@ $config['website']['mobile_theme'] = 'mobile'; * By default, all newly created accounts will require an email verificaiton. * Only after acitivating an account the user will be able to login * + * Invitations will allow your users to invite new members to join the pool. + * After sending a mail to the invited user, they can register using the token + * created. Invitations can be enabled and disabled. They are listed on the accounts + * page. + * + * You can limit the number of registrations send per account via configuration + * variable. + * * Options: * confirm_email : Send confirmation mail to user after registration + * invitations : Enable or disable the invitation system + * count : Maximum invitations a user is able to send * * Defaults: * confirm_email : true - * + * invitations : true + * count : 5 **/ -$config['accounts']['confirm_email'] = true; +$config['accounts']['confirm_email']['enabled'] = true; +$config['accounts']['invitations']['enabled'] = true; +$config['accounts']['invitations']['count'] = 5; /** * Some basic access restrictions on some pages diff --git a/public/include/pages/account/invitations.inc.php b/public/include/pages/account/invitations.inc.php new file mode 100644 index 00000000..cd8d644a --- /dev/null +++ b/public/include/pages/account/invitations.inc.php @@ -0,0 +1,25 @@ +isAuthenticated()) { + if ($config['accounts']['invitations']['enabled']) { + if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) { + $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg'); + } else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') { + if ($invitation->sendInvitation($_SESSION['USERDATA']['id'], $_POST['data'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Invitation sent'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to send invitation to recipient: ' . $invitation->getError(), 'TYPE' => 'errormsg'); + } + } + $aInvitations = $invitation->getInvitations($_SESSION['USERDATA']['id']); + $smarty->assign('INVITATIONS', $aInvitations); + } else { + $aInvitations = array(); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invitations are disabled', 'TYPE' => 'errormsg'); + } +} +$smarty->assign('CONTENT', 'default.tpl'); +?> diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 526ef247..9a099816 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -19,8 +19,8 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); 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')) { - $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 if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { + $config['accounts']['confirm_email']['enabled'] ? $_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'); } @@ -36,8 +36,8 @@ if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_PO } else { 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')) { - $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 if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token']) && !$setting->getValue('lock_registration')) { + $config['accounts']['confirm_email']['enabled'] ? $_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_globals.inc.php b/public/include/smarty_globals.inc.php index a3a47e66..d0d5dbbf 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -48,6 +48,7 @@ $aGlobal = array( 'chaininfo' => $config['chaininfo'], 'config' => array( 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), + 'accounts' => $config['accounts'], 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], diff --git a/public/templates/mail/invitations/body.tpl b/public/templates/mail/invitations/body.tpl new file mode 100644 index 00000000..353b82e0 --- /dev/null +++ b/public/templates/mail/invitations/body.tpl @@ -0,0 +1,11 @@ + + +

Hello valued miner,


+

{$DATA.username} invited you to participate on this pool: +

http://{$smarty.server.SERVER_NAME}{$smarty.server.PHP_SELF}?page=register&token={$DATA.token}

+{if $DATA.message}

Personal message:

{$DATA.message}

{/if} +

+

Cheers,

+

Website Administration

+ + diff --git a/public/templates/mmcFE/account/invitations/default.tpl b/public/templates/mmcFE/account/invitations/default.tpl new file mode 100644 index 00000000..b8d6743a --- /dev/null +++ b/public/templates/mmcFE/account/invitations/default.tpl @@ -0,0 +1,43 @@ +{include file="global/block_header.tpl" ALIGN="left" BLOCK_HEADER="Invitations"} +
+ + + +
New Password:
New Password Repeat:
+ + + + + + + + + + + +
E-Mail
Message
+ +
+ +{include file="global/block_footer.tpl"} + +{include file="global/block_header.tpl" ALIGN="right" BLOCK_HEADER="Past Invitations"} + + + + + + + + + +{section name=invite loop=$INVITATIONS} + + + + + +{/section} + +
E-MailSentActivated
{$INVITATIONS[invite].email}{$INVITATIONS[invite].time|date_format:"%d/%m/%Y %H:%M:%S"}
+{include file="global/block_footer.tpl"} diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 42c89e9a..4d3d3621 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -7,6 +7,7 @@
  • My Workers
  • Transactions
  • Notifications
  • + {if $GLOBAL.config.accounts.invitations}
  • Invitations
  • {/if} {/if} diff --git a/public/templates/mmcFE/register/default.tpl b/public/templates/mmcFE/register/default.tpl index 3316dfa9..7253a243 100644 --- a/public/templates/mmcFE/register/default.tpl +++ b/public/templates/mmcFE/register/default.tpl @@ -1,6 +1,9 @@ {include file="global/block_header.tpl" BLOCK_HEADER="Join our pool" BLOCK_STYLE="clear:none;"}
    +{if $smarty.request.token|default:""} + +{/if} diff --git a/sql/004_tokens.sql b/sql/004_tokens_invitations.sql similarity index 60% rename from sql/004_tokens.sql rename to sql/004_tokens_invitations.sql index 439c3ab5..3ed5a8dc 100644 --- a/sql/004_tokens.sql +++ b/sql/004_tokens_invitations.sql @@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS `tokens` ( /*!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 SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; @@ -34,14 +34,35 @@ 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 ; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; INSERT INTO `token_types` (`id`, `name`) VALUES (1, 'password_reset'), -(2, 'confirm_email'); +(2, 'confirm_email'), +(3, 'invitation'); /*!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"; -ALTER TABLE `accounts` DROP `token`; +/*!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 `invitations` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `account_id` int(11) unsigned NOT NULL, + `email` varchar(50) CHARACTER SET utf8 NOT NULL, + `token_id` int(11) NOT NULL, + `is_activated` tinyint(1) NOT NULL DEFAULT '0', + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`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 */; From 7f4f5cd343c8481ab825948b12061563eb562d28 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:43:29 +0200 Subject: [PATCH 06/13] Make invitations configurable via admin panel Invitations can now be configured through admin panel settings. By default, invitations are enabled. Invitation system is also available if registrations are disabled. To completely remove the ability of new users to sign up, disable both registration and invitations. Fixes #330 --- public/include/config/global.inc.dist.php | 7 +-- .../include/pages/account/invitations.inc.php | 2 +- public/include/pages/admin/settings.inc.php | 1 + public/include/pages/register.inc.php | 2 +- .../include/pages/register/register.inc.php | 46 +++++++++---------- public/include/smarty_globals.inc.php | 1 + .../mmcFE/admin/settings/default.tpl | 10 ++++ public/templates/mmcFE/global/navigation.tpl | 2 +- 8 files changed, 40 insertions(+), 31 deletions(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index deeba5b9..e66197fe 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -131,24 +131,21 @@ $config['website']['mobile_theme'] = 'mobile'; * * Invitations will allow your users to invite new members to join the pool. * After sending a mail to the invited user, they can register using the token - * created. Invitations can be enabled and disabled. They are listed on the accounts - * page. + * created. Invitations can be enabled and disabled through the admin panel. + * Sent invitations are listed on the account invitations page. * * You can limit the number of registrations send per account via configuration * variable. * * Options: * confirm_email : Send confirmation mail to user after registration - * invitations : Enable or disable the invitation system * count : Maximum invitations a user is able to send * * Defaults: * confirm_email : true - * invitations : true * count : 5 **/ $config['accounts']['confirm_email']['enabled'] = true; -$config['accounts']['invitations']['enabled'] = true; $config['accounts']['invitations']['count'] = 5; /** diff --git a/public/include/pages/account/invitations.inc.php b/public/include/pages/account/invitations.inc.php index cd8d644a..b12e2d4e 100644 --- a/public/include/pages/account/invitations.inc.php +++ b/public/include/pages/account/invitations.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { - if ($config['accounts']['invitations']['enabled']) { + if (!$setting->getValue('disable_invitations')) { if ($invitation->getCountInvitations($_SESSION['USERDATA']['id']) >= $config['accounts']['invitations']['count']) { $_SESSION['POPUP'][] = array('CONTENT' => 'You have exceeded the allowed invitations of ' . $config['accounts']['invitations']['count'], 'TYPE' => 'errormsg'); } else if (isset($_POST['do']) && $_POST['do'] == 'sendInvitation') { diff --git a/public/include/pages/admin/settings.inc.php b/public/include/pages/admin/settings.inc.php index dab1f096..9bc7ef3b 100644 --- a/public/include/pages/admin/settings.inc.php +++ b/public/include/pages/admin/settings.inc.php @@ -19,6 +19,7 @@ if (@$_REQUEST['do'] == 'save' && !empty($_REQUEST['data'])) { // Fetch settings to propagate to template $smarty->assign("MAINTENANCE", $setting->getValue('maintenance')); $smarty->assign("LOCKREGISTRATION", $setting->getValue('lock_registration')); +$smarty->assign("DISABLEINVITATIONS", $setting->getValue('disable_invitations')); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index d47c67ed..9b338b51 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -3,7 +3,7 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($setting->getValue('lock_registration')) { +if ($setting->getValue('lock_registration') && !$config['accounts']['invitations']['enabled']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); } else { diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 9a099816..9e438853 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -13,33 +13,33 @@ if ($config['recaptcha']['enabled']) { ); } -// Check if recaptcha is enabled, process form data if valid -if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ - if ($rsp->is_valid) { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); - 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'], $_POST['token']) && !$setting->getValue('lock_registration')) { +if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); +} else { + // Check if recaptcha is enabled, process form data if valid + if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ + if ($rsp->is_valid) { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + $config['accounts']['confirm_email']['enabled'] ? $_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'); + } + } else { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); + $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg'); + } + // Empty captcha + } else if ($config['recaptcha']['enabled']) { + $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); + $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); + // Captcha disabled + } else { + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { $config['accounts']['confirm_email']['enabled'] ? $_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'); } - } else { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); - $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid Captcha, please try again. (' . $rsp->error . ')', 'TYPE' => 'errormsg'); - } -// Empty captcha -} else if ($config['recaptcha']['enabled']) { - $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'], $rsp->error)); - $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); -// Captcha disabled -} else { - 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'], $_POST['token']) && !$setting->getValue('lock_registration')) { - $config['accounts']['confirm_email']['enabled'] ? $_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_globals.inc.php b/public/include/smarty_globals.inc.php index d0d5dbbf..5f6ca959 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -49,6 +49,7 @@ $aGlobal = array( 'config' => array( 'website' => array( 'title' => $config['website']['title'], 'acl' => $config['website']['acl'] ), 'accounts' => $config['accounts'], + 'disable_invitations' => $setting->getValue('disable_invitations'), 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], 'currency' => $config['currency'], diff --git a/public/templates/mmcFE/admin/settings/default.tpl b/public/templates/mmcFE/admin/settings/default.tpl index a2ffbd5b..154c39d1 100644 --- a/public/templates/mmcFE/admin/settings/default.tpl +++ b/public/templates/mmcFE/admin/settings/default.tpl @@ -30,6 +30,16 @@ + + + + +
    Disable Invitations + +
    diff --git a/public/templates/mmcFE/global/navigation.tpl b/public/templates/mmcFE/global/navigation.tpl index 4d3d3621..79d36af8 100644 --- a/public/templates/mmcFE/global/navigation.tpl +++ b/public/templates/mmcFE/global/navigation.tpl @@ -7,7 +7,7 @@
  • My Workers
  • Transactions
  • Notifications
  • - {if $GLOBAL.config.accounts.invitations}
  • Invitations
  • {/if} + {if !$GLOBAL.config.disable_invitations}
  • Invitations
  • {/if} {/if} From bf3cd25326eb054767de77f96f6ba8768205ade4 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 12:52:55 +0200 Subject: [PATCH 07/13] removing unused token methods --- public/include/classes/user.class.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 64427486..8c108dbd 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -49,9 +49,6 @@ class User { public function getUserLocked($id) { return $this->getSingle($id, 'is_locked', 'id'); } - public function getUserToken($id) { - return $this->getSingle($id, 'token', 'id'); - } public function getUserIp($id) { return $this->getSingle($id, 'loggedIp', 'id'); } @@ -61,9 +58,6 @@ class User { public function getUserFailed($id) { return $this->getSingle($id, 'failed_logins', 'id'); } - public function getIdFromToken($token) { - return $this->getSingle($token, 'id', 'token', 's'); - } public function isLocked($id) { return $this->getUserLocked($id); } @@ -78,10 +72,6 @@ class User { $field = array('name' => 'is_admin', 'type' => 'i', 'value' => !$this->isAdmin($id)); return $this->updateSingle($id, $field); } - public function setUserToken($id) { - $field = array('name' => 'token', 'type' => 's', 'value' => setHash($id.time())); - return $this->updateSingle($id, $field); - } public function setUserFailed($id, $value) { $field = array( 'name' => 'failed_logins', 'type' => 'i', 'value' => $value); return $this->updateSingle($id, $field); From 8f720625585d25d3f6dcd2cb71d93f1eef987d2d Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 13:44:22 +0200 Subject: [PATCH 08/13] Fixing issue with registration disabled and invitations This will fix an issue with certain combinations of registration and/or invitations being enabled or disabled. Addresses #330 --- public/include/pages/register.inc.php | 5 ++++- public/include/pages/register/register.inc.php | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index 9b338b51..2866a4b6 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -3,9 +3,12 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -if ($setting->getValue('lock_registration') && !$config['accounts']['invitations']['enabled']) { +if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); +} else if (!$setting->getValue('disable_invitations') && !isset($_GET['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); + $smarty->assign("CONTENT", "disabled.tpl"); } else { if ($config['recaptcha']['enabled']) { require_once(INCLUDE_DIR . '/lib/recaptchalib.php'); diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 9e438853..560961f6 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -15,6 +15,8 @@ if ($config['recaptcha']['enabled']) { if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); +} else if (!$setting->getValue('disable_invitations') && !isset($_POST['token'])) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); } else { // Check if recaptcha is enabled, process form data if valid if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ From 0f00f7d322826ecfc2f64e77f377f9031cec1275 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 13:46:20 +0200 Subject: [PATCH 09/13] Another fix for registration issues Addresses #330 --- public/include/pages/register.inc.php | 2 +- public/include/pages/register/register.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/include/pages/register.inc.php b/public/include/pages/register.inc.php index 2866a4b6..01b71b18 100644 --- a/public/include/pages/register.inc.php +++ b/public/include/pages/register.inc.php @@ -6,7 +6,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($setting->getValue('lock_registration') && $setting->getValue('disable_invitations')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); -} else if (!$setting->getValue('disable_invitations') && !isset($_GET['token'])) { +} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_GET['token'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); $smarty->assign("CONTENT", "disabled.tpl"); } else { diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index 560961f6..ab43ba34 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -15,7 +15,7 @@ if ($config['recaptcha']['enabled']) { if ($setting->getValue('disable_invitations') && $setting->getValue('lock_registration')) { $_SESSION['POPUP'][] = array('CONTENT' => 'Account registration is currently disabled. Please try again later.', 'TYPE' => 'errormsg'); -} else if (!$setting->getValue('disable_invitations') && !isset($_POST['token'])) { +} else if ($setting->getValue('lock_registration') && !$setting->getValue('disable_invitations') && !isset($_POST['token'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Only invited users are allowed to register.', 'TYPE' => 'errormsg'); } else { // Check if recaptcha is enabled, process form data if valid From 525c0ab0090a67273b455ed846a942a100ec07a5 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 14:28:54 +0200 Subject: [PATCH 10/13] Fixing PHP Warning on unsert token on register Addresses #330 and cleans up PHP Log --- public/include/classes/user.class.php | 2 +- public/include/pages/register/register.inc.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 8c108dbd..6d7a6d27 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -478,7 +478,7 @@ class User { $this->setErrorMessage( 'Invalid PIN' ); return false; } - if (isset($strToken)) { + if (isset($strToken) && !empty($strToken)) { $aToken = $this->token->getToken($strToken); // Circle dependency, so we create our own object here $invitation = new Invitation(); diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index ab43ba34..ca165e3b 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -22,7 +22,8 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist if($config['recaptcha']['enabled'] && $_POST["recaptcha_response_field"] && $_POST["recaptcha_response_field"]!=''){ if ($rsp->is_valid) { $smarty->assign("RECAPTCHA", recaptcha_get_html($config['recaptcha']['public_key'])); - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { $config['accounts']['confirm_email']['enabled'] ? $_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 +38,8 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist $_SESSION['POPUP'][] = array('CONTENT' => 'Empty Captcha, please try again.', 'TYPE' => 'errormsg'); // Captcha disabled } else { - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $_POST['token'])) { + isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; + if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { $config['accounts']['confirm_email']['enabled'] ? $_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'); From 501f369b4e570d64776be3342edee783560b2e88 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 15:31:18 +0200 Subject: [PATCH 11/13] Further fixes to PHP warnings Addresses #330 and further cleans up the PHP log. --- public/include/classes/user.class.php | 6 +++--- public/include/pages/password/change.inc.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 6d7a6d27..9d448a40 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -552,7 +552,7 @@ class User { **/ public function resetPassword($token, $new1, $new2) { $this->debug->append("STA " . __METHOD__, 4); - if ($token = $this->token->getToken($token)) { + if ($aToken = $this->token->getToken($token)) { if ($new1 !== $new2) { $this->setErrorMessage( 'New passwords do not match' ); return false; @@ -563,8 +563,8 @@ class User { } $new_hash = $this->getHash($new1); $stmt = $this->mysqli->prepare("UPDATE $this->table SET pass = ? WHERE id = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $token['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { - if ($this->token->deleteToken($token)) { + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $new_hash, $aToken['account_id']) && $stmt->execute() && $stmt->affected_rows === 1) { + if ($this->token->deleteToken($aToken['token'])) { return true; } else { $this->setErrorMessage('Unable to invalidate used token'); diff --git a/public/include/pages/password/change.inc.php b/public/include/pages/password/change.inc.php index b45b3ee4..919632bd 100644 --- a/public/include/pages/password/change.inc.php +++ b/public/include/pages/password/change.inc.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); -if ($_POST['do'] == 'resetPassword') { +if (isset($_POST['do']) && $_POST['do'] == 'resetPassword') { if ($user->resetPassword($_POST['token'], $_POST['newPassword'], $_POST['newPassword2'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Password reset complete! Please login.'); } else { From 7b929ed3e4b97ec92a5f84c5f42eaae6697358c6 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 16:16:40 +0200 Subject: [PATCH 12/13] Fixing PHP Warning on resetting failed login count Addresses #330 and further cleans PHP Log --- public/include/pages/account/reset_failed.inc.php | 3 ++- public/templates/mmcFE/global/empty.tpl | 0 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 public/templates/mmcFE/global/empty.tpl diff --git a/public/include/pages/account/reset_failed.inc.php b/public/include/pages/account/reset_failed.inc.php index bce9b418..39541dc2 100644 --- a/public/include/pages/account/reset_failed.inc.php +++ b/public/include/pages/account/reset_failed.inc.php @@ -8,5 +8,6 @@ if ($user->isAuthenticated()) { $user->setUserFailed($_SESSION['USERDATA']['id'], 0); header("Location: " . $_SERVER['HTTP_REFERER']); } - +// Somehow we still need to load this empty template +$smarty->assign("CONTENT", "../../global/empty.tpl"); ?> diff --git a/public/templates/mmcFE/global/empty.tpl b/public/templates/mmcFE/global/empty.tpl new file mode 100644 index 00000000..e69de29b From 41ec58ea168a4b8aa6eb2a939901df8c3032051a Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 15 Jul 2013 16:28:22 +0200 Subject: [PATCH 13/13] Adding inline docuemtation to invitation class Adding proper inline documentation to invitation class. --- public/include/classes/invitation.class.php | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/public/include/classes/invitation.class.php b/public/include/classes/invitation.class.php index ef640420..822dfef3 100644 --- a/public/include/classes/invitation.class.php +++ b/public/include/classes/invitation.class.php @@ -21,6 +21,11 @@ class Invitation extends Base { return false; } + /** + * Count invitations sent by an account_id + * @param account_id integer Account ID + * @return mixes Integer on success, boolean on failure + **/ public function getCountInvitations($account_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); @@ -30,15 +35,34 @@ class Invitation extends Base { $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); return false; } + + /** + * Get a specific invitation by email address + * Used to ensure no invitation was already sent + * @param strEmail string Email address to check for + * @return bool boolean true of ralse + **/ public function getByEmail($strEmail) { $this->debug->append("STA " . __METHOD__, 4); return $this->getSingle($strEmail, 'id', 'email', 's'); } + /** + * Get a specific token by token ID + * Used to match an invitation against a token + * @param token_id integer Token ID stored in invitation + * @return data mixed Invitation ID on success, false on error + **/ public function getByTokenId($token_id) { $this->debug->append("STA " . __METHOD__, 4); return $this->getSingle($token_id, 'id', 'token_id'); } + + /** + * Set an invitation as activated by the invitee + * @param token_id integer Token to activate + * @return bool boolean true or false + **/ public function setActivated($token_id) { if (!$iInvitationId = $this->getByTokenId($token_id)) { $this->setErrorMessage('Unable to convert token ID to invitation ID'); @@ -47,6 +71,14 @@ class Invitation extends Base { $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); return $this->updateSingle($iInvitationId, $field); } + + /** + * Insert a new invitation to the database + * @param account_id integer Account ID to bind the invitation to + * @param email string Email address the invite was sent to + * @param token_id integer Token ID used during invitation + * @return bool boolean True of false + **/ public function createInvitation($account_id, $email, $token_id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); @@ -54,6 +86,13 @@ class Invitation extends Base { return true; return false; } + /** + * Send an invitation out to a user + * Uses the mail class to send mails + * @param account_id integer Sending account ID + * @param aData array Data array including mail information + * @return bool boolean True or false + **/ public function sendInvitation($account_id, $aData) { $this->debug->append("STA " . __METHOD__, 4); // Check data input @@ -95,6 +134,7 @@ class Invitation extends Base { } } +// Instantiate class $invitation = new invitation(); $invitation->setDebug($debug); $invitation->setMysql($mysqli);