Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email change notification #2500

Merged
merged 42 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2b16e60
Include previous_email_addr and email_addr_change_time to database sc…
Uplinger Apr 13, 2018
23a9b2b
Update to have a changed email sent to members after they change thei…
Apr 16, 2018
8956751
Updated to send the email out properly.
Uplinger Apr 17, 2018
cc38a9d
Update to actually print the word at in email message.
Uplinger Apr 19, 2018
863cf3d
Added text to the website that will show if an email was changed with…
Uplinger Apr 19, 2018
a2a78a6
Merge branch 'master' into email_change_notification
Uplinger Apr 19, 2018
db4e020
Update to have db_update.php to add previous_email_addr and email_add…
Uplinger Apr 19, 2018
36a8b1f
Changed to use php time passed to the database instead of using the d…
Uplinger Apr 19, 2018
a1b03c8
Merge remote-tracking branch 'upstream/knr_token2' into email_change_…
Apr 23, 2018
dbafb59
Change to use token.inc for email_change_notification
Apr 23, 2018
c516ed3
Fixed error in string as well as force email.inc to include token.inc.
Uplinger Apr 23, 2018
fa4603f
Updated user pages to include a reset_email_addr.php that is called f…
Uplinger Apr 24, 2018
6602dcf
Renamed to recover_email.php from reset_email_addr.php
Uplinger Apr 24, 2018
50a8756
Changed to fix datbase update error on recover_email.php.
Uplinger Apr 24, 2018
79b169c
Fixed email changes so that database_hash is used instead of simple e…
Uplinger Apr 24, 2018
09c54fb
Merge remote-tracking branch 'upstream/master' into email_change_noti…
Apr 25, 2018
389ede3
Updated to have function to look up previous email addresses that are…
May 1, 2018
c226ee7
Account manager set info and create user has a check to make sure tha…
May 1, 2018
4785a05
Fixing missing ending call in function.
Uplinger May 1, 2018
69f78a6
Fix bug where I was sending it a null string to lookup_prev_email_addr
Uplinger May 1, 2018
3011ccf
Fixed a typo in recover_email.php as well as not require the user to …
Uplinger May 1, 2018
aaf866e
Delete token for email recovery.
Uplinger May 2, 2018
7916631
Fix setting previous email address in AccountManager set info.
Uplinger May 2, 2018
93d6e0b
Clean up boinc_db email calls.
Uplinger May 2, 2018
7c9b4ad
Fixed bug where new email address and change time was not being passe…
Uplinger May 2, 2018
4aa5acb
Changed email.inc so that it will look for older version of PHPMailer…
Uplinger May 2, 2018
d0fcd1e
Various tweaks to Keith's changes.
davidpanderson May 7, 2018
e874a86
Removed call to BoincDb::get() as it called within escape_string func…
May 8, 2018
2905e50
Cleaned up text within email recovery pages.
May 8, 2018
cbcbb5e
Merge remote-tracking branch 'upstream/keith_email' into email_change…
May 8, 2018
6964382
Updated comments in a few places for email address changes.
May 8, 2018
7749c64
Added database call for previous email address in c++ code.
May 8, 2018
94a6687
am_set_info, if email address is the same as the user, then do nothin…
May 8, 2018
5cead5f
Added check in am_set_info to return an error if the email address wa…
May 8, 2018
387d596
Fixed problem with bad constant called for invalid email address in a…
May 8, 2018
8c9626f
Changed setting previous_email_addr to blank instead of null since th…
May 8, 2018
e82a698
Fixed Scrutinizer issue where variables were declared blank but decla…
May 8, 2018
9cd7d63
Change error code for duplicate email addresses found in am_set_info.php
May 8, 2018
0d78176
Change phrasing in the edit_email_form.php
May 8, 2018
4168861
Added comments for recover_email.php
May 8, 2018
fecbfa3
Modified the table definition for user table to have email_addr_chang…
Uplinger May 10, 2018
12a3933
Updated boinc_db.cpp to print email address change columns.
May 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 8 additions & 2 deletions db/boinc_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ void DB_USER::db_print(char* buf){
ESCAPE(project_prefs);
ESCAPE(url);
ESCAPE(signature);
ESCAPE(previous_email_addr);
sprintf(buf,
"create_time=%d, email_addr='%s', name='%s', "
"authenticator='%s', "
Expand All @@ -360,7 +361,8 @@ void DB_USER::db_print(char* buf){
"seti_total_cpu=%.15e, signature='%s', has_profile=%d, "
"cross_project_id='%s', passwd_hash='%s', "
"email_validated=%d, donated=%d, "
"login_token='%s', login_token_time=%f",
"login_token='%s', login_token_time=%f, "
"previous_email_addr='%s', email_addr_change_time=%f",
create_time, email_addr, name,
authenticator,
country, postal_code,
Expand All @@ -372,7 +374,8 @@ void DB_USER::db_print(char* buf){
seti_total_cpu, signature, has_profile,
cross_project_id, passwd_hash,
email_validated, donated,
login_token, login_token_time
login_token, login_token_time,
previous_email_addr, email_addr_change_time
);
UNESCAPE(email_addr);
UNESCAPE(name);
Expand All @@ -382,6 +385,7 @@ void DB_USER::db_print(char* buf){
UNESCAPE(project_prefs);
UNESCAPE(url);
UNESCAPE(signature);
UNESCAPE(previous_email_addr);
}

void DB_USER::db_parse(MYSQL_ROW &r) {
Expand Down Expand Up @@ -417,6 +421,8 @@ void DB_USER::db_parse(MYSQL_ROW &r) {
donated = atoi(r[i++]);
strcpy2(login_token, r[i++]);
login_token_time = atof(r[i++]);
strcpy2(previous_email_addr, r[i++]);
email_addr_change_time = atof(r[i++]);
}

void DB_TEAM::db_print(char* buf){
Expand Down
2 changes: 2 additions & 0 deletions db/boinc_db_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ struct USER {
int donated;
char login_token[32];
double login_token_time;
char previous_email_addr[256];
double email_addr_change_time;
void clear();
};

Expand Down
3 changes: 2 additions & 1 deletion db/constraints.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ alter table user
add index user_name(name),
add index user_tot (total_credit desc),
-- db_dump.C
add index user_avg (expavg_credit desc);
add index user_avg (expavg_credit desc),
-- db_dump.C
add index user_email_time (email_addr_change_time);

alter table team
add unique(name),
Expand Down
2 changes: 2 additions & 0 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ create table user (
donated smallint not null,
login_token char(32) not null default '',
login_token_time double not null default 0,
previous_email_addr varchar(254) not null default '',
email_addr_change_time double not null default 0,
primary key (id)
) engine=InnoDB;

Expand Down
16 changes: 13 additions & 3 deletions html/inc/boinc_db.inc
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,19 @@ class BoincUser {
return self::$cache[$id];
}
static function lookup_auth($auth) {
$db = BoincDb::get();
$auth = BoincDb::escape_string($auth);
return self::lookup("authenticator='$auth'");
}
static function lookup_email_addr($email_addr) {
$db = BoincDb::get();
$email_addr = BoincDb::escape_string($email_addr);
$email_addr = strtolower(BoincDb::escape_string($email_addr));
return self::lookup("email_addr='$email_addr'");
}
//This is to find email addresses that have changed in the past 7 days.
static function lookup_prev_email_addr($email_addr) {
$email_addr = strtolower(BoincDb::escape_string($email_addr));
$mytime = time() - 604800;
return self::lookup("email_addr_change_time > $mytime and previous_email_addr='$email_addr'");
}
// name is not necessarily unique
//
static function lookup_name($name) {
Expand Down Expand Up @@ -805,6 +809,12 @@ class BoincToken {
$db = BoincDB::get();
return $db->get_list('token', 'userid', 'type', 'create_time', 'expire_time', 'BoincToken', '*', $where_clause, $order_clause, $limit);
}

static function delete_token($where_clause) {
$db = BoincDb::get();
$db->delete_aux('token', $where_clause);
return $db->affected_rows();
}

static function delete_expired() {
$db = BoincDb::get();
Expand Down
46 changes: 43 additions & 3 deletions html/inc/email.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,27 @@

require_once("../inc/util.inc");
require_once("../project/project.inc");
require_once("../inc/token.inc");

// send an email, using PHPMailer or not.
//
function send_email($user, $subject, $body, $body_html=null) {
function send_email($user, $subject, $body, $body_html=null, $email_addr=null) {
if (function_exists("make_php_mailer")) {
require_once("../inc/phpmailer/class.phpmailer.php");
if (file_exists("../inc/PHPMailer/src/PHPMailer.php") && file_exists("../inc/PHPMailer/src/SMTP.php")) {
require_once("../inc/PHPMailer/src/PHPMailer.php");
require_once("../inc/PHPMailer/src/SMTP.php");
} else if (file_exists("../inc/phpmailer/class.phpmailer.php")) {
require_once("../inc/phpmailer/class.phpmailer.php");
} else {
echo "PHPMailer not installed";
return false;
}
$mail = make_php_mailer();
$mail->AddAddress($user->email_addr, $user->name);
if ($email_addr) {
$mail->AddAddress($email_addr, $user->name);
} else {
$mail->AddAddress($user->email_addr, $user->name);
}
$mail->Subject = $subject;
if ($body_html) {
$mail->AltBody = $body;
Expand Down Expand Up @@ -86,6 +99,33 @@ For further information and assistance with ".PROJECT.", visit
return send_email($user, $subject, $body);
}

function send_changed_email($user) {
$duration = TOKEN_DURATION_ONE_WEEK;

$token = create_token($user->id, TOKEN_TYPE_CHANGE_EMAIL, $duration);

$subject = PROJECT." email address change.";

// Body for the new email address to explain how quickly
// they can do another email change.
//
$body_new = "Your email address was changed from ".$user->previous_email_addr.
" to ".$user->email_addr." on ".date('F j \a\t g:i a T', $user->email_addr_change_time).
". You will not be able to change your email address again until ".date('F j \a\t g:i a T', $user->email_addr_change_time + $duration).
". If you need to undo this immediately, please look for an email from us at your ".$user->previous_email_addr." address.";

// We need to send a different version of the email to the old address.
//
$body_old = "Your email address has been changed. If you did not take this action,
then please click on the link below to reverse this process and change your password.

".secure_url_base()."recover_email.php?id=".$user->id."&token=".$token."

Note: Your password will need to be recovered after clicking this link";

return send_email($user, $subject, $body_new) && send_email($user, $subject, $body_old, null, $user->previous_email_addr);
}

// a valid email address is of the form A@B.C
// where A, B, C are nonempty,
// A and B don't contain @ or .,
Expand Down
9 changes: 9 additions & 0 deletions html/inc/token.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ require_once("../inc/util.inc");

// Constants for valid token types
define("TOKEN_TYPE_DELETE_ACCOUNT", "D");
define("TOKEN_TYPE_CHANGE_EMAIL", "E");

// Constants for token durations
define("TOKEN_DURATION_ONE_DAY", 86400);
define("TOKEN_DURATION_ONE_WEEK", 604800);

function create_token($userid, $type, $duration) {
$token = random_string();
Expand All @@ -37,6 +39,13 @@ function create_token($userid, $type, $duration) {
return $token;
}

function delete_token($userid, $token, $type) {
$token = BoincDb::escape_string($token);
$type = BoincDb::escape_string($type);
$result = BoincToken::delete_token("userid = $userid and token = '$token' and type = '$type'");
return $result;
}

function is_valid_token($userid, $token, $type) {
$boincToken = BoincToken::lookup_valid_token($userid, $token, $type);
if ( $boincToken == null ) {
Expand Down
4 changes: 4 additions & 0 deletions html/inc/user_util.inc
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ function validate_post_make_user() {
if ($user) {
show_error(tra("There's already an account with that email address."));
}
$tmpuser = BoincUser::lookup_prev_email_addr($new_email_addr);
if ($tmpuser) {
show_error(tra("There's already an account with that email address."));
}

$passwd = post_str("passwd");

Expand Down
11 changes: 10 additions & 1 deletion html/ops/db_update.php
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,14 @@ function update_4_18_2018() {
");
}

function update_4_19_2018() {
do_query("alter table user
add column previous_email_addr varchar(254) not null default '',
add column email_addr_change_time double not null default 0
");
do_query("alter table user add index user_email_time (email_addr_change_time)");
}

// Updates are done automatically if you use "upgrade".
//
// If you need to do updates manually,
Expand Down Expand Up @@ -1157,7 +1165,8 @@ function update_4_18_2018() {
array(27021, "update_3_8_2018"),
array(27022, "update_4_5_2018"),
array(27023, "update_4_6_2018"),
array(27024, "update_4_18_2018")
array(27024, "update_4_18_2018"),
array(27025, "update_4_19_2018")
);

?>
40 changes: 36 additions & 4 deletions html/user/am_set_info.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
require_once("../inc/team.inc");
require_once("../inc/email.inc");
require_once("../inc/password_compat/password.inc");
require_once("../inc/user_util.inc");

// do a very cursory check that the given text is valid;
// for now, just make sure it has the given start and end tags,
Expand Down Expand Up @@ -111,7 +112,24 @@ function success($x) {
$send_email = BoincDb::escape_string($send_email);
$show_hosts = BoincDb::escape_string($show_hosts);
$venue = BoincDb::escape_string($venue);
if ($email_addr) {
$send_changed_email_to_user = false;
$email_addr = strtolower($email_addr);
if ($email_addr && $email_addr != $user->email_addr) {
$tmpuser = BoincUser::lookup_email_addr($email_addr);
if ($tmpuser) {
xml_error(ERR_DB_NOT_UNIQUE, "There's already an account with that email address.");
}

//check if the email address is included in previous_email_addr window.
//
$tmpuser = BoincUser::lookup_prev_email_addr($email_addr);
if ($tmpuser) {
xml_error(ERR_DB_NOT_UNIQUE, "Email address is already in use");
}
if ($user->email_addr_change_time + 604800 > time()) {
xml_error(ERR_BAD_EMAIL_ADDR, "Email address was changed within the past 7 days, please look for an email to $user->previous_email_addr if this email change is incorrect.");
}

if (!is_valid_email_addr($email_addr)) {
xml_error(ERR_BAD_EMAIL_ADDR, "Invalid email address");
}
Expand Down Expand Up @@ -172,9 +190,20 @@ function success($x) {
if ($venue) {
$query .= " venue='$venue', ";
}
if ($email_addr && $email_addr!=$user->email_addr) {
$old_email_addr = $user->email_addr;
$query .= " email_addr='$email_addr', ";

// Check to see if email_addr is different then what user->email-addr has
// If it is different, then update the database and trigger sending an
// email to the user that the email address has been changed.
//
if ($email_addr && $email_addr != $user->email_addr) {
$user->previous_email_addr = $user->email_addr;
$user->email_addr_change_time = time();
$user->email_addr = $email_addr;
$query .= " email_addr='$user->email_addr', ";
if ($user->previous_email_addr) {
$query .= " previous_email_addr='$user->previous_email_addr', email_addr_change_time=$user->email_addr_change_time, ";
$send_changed_email_to_user = true;
}
}
if ($password_hash) {
$database_passwd_hash = password_hash($password_hash, PASSWORD_DEFAULT);
Expand All @@ -188,6 +217,9 @@ function success($x) {
$query = "$query seti_id=seti_id";
$result = $user->update($query);
if ($result) {
if ($send_changed_email_to_user) {
send_changed_email($user);
}
success("");
} else {
xml_error(-1, "database error: ".BoincDb::error());
Expand Down
5 changes: 5 additions & 0 deletions html/user/create_account.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
xml_error(-1, "password hash length not 32");
}

$tmpuser = BoincUser::lookup_prev_email_addr($email_addr);
if ($tmpuser) {
xml_error(ERR_DB_NOT_UNIQUE);
}

$user = BoincUser::lookup_email_addr($email_addr);
if ($user) {
if ($user->passwd_hash != $passwd_hash && !password_verify($passwd_hash, $user->passwd_hash)) {
Expand Down
13 changes: 12 additions & 1 deletion html/user/edit_email_action.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.

// This changes the user email, and then sets previous_email_addr to the old
// user email address for recovery when needed. It triggers emails to both
// addresses for a way to revert it to the previous address.
//

require_once("../inc/boinc_db.inc");
require_once("../inc/util.inc");
require_once("../inc/email.inc");
Expand All @@ -37,6 +42,8 @@
echo tra("New email address '%1' is invalid.", $email_addr);
} else if ($email_addr == $user->email_addr) {
echo tra("New email address is same as existing address. Nothing is changed.");
} else if ($user->email_addr_change_time + 604800 > time()) {
echo tra("Email address was changed within the past 7 days, please look for an email to $user->previous_email_addr if this email change is incorrect.");
} else {
$existing = BoincUser::lookup_email_addr($email_addr);
if ($existing) {
Expand All @@ -57,14 +64,18 @@
$passwd_hash = md5($passwd.$email_addr);
$database_passwd_hash = password_hash($passwd_hash , PASSWORD_DEFAULT);
$email_addr = BoincDb::escape_string($email_addr);
$user->email_addr_change_time = time();
$result = $user->update(
"email_addr='$email_addr', passwd_hash='$database_passwd_hash', email_validated=0"
"email_addr='$email_addr', previous_email_addr='$user->email_addr', email_addr_change_time=$user->email_addr_change_time, passwd_hash='$database_passwd_hash', email_validated=0"
);
$user->previous_email_addr = $user->email_addr;
$user->email_addr = $email_addr;
if ($result) {
echo tra("The email address of your account is now %1.", $email_addr);
if (defined("SHOW_NONVALIDATED_EMAIL_ADDR")) {
echo "<p>".tra("Please %1 validate this email address %2.", "<a href=validate_email_addr.php>", "</a>")."\n";
}
send_changed_email($user);
} else {
echo tra("We can't update your email address due to a database problem. Please try again later.");
}
Expand Down
32 changes: 18 additions & 14 deletions html/user/edit_email_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,24 @@
$email_text = $user->email_addr;
}

form_start(secure_url_base()."/edit_email_action.php", "post");
form_input_text(
tra("New email address").
"<br><p class=\"text-muted\">".tra("Must be a valid address of the form 'name@domain'")."</p>",
"email_addr", $email_text
);

// we need the password here not for verification,
// but because we store it salted with email address,
// which is about to change.

form_input_text(tra("Password"), "passwd", "", "password");
form_submit(tra("Change email address"));
form_end();
if ($user->email_addr_change_time + 604800 > time()) {
echo tra("Email address was changed within the past 7 days. Please look for an email to $user->previous_email_addr if you need to revert this change.");
} else {
form_start(secure_url_base()."edit_email_action.php", "post");
form_input_text(
tra("New email address").
"<br><p class=\"text-muted\">".tra("Must be a valid address of the form 'name@domain'")."</p>",
"email_addr", $email_text
);

// we need the password here not for verification,
// but because we store it salted with email address,
// which is about to change.

form_input_text(tra("Password"), "passwd", "", "password");
form_submit(tra("Change email address"));
form_end();
}
page_tail();

?>