diff --git a/.gitignore b/.gitignore
index 91f343f8..15e9bfc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,6 @@ public/include/config/global.inc.sha.php
# IDE Settings
/.idea/*
+.buildpath
+.project
+.settings
diff --git a/public/include/classes/base.class.php b/public/include/classes/base.class.php
index 85b43d57..d3dd18c1 100644
--- a/public/include/classes/base.class.php
+++ b/public/include/classes/base.class.php
@@ -31,6 +31,9 @@ class Base {
public function setSalt($salt) {
$this->salt = $salt;
}
+ public function setSalty($salt) {
+ $this->salty = $salt;
+ }
public function setSmarty($smarty) {
$this->smarty = $smarty;
}
diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php
index b11f990c..3db6acf8 100644
--- a/public/include/classes/notification.class.php
+++ b/public/include/classes/notification.class.php
@@ -112,28 +112,16 @@ class Notification extends Mail {
$this->debug->append("STA " . __METHOD__, 4);
$failed = $ok = 0;
foreach ($data as $type => $active) {
- // Does an entry exist already
- $stmt = $this->mysqli->prepare("SELECT * FROM $this->tableSettings WHERE account_id = ? AND type = ?");
- if ($stmt && $stmt->bind_param('is', $account_id, $type) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows() > 0) {
- // We found a matching row
- $stmt = $this->mysqli->prepare("UPDATE $this->tableSettings SET active = ? WHERE type = ? AND account_id = ?");
- if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute() && $stmt->close()) {
- $ok++;
- } else {
- $failed++;
- }
+ $stmt = $this->mysqli->prepare("INSERT INTO $this->tableSettings (active, type, account_id) VALUES (?,?,?) ON DUPLICATE KEY UPDATE active = ?");
+ if ($stmt && $stmt->bind_param('isii', $active, $type, $account_id, $active) && $stmt->execute()) {
+ $ok++;
} else {
- $stmt = $this->mysqli->prepare("INSERT INTO $this->tableSettings (active, type, account_id) VALUES (?,?,?)");
- if ($stmt && $stmt->bind_param('isi', $active, $type, $account_id) && $stmt->execute()) {
- $ok++;
- } else {
- $failed++;
- }
+ $failed++;
}
}
if ($failed > 0) {
$this->setErrorMessage($this->getErrorMsg('E0047', $failed));
- return false;
+ return $this->sqlError();
}
return true;
}
diff --git a/public/include/classes/payout.class.php b/public/include/classes/payout.class.php
index d7c2cae1..ce4368e8 100644
--- a/public/include/classes/payout.class.php
+++ b/public/include/classes/payout.class.php
@@ -32,12 +32,23 @@ class Payout Extends Base {
/**
* Insert a new payout request
- * @param account_id Account ID
+ * @param account_id int Account ID
+ * @param strToken string Token to confirm
* @return data mixed Inserted ID or false
**/
- public function createPayout($account_id=NULL) {
+ public function createPayout($account_id=NULL, $strToken) {
$stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id) VALUES (?)");
if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute()) {
+ // twofactor - consume the token if it is enabled and valid
+ if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['withdraw']) {
+ $tValid = $this->token->isTokenValid($account_id, $strToken, 7);
+ if ($tValid) {
+ $this->token->deleteToken($strToken);
+ } else {
+ $this->setErrorMessage('Invalid token');
+ return false;
+ }
+ }
return $stmt->insert_id;
}
return $this->sqlError('E0049');
@@ -59,6 +70,8 @@ class Payout Extends Base {
$oPayout = new Payout();
$oPayout->setDebug($debug);
$oPayout->setMysql($mysqli);
+$oPayout->setConfig($config);
+$oPayout->setToken($oToken);
$oPayout->setErrorCodes($aErrorCodes);
?>
diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php
index 8453c245..bb947005 100644
--- a/public/include/classes/token.class.php
+++ b/public/include/classes/token.class.php
@@ -21,6 +21,37 @@ class Token Extends Base {
return $result->fetch_assoc();
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
+ * @param account_id int Account id of user to check
+ * @return mixed Number of rows on success, false on failure
+ */
+ public function doesTokenExist($strType=NULL, $account_id=NULL) {
+ if (!$iToken_id = $this->tokentype->getTypeId($strType)) {
+ $this->setErrorMessage('Invalid token type: ' . $strType);
+ return false;
+ }
+ $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->get_result()->num_rows;
+ return $this->sqlError();
+ }
/**
* Insert a new token
diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php
index f335f75f..ada25335 100644
--- a/public/include/classes/user.class.php
+++ b/public/include/classes/user.class.php
@@ -130,8 +130,26 @@ class User extends Base {
if ($this->checkUserPassword($username, $password)) {
$this->updateLoginTimestamp($this->getUserId($username));
$this->createSession($username);
- if ($this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR']))
+ if ($this->setUserIp($this->getUserId($username), $_SERVER['REMOTE_ADDR'])) {
+ // send a notification if success_login is active
+ $uid = $this->getUserId($username);
+ $notifs = new Notification();
+ $notifs->setDebug($this->debug);
+ $notifs->setMysql($this->mysqli);
+ $notifs->setSmarty($this->smarty);
+ $notifs->setConfig($this->config);
+ $notifs->setSetting($this->setting);
+ $notifs->setErrorCodes($this->aErrorCodes);
+ $ndata = $notifs->getNotificationSettings($uid);
+ if ($ndata['success_login'] == 1) {
+ // seems to be active, let's send it
+ $aDataN['username'] = $username;
+ $aDataN['email'] = $this->getUserEmail($username);
+ $aDataN['subject'] = 'Successful login notification';
+ $notifs->sendNotification($uid, 'success_login', $aDataN);
+ }
return true;
+ }
}
$this->setErrorMessage("Invalid username or password");
if ($id = $this->getUserId($username)) {
@@ -142,7 +160,7 @@ class User extends Base {
if ($token = $this->token->createToken('account_unlock', $id)) {
$aData['token'] = $token;
$aData['username'] = $username;
- $aData['email'] = $this->getUserEmail($username);;
+ $aData['email'] = $this->getUserEmail($username);
$aData['subject'] = 'Account auto-locked';
$this->mail->sendMail('notifications/locked', $aData);
}
@@ -255,15 +273,53 @@ 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 sendChangeConfigEmail($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
* @param current string Current password
* @param new1 string New password
* @param new2 string New password confirmation
+ * @param strToken string Token for confirmation
* @return bool
**/
- public function updatePassword($userID, $current, $new1, $new2) {
+ public function updatePassword($userID, $current, $new1, $new2, $strToken) {
$this->debug->append("STA " . __METHOD__, 4);
if ($new1 !== $new2) {
$this->setErrorMessage( 'New passwords do not match' );
@@ -280,6 +336,16 @@ class User extends Base {
$stmt->bind_param('sis', $new, $userID, $current);
$stmt->execute();
if ($stmt->errno == 0 && $stmt->affected_rows === 1) {
+ // twofactor - consume the token if it is enabled and valid
+ if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['changepw']) {
+ $tValid = $this->token->isTokenValid($userID, $strToken, 6);
+ if ($tValid) {
+ $this->token->deleteToken($strToken);
+ } else {
+ $this->setErrorMessage('Invalid token');
+ return false;
+ }
+ }
return true;
}
$stmt->close();
@@ -287,19 +353,19 @@ 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
* @param address string new coin address
* @param threshold float auto payout threshold
* @param donat float donation % of income
+ * @param strToken string Token for confirmation
* @return bool
**/
- public function updateAccount($userID, $address, $threshold, $donate, $email, $is_anonymous) {
+ public function updateAccount($userID, $address, $threshold, $donate, $email, $is_anonymous, $strToken) {
$this->debug->append("STA " . __METHOD__, 4);
$bUser = false;
-
// number validation checks
if (!is_numeric($threshold)) {
$this->setErrorMessage('Invalid input for auto-payout');
@@ -350,6 +416,16 @@ class User extends Base {
// We passed all validation checks so update the account
$stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ?, is_anonymous = ? WHERE id = ?");
if ($this->checkStmt($stmt) && $stmt->bind_param('sddsii', $address, $threshold, $donate, $email, $is_anonymous, $userID) && $stmt->execute())
+ // twofactor - consume the token if it is enabled and valid
+ if ($this->config['twofactor']['enabled'] && $this->config['twofactor']['options']['details']) {
+ $tValid = $this->token->isTokenValid($userID, $strToken, 5);
+ if ($tValid) {
+ $this->token->deleteToken($strToken);
+ } else {
+ $this->setErrorMessage('Invalid token');
+ return false;
+ }
+ }
return true;
// Catchall
$this->setErrorMessage('Failed to update your account');
@@ -712,6 +788,29 @@ class User extends Base {
if ($logout == true) $this->logoutUser($_SERVER['REQUEST_URI']);
return false;
}
+
+ /**
+ * Gets the current CSRF token for this user/type setting and time chunk
+ * @param string User; for hash seed, if username isn't available use IP
+ * @param string Type of token; for hash seed, should be unique per page/use
+ * @return string CSRF token
+ */
+ public function getCSRFToken($user, $type) {
+ $date = date('m/d/y/H/i/s');
+ $data = explode('/', $date);
+ $month = $data[0]; $day = $data[1]; $year = $data[2];
+ $hour = $data[3]; $minute = $data[4]; $second = $data[5];
+ $seed = $this->salty;
+ $lead = $this->config['csrf']['options']['leadtime'];
+ if ($lead >= 11) { $lead = 10; }
+ if ($lead <= 0) { $lead = 3; }
+ if ($minute == 59 && $second > (60-$lead)) {
+ $minute = 0;
+ $fhour = ($hour == 23) ? $hour = 0 : $hour+=1;
+ }
+ $seed = $seed.$month.$day.$user.$type.$year.$hour.$minute.$seed;
+ return $this->getHash($seed);
+ }
}
// Make our class available automatically
@@ -719,6 +818,8 @@ $user = new User();
$user->setDebug($debug);
$user->setMysql($mysqli);
$user->setSalt(SALT);
+$user->setSalty(SALTY);
+$user->setSmarty($smarty);
$user->setConfig($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 85dac842..fddace83 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');
@@ -26,6 +26,7 @@ define('DEBUG', 0);
// SALT used to hash passwords
define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM');
+define('SALTY', 'THISSHOULDALSOBERRAANNDDOOM');
/**
* Underlying coin algorithm that you are mining on. Set this to whatever your coin needs:
@@ -99,6 +100,52 @@ $config['coldwallet']['address'] = '';
$config['coldwallet']['reserve'] = 50;
$config['coldwallet']['threshold'] = 5;
+/**
+ * E-mail confirmations for user actions
+ *
+ * Explanation:
+ * To increase security for users, account detail changes can require
+ * an e-mail confirmation prior to performing certain actions.
+ *
+ * Options:
+ * enabled : Whether or not to require e-mail confirmations
+ * details : Require confirmation to change account details
+ * withdraw : Require confirmation to manually withdraw/payout
+ * changepw : Require confirmation to change password
+ *
+ * Default:
+ * enabled = true
+ * details = true
+ * withdraw = true
+ * changepw = true
+ */
+$config['twofactor']['enabled'] = true;
+$config['twofactor']['options']['details'] = true;
+$config['twofactor']['options']['withdraw'] = true;
+$config['twofactor']['options']['changepw'] = true;
+
+/**
+ * CSRF protection config
+ *
+ * Explanation:
+ * To help protect against CSRF, we can generate a hash that changes every minute
+ * and is unique for each user/IP and page or use, and check against that when a
+ * form is submitted.
+ *
+ * Options:
+ * enabled = Whether or not we will generate/check for valid CSRF tokens
+ * leadtime = Length of time in seconds to give as leeway, 1-10s
+ * login = Use and check CSRF tokens for the login forms
+ *
+ * Default:
+ * enabled = true
+ * leadtime = 3
+ * login = true
+ */
+$config['csrf']['enabled'] = true;
+$config['csrf']['options']['leadtime'] = 3;
+$config['csrf']['forms']['login'] = true;
+
/**
* Lock account after maximum failed logins
*
diff --git a/public/include/pages/account/edit.inc.php b/public/include/pages/account/edit.inc.php
index e368ea92..8df98502 100644
--- a/public/include/pages/account/edit.inc.php
+++ b/public/include/pages/account/edit.inc.php
@@ -4,7 +4,57 @@
if (!defined('SECURITY'))
die('Hacking attempt');
+// twofactor stuff
+$cp_editable = $wf_editable = $ea_editable = $wf_sent = $ea_sent = $cp_sent = 0;
+$ea_token = (!isset($_POST['ea_token'])) ? '' : $_POST['ea_token'];
+$cp_token = (!isset($_POST['cp_token'])) ? '' : $_POST['cp_token'];
+$wf_token = (!isset($_POST['wf_token'])) ? '' : $_POST['wf_token'];
+// set old token and type so we can use it later
+$old_token = "";
+$old_token_type = 0;
+if ($ea_token !== "") {
+ $old_token = $ea_token;
+ $old_token_type = 5;
+} else if ($wf_token !== "") {
+ $old_token = $wf_token;
+ $old_token_type = 7;
+} else if ($cp_token !== "") {
+ $old_token_type = 6;
+ $old_token = $cp_token;
+}
+
if ($user->isAuthenticated()) {
+ 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,51 +63,106 @@ 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'])) {
- if ($iPayoutId = $oPayout->createPayout($_SESSION['USERDATA']['id'])) {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Created new manual payout request with ID #' . $iPayoutId);
- } else {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to create manual payout request.', '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->sendChangeConfigEmail($ctype, $_SESSION['USERDATA']['id']);
+ 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':
- if ($user->updateAccount($_SESSION['USERDATA']['id'], $_POST['paymentAddress'], $_POST['payoutThreshold'], $_POST['donatePercent'], $_POST['email'], $_POST['is_anonymous'])) {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Account details updated', 'TYPE' => 'success');
- } else {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update your account: ' . $user->getError(), 'TYPE' => 'errormsg');
+ } else {
+ // back to get, was only post to fix for old token
+ $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 ($ea_token == '' && isset($_POST['ea_token']) && strlen($_POST['ea_token']) > 1) {
+ $ea_token = $_POST['ea_token'];
+ } else if ($ea_token == '' && isset($_POST['cp_token']) && strlen($_POST['cp_token']) > 1) {
+ $cp_token = $_POST['cp_token'];
+ } else if ($wf_token == '' && isset($_POST['wf_token']) && strlen($_POST['wf_token']) > 1) {
+ $wf_token = $_POST['wf_token'];
}
- break;
-
- case 'updatePassword':
- if ($user->updatePassword($_SESSION['USERDATA']['id'], $_POST['currentPassword'], $_POST['newPassword'], $_POST['newPassword2'])) {
- $_SESSION['POPUP'][] = array('CONTENT' => 'Password updated', 'TYPE' => 'success');
- } else {
- $_SESSION['POPUP'][] = array('CONTENT' => $user->getError(), 'TYPE' => 'errormsg');
+
+ 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;
}
}
}
}
+// 2fa - one last time so we can sync with changes we made during this page
+if ($user->isAuthenticated() && $config['twofactor']['enabled']) {
+ // set the token to be the old token so we still have it if it errors out
+ if ($old_token_type == 5) {
+ $ea_token = $old_token;
+ } else if ($old_token_type == 7) {
+ $wf_token = $old_token;
+ } else if ($old_token_type == 6) {
+ $cp_token = $old_token;
+ }
+ if ($config['twofactor']['options']['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']) {
+ $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']) {
+ $wf_editable = $user->token->isTokenValid($_SESSION['USERDATA']['id'], $wf_token, 7);
+ $wf_sent = $user->token->doesTokenExist('withdraw_funds', $_SESSION['USERDATA']['id']);
+ }
+}
// 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/pages/home.inc.php b/public/include/pages/home.inc.php
index ea85e43b..4dffeb8a 100644
--- a/public/include/pages/home.inc.php
+++ b/public/include/pages/home.inc.php
@@ -22,7 +22,12 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) {
} else {
$debug->append('Using cached page', 3);
}
-
+// csrf token - update if it's enabled
+$token = '';
+if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
+ $token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login');
+}
// Load news entries for Desktop site and unauthenticated users
$smarty->assign("CONTENT", "default.tpl");
+$smarty->assign('CTOKEN', $token);
?>
diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php
index e7cbaffa..5c26848d 100644
--- a/public/include/pages/login.inc.php
+++ b/public/include/pages/login.inc.php
@@ -5,16 +5,38 @@ if (!defined('SECURITY')) die('Hacking attempt');
if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST['username']))) {
$_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info');
-} else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) {
- empty($_POST['to']) ? $to = $_SERVER['SCRIPT_NAME'] : $to = $_POST['to'];
- $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
- $location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to;
- if (!headers_sent()) header('Location: ' . $location);
- exit('');
+} else if (isset($_POST['username']) && isset($_POST['password'])) {
+ $nocsrf = 1;
+ if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
+ if ((isset($_POST['ctoken']) && $_POST['ctoken'] !== $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login')) || (!isset($_POST['ctoken']))) {
+ // csrf protection is on and this token is invalid, error out -> time expired
+ $nocsrf = 0;
+ }
+ }
+ if ($nocsrf == 1 || (!$config['csrf']['enabled'] || !$config['csrf']['forms']['login'])) {
+ $checklogin = $user->checkLogin($_POST['username'], $_POST['password']);
+ if ($checklogin) {
+ empty($_POST['to']) ? $to = $_SERVER['SCRIPT_NAME'] : $to = $_POST['to'];
+ $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
+ $location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to;
+ if (!headers_sent()) header('Location: ' . $location);
+ exit('');
+ } else {
+ $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg');
+ }
+ } else {
+ $img = "
";
+ $_SESSION['POPUP'][] = array('CONTENT' => "Login token expired, please try again $img", 'TYPE' => 'info');
+ }
} else if (@$_POST['username'] && @$_POST['password']) {
$_SESSION['POPUP'][] = array('CONTENT' => 'Unable to login: '. $user->getError(), 'TYPE' => 'errormsg');
}
-
+// csrf token - update if it's enabled
+$token = '';
+if ($config['csrf']['enabled'] && $config['csrf']['forms']['login']) {
+ $token = $user->getCSRFToken($_SERVER['REMOTE_ADDR'], 'login');
+}
// Load login template
$smarty->assign('CONTENT', 'default.tpl');
+$smarty->assign('CTOKEN', $token);
?>
diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php
index 01eeacbf..f059d477 100644
--- a/public/include/smarty_globals.inc.php
+++ b/public/include/smarty_globals.inc.php
@@ -64,6 +64,8 @@ $aGlobal = array(
'confirmations' => $config['confirmations'],
'reward' => $config['reward'],
'price' => $setting->getValue('price'),
+ 'twofactor' => $config['twofactor'],
+ 'csrf' => $config['csrf'],
'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 f7feed1f..157f229b 100644
--- a/public/include/version.inc.php
+++ b/public/include/version.inc.php
@@ -3,9 +3,9 @@
// Make sure we are called from index.php
if (!defined('SECURITY')) die('Hacking attempt');
-define('DB_VERSION', '0.0.2');
-define('CONFIG_VERSION', '0.0.3');
define('MPOS_VERSION', '0.0.1');
+define('DB_VERSION', '0.0.3');
+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..8ea0107b 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 {
+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/mail/notifications/account_edit.tpl b/public/templates/mail/notifications/account_edit.tpl
new file mode 100644
index 00000000..fa1fcfcf
--- /dev/null
+++ b/public/templates/mail/notifications/account_edit.tpl
@@ -0,0 +1,9 @@
+
+
You have a pending request to change your account details.
+If you initiated this request, please follow the link below to confirm your changes. If you did NOT, please notify an administrator.
+http://{$smarty.server.SERVER_NAME}{$smarty.server.SCRIPT_NAME}?page=account&action=edit&ea_token={nocache}{$DATA.token}{/nocache}
+You have a pending request to change your password.
+If you initiated this request, please follow the link below to confirm your changes. If you did NOT, please notify an administrator.
+http://{$smarty.server.SERVER_NAME}{$smarty.server.SCRIPT_NAME}?page=account&action=edit&cp_token={nocache}{$DATA.token}{/nocache}
+Your account has successfully logged in
+If you initiated this login, you can ignore this message. If you did NOT, please notify an administrator.
+You have a pending request to manually withdraw funds.
+If you initiated this request, please follow the link below to confirm your changes. If you did NOT, please notify an administrator.
+http://{$smarty.server.SERVER_NAME}{$smarty.server.SCRIPT_NAME}?page=account&action=edit&wf_token={nocache}{$DATA.token}{/nocache}
+