diff --git a/public/include/classes/payout.class.php b/public/include/classes/payout.class.php index 02c7ae84..c8298668 100644 --- a/public/include/classes/payout.class.php +++ b/public/include/classes/payout.class.php @@ -37,29 +37,14 @@ class Payout Extends Base { * @return data mixed Inserted ID or false **/ public function createPayout($account_id=NULL, $strToken) { - // twofactor - if cashout enabled we need to create/check the token + // twofactor - consume the token if it is enabled and valid if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['withdraw']) { - $tData = $this->token->getToken($strToken, 'withdraw_funds'); - $tExists = $this->token->doesTokenExist('withdraw_funds', $account_id); - if (!is_array($tData) && $tExists == false) { - // token doesn't exist, let's create one, send an email with a link to use it, and error out - $token = $this->token->createToken('withdraw_funds', $account_id); - $aData['token'] = $token; - $aData['username'] = $this->getUserName($account_id); - $aData['email'] = $this->getUserEmail($aData['username']); - $aData['subject'] = 'Manual payout request confirmation'; - $this->mail->sendMail('notifications/withdraw_funds', $aData); - $this->setErrorMessage("A confirmation has been sent to your e-mail"); - return true; + $tValid = $this->token->isTokenValid($account_id, $strToken, 7); + if ($tValid) { + $this->token->deleteToken($strToken); } else { - // already exists, if it's valid delete it and allow this edit - if ($strToken === $tData['token']) { - $this->token->deleteToken($tData['token']); - } else { - // token exists for this type, but this is not the right token - $this->setErrorMessage("A confirmation was sent to your e-mail, follow that link to cash out"); - return false; - } + $this->setErrorMessage('Invalid token'); + return false; } } $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id) VALUES (?)"); @@ -86,7 +71,6 @@ $oPayout = new Payout(); $oPayout->setDebug($debug); $oPayout->setMysql($mysqli); $oPayout->setConfig($config); -$oPayout->setMail($mail); $oPayout->setToken($oToken); $oPayout->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php index ca68ebc7..bb947005 100644 --- a/public/include/classes/token.class.php +++ b/public/include/classes/token.class.php @@ -22,6 +22,20 @@ class Token Extends Base { return $this->sqlError(); } + /** + * Check if a token we're passing in is completely valid + * @param account_id int Account id of user + * @param token string Token to check + * @param type int Type of token + * @return int 0 or 1 + */ + public function isTokenValid($account_id, $token, $type) { + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? AND token = ? AND type = ? AND time < NOW() LIMIT 1"); + if ($stmt && $stmt->bind_param('isi', $account_id, $token, $type) && $stmt->execute()) + return $stmt->get_result()->num_rows; + return $this->sqlError(); + } + /** * Check if a token of this type already exists for a given account_id * @param strType string Name of the type of token @@ -35,7 +49,7 @@ class Token Extends Base { } $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? AND type = ? LIMIT 1"); if ($stmt && $stmt->bind_param('ii', $account_id, $iToken_id) && $stmt->execute()) - return $stmt->num_rows; + return $stmt->get_result()->num_rows; return $this->sqlError(); } diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index 99275636..7e36b54a 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -255,6 +255,43 @@ class User extends Base { return $dPercent; } + /** + * Send e-mail to confirm a change for 2fa + * @param strType string Token type name + * @param userID int User ID + * @return bool + */ + public function sendChangeConf($strType, $userID) { + $exists = $this->token->doesTokenExist($strType, $userID); + if ($exists == 0) { + $token = $this->token->createToken($strType, $userID); + $aData['token'] = $token; + $aData['username'] = $this->getUserName($userID); + $aData['email'] = $this->getUserEmail($aData['username']); + switch ($strType) { + case 'account_edit': + $aData['subject'] = 'Account detail change confirmation'; + break; + case 'change_pw': + $aData['subject'] = 'Account password change confirmation'; + break; + case 'withdraw_funds': + $aData['subject'] = 'Manual payout request confirmation'; + break; + default: + $aData['subject'] = ''; + } + if ($this->mail->sendMail('notifications/'.$strType, $aData)) { + return true; + } else { + $this->setErrorMessage('Failed to send the notification'); + return false; + } + } + $this->setErrorMessage('A request has already been sent to your e-mail address. Please wait 10 minutes for it to expire.'); + return false; + } + /** * Update the accounts password * @param userID int User ID @@ -274,29 +311,14 @@ class User extends Base { $this->setErrorMessage( 'New password is too short, please use more than 8 chars' ); return false; } - // twofactor - if changepw is enabled we need to create/check the token + // twofactor - consume the token if it is enabled and valid if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['changepw']) { - $tData = $this->token->getToken($strToken, 'change_pw'); - $tExists = $this->token->doesTokenExist('change_pw', $userID); - if (!is_array($tData) && $tExists == false) { - // token doesn't exist, let's create one, send an email with a link to use it, and error out - $token = $this->token->createToken('change_pw', $userID); - $aData['token'] = $token; - $aData['username'] = $this->getUserName($userID); - $aData['email'] = $this->getUserEmail($aData['username']); - $aData['subject'] = 'Account password change confirmation'; - $this->mail->sendMail('notifications/change_pw', $aData); - $this->setErrorMessage("A confirmation has been sent to your e-mail"); - return true; + $tValid = $this->token->isTokenValid($userID, $strToken, 6); + if ($tValid) { + $this->token->deleteToken($strToken); } else { - // already exists, if it's valid delete it and allow this edit - if ($strToken === $tData['token']) { - $this->token->deleteToken($tData['token']); - } else { - // token exists for this type, but this is not the right token - $this->setErrorMessage("A confirmation was sent to your e-mail, follow that link to change your password"); - return false; - } + $this->setErrorMessage('Invalid token'); + return false; } } $current = $this->getHash($current); @@ -313,7 +335,7 @@ class User extends Base { $this->setErrorMessage( 'Unable to update password, current password wrong?' ); return false; } - + /** * Update account information from the edit account page * @param userID int User ID @@ -373,29 +395,14 @@ class User extends Base { $threshold = min($this->config['ap_threshold']['max'], max(0, floatval($threshold))); $donate = min(100, max(0, floatval($donate))); - // twofactor - if details enabled we need to create/check the token + // twofactor - consume the token if it is enabled and valid if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['details']) { - $tData = $this->token->getToken($strToken, 'account_edit'); - $tExists = $this->token->doesTokenExist('account_edit', $userID); - if (!is_array($tData) && $tExists == false) { - // token doesn't exist, let's create one, send an email with a link to use it, and error out - $token = $this->token->createToken('account_edit', $userID); - $aData['token'] = $token; - $aData['username'] = $this->getUserName($userID); - $aData['email'] = $this->getUserEmail($aData['username']); - $aData['subject'] = 'Account detail change confirmation'; - $this->mail->sendMail('notifications/account_edit', $aData); - $this->setErrorMessage("A confirmation has been sent to your e-mail"); - return true; + $tValid = $this->token->isTokenValid($userID, $strToken, 5); + if ($tValid) { + $this->token->deleteToken($strToken); } else { - // already exists, if it's valid delete it and allow this edit - if ($strToken === $tData['token']) { - $this->token->deleteToken($tData['token']); - } else { - // token exists for this type, but this is not the right token - $this->setErrorMessage("A confirmation was sent to your e-mail, follow that link to edit your account details"); - return false; - } + $this->setErrorMessage('Invalid token'); + return false; } } diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 07c20eb0..13daf7da 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -7,7 +7,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); * This is used in the version check to ensure you run the latest version of the configuration file. * Once you upgraded your config, change the version here too. **/ -$config['version'] = '0.0.3'; +$config['version'] = '0.0.4'; // Our include directory for additional features define('INCLUDE_DIR', BASEPATH . 'include'); diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php index b69baebd..95bbf74f 100644 --- a/public/include/pages/account/edit.inc.php +++ b/public/include/pages/account/edit.inc.php @@ -4,7 +4,44 @@ if (!defined('SECURITY')) die('Hacking attempt'); +// 2fa tpl stuff +$cp_editable = $wf_editable = $ea_editable = $wf_sent = $ea_sent = $cp_sent = 0; +$ea_token = (!isset($_GET['ea_token'])) ? '' : $_GET['ea_token']; +$cp_token = (!isset($_GET['cp_token'])) ? '' : $_GET['cp_token']; +$wf_token = (!isset($_GET['wf_token'])) ? '' : $_GET['wf_token']; if ($user->isAuthenticated()) { + // update 2f tpl stuff + if ($config['twofactor']['enabled']) { + $popupmsg = 'E-mail confirmations are required for '; + $popuptypes = array(); + if ($config['twofactor']['options']['details']) { + $popuptypes[] = 'editing your details'; + $ea_editable = $user->token->isTokenValid($_SESSION['USERDATA']['id'], $ea_token, 5); + $ea_sent = $user->token->doesTokenExist('account_edit', $_SESSION['USERDATA']['id']); + } + if ($config['twofactor']['options']['changepw']) { + $popuptypes[] = 'changing your password'; + $cp_editable = $user->token->isTokenValid($_SESSION['USERDATA']['id'], $cp_token, 6); + $cp_sent = $user->token->doesTokenExist('change_pw', $_SESSION['USERDATA']['id']); + } + if ($config['twofactor']['options']['withdraw']) { + $popuptypes[] = 'withdrawals'; + $wf_editable = $user->token->isTokenValid($_SESSION['USERDATA']['id'], $wf_token, 7); + $wf_sent = $user->token->doesTokenExist('withdraw_funds', $_SESSION['USERDATA']['id']); + } + $ptc = 0; + $ptcn = count($popuptypes); + foreach ($popuptypes as $pt) { + if ($ptcn == 1) { $popupmsg.= $popuptypes[$ptc]; continue; } + if ($ptc !== ($ptcn-1)) { + $popupmsg.= $popuptypes[$ptc].', '; + } else { + $popupmsg.= 'and '.$popuptypes[$ptc]; + } + $ptc++; + } + $_SESSION['POPUP'][] = array('CONTENT' => $popupmsg, 'TYPE' => 'info'); + } if (isset($_POST['do']) && $_POST['do'] == 'genPin') { if ($user->generatePin($_SESSION['USERDATA']['id'], $_POST['currentPassword'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Your PIN # has been sent to your email.', 'TYPE' => 'success'); @@ -13,54 +50,78 @@ if ($user->isAuthenticated()) { } } else { - if ( @$_POST['do'] && (! $user->checkPin($_SESSION['USERDATA']['id'], @$_POST['authPin']))) { + if ( @$_POST['do'] && (!$checkpin = $user->checkPin($_SESSION['USERDATA']['id'], @$_POST['authPin']))) { $_SESSION['POPUP'][] = array('CONTENT' => 'Invalid PIN. ' . ($config['maxfailed']['pin'] - $user->getUserPinFailed($_SESSION['USERDATA']['id'])) . ' attempts remaining.', 'TYPE' => 'errormsg'); } else { - switch (@$_POST['do']) { - case 'cashOut': - if ($setting->getValue('disable_payouts') == 1 || $setting->getValue('disable_manual_payouts') == 1) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Manual payouts are disabled.', 'TYPE' => 'info'); - } else { - $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); - $dBalance = $aBalance['confirmed']; - if ($dBalance > $config['txfee_manual']) { - if (!$oPayout->isPayoutActive($_SESSION['USERDATA']['id'])) { - $wf_token = (!isset($_POST['wf_token'])) ? '' : $_POST['wf_token']; - if ($iPayoutId = $oPayout->createPayout($_SESSION['USERDATA']['id'], $wf_token)) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Created new manual payout request with ID #' . $iPayoutId); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $iPayoutId->getError(), 'TYPE' => 'errormsg'); - } - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'You already have one active manual payout request.', 'TYPE' => 'errormsg'); - } + if (isset($_POST['unlock']) && isset($_POST['utype']) && $checkpin) { + $validtypes = array('account_edit','change_pw','withdraw_funds'); + $isvalid = in_array($_POST['utype'],$validtypes); + if ($isvalid) { + $ctype = strip_tags($_POST['utype']); + $send = $user->sendChangeConf($ctype, $_SESSION['USERDATA']['id']); + // set to sent for this pageload + if ($ctype == 'account_edit') $ea_sent = 1; + if ($ctype == 'change_pw') $cp_sent = 1; + if ($ctype == 'withdraw_funds') $wf_sent = 1; + if ($send) { + $_SESSION['POPUP'][] = array('CONTENT' => 'A confirmation was sent to your e-mail, follow that link to continue', 'TYPE' => 'success'); } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee_manual'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); } } - break; - - case 'updateAccount': + } else { $ea_token = (!isset($_POST['ea_token'])) ? '' : $_POST['ea_token']; - if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'], $ea_token)) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); - } - break; - - case 'updatePassword': $cp_token = (!isset($_POST['cp_token'])) ? '' : $_POST['cp_token']; - if ($user->updatePassword($_SESSION['USERDATA']['id'], $_POST['currentPassword'], $_POST['newPassword'], $_POST['newPassword2'], $cp_token)) { - $_SESSION['POPUP'][] = array('CONTENT' => 'Password updated', 'TYPE' => 'success'); - } else { - $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); + $wf_token = (!isset($_POST['wf_token'])) ? '' : $_POST['wf_token']; + switch (@$_POST['do']) { + case 'cashOut': + if ($setting->getValue('disable_payouts') == 1 || $setting->getValue('disable_manual_payouts') == 1) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Manual payouts are disabled.', 'TYPE' => 'info'); + } else { + $aBalance = $transaction->getBalance($_SESSION['USERDATA']['id']); + $dBalance = $aBalance['confirmed']; + if ($dBalance > $config['txfee']) { + if (!$oPayout->isPayoutActive($_SESSION['USERDATA']['id'])) { + if ($iPayoutId = $oPayout->createPayout($_SESSION['USERDATA']['id'], $wf_token)) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Created new manual payout request with ID #' . $iPayoutId); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $iPayoutId->getError(), 'TYPE' => 'errormsg'); + } + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'You already have one active manual payout request.', 'TYPE' => 'errormsg'); + } + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Insufficient funds, you need more than ' . $config['txfee'] . ' ' . $config['currency'] . ' to cover transaction fees', 'TYPE' => 'errormsg'); + } + } + break; + + case 'updateAccount': + if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'], $ea_token)) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg'); + } + break; + + case 'updatePassword': + if ($user->updatePassword($_SESSION['USERDATA']['id'], $_POST['currentPassword'], $_POST['newPassword'], $_POST['newPassword2'], $cp_token)) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Password updated', 'TYPE' => 'success'); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg'); + } + break; } - break; } } } } // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); +$smarty->assign("CHANGEPASSUNLOCKED", $cp_editable); +$smarty->assign("WITHDRAWUNLOCKED", $wf_editable); +$smarty->assign("DETAILSUNLOCKED", $ea_editable); +$smarty->assign("CHANGEPASSSENT", $cp_sent); +$smarty->assign("WITHDRAWSENT", $wf_sent); +$smarty->assign("DETAILSSENT", $ea_sent); ?> diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 01eeacbf..21fbcf2a 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -64,6 +64,7 @@ $aGlobal = array( 'confirmations' => $config['confirmations'], 'reward' => $config['reward'], 'price' => $setting->getValue('price'), + 'twofactor' => $config['twofactor'], 'config' => array( 'disable_navbar' => $setting->getValue('disable_navbar'), 'disable_navbar_api' => $setting->getValue('disable_navbar_api'), diff --git a/public/include/version.inc.php b/public/include/version.inc.php index ffc53d5c..6db42c6c 100644 --- a/public/include/version.inc.php +++ b/public/include/version.inc.php @@ -3,8 +3,8 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -define('CONFIG_VERSION', '0.0.3'); -define('MPOS_VERSION', '0.0.1'); +define('DB_VERSION', '0.0.4'); +define('CONFIG_VERSION', '0.0.4'); // Fetch installed database version $db_version = $setting->getValue('DB_VERSION'); diff --git a/public/site_assets/mpos/css/layout.css b/public/site_assets/mpos/css/layout.css index f28a2a70..d44651ee 100644 --- a/public/site_assets/mpos/css/layout.css +++ b/public/site_assets/mpos/css/layout.css @@ -580,6 +580,17 @@ text-shadow: 0 1px 0 #6CDCF9; cursor: pointer; } +input[type=submit].alt_btn:disabled,input[type=submit].alt_btn:readonly { +background: #D0D1D4 url(../images/btn_submit.png) repeat-x; +border: 1px solid#aaa; +text-shadow: none; +color: #999; +} + +input[type=submit].alt_btn:disabled:hover { +color: #999; +} + input[type=submit].alt_btn:hover { color: #001217; } diff --git a/public/templates/mpos/account/edit/default.tpl b/public/templates/mpos/account/edit/default.tpl index 1134ec2f..c63e6ccc 100644 --- a/public/templates/mpos/account/edit/default.tpl +++ b/public/templates/mpos/account/edit/default.tpl @@ -7,11 +7,11 @@