fixing login email case sensitivity issues

This commit is contained in:
forest 2020-12-29 18:42:38 -06:00
parent 7f1f5ac976
commit ff38858c74
5 changed files with 45 additions and 10 deletions

View File

@ -43,17 +43,28 @@ def login():
errors.append("enter a valid email address") errors.append("enter a valid email address")
if len(errors) == 0: if len(errors) == 0:
token = get_model().login(email) result = get_model().login(email)
token = result[0]
ignoreCaseMatches = result[1]
if token is None: if token is None:
errors.append("too many logins. please use one of the existing login links that have been emailed to you") errors.append("too many logins. please use one of the existing login links that have been emailed to you")
else: else:
link = f"{current_app.config['BASE_URL']}/auth/magic/{token}" link = f"{current_app.config['BASE_URL']}/auth/magic/{token}"
message = (f"Navigate to {link} to log into Capsul.\n"
"\nIf you didn't request this, ignore this message.")
if len(ignoreCaseMatches) > 0:
joinedMatches = " or ".join(ignoreCaseMatches)
message = (f"You tried to log in as '{email}', but that account doesn't exist yet. "
"If you would like to create a new account for '{email}', click here {link} "
"If you meant to log in as {joinedMatches}, please return to capsul.org "
"and log in again with the correct (case-sensitive) email address.")
current_app.config["FLASK_MAIL_INSTANCE"].send( current_app.config["FLASK_MAIL_INSTANCE"].send(
Message( Message(
"Click This Link to Login to Capsul", "Click This Link to Login to Capsul",
body=(f"Navigate to {link} to log into Capsul.\n" body=message,
"\nIf you didn't request this, ignore this message."),
recipients=[email] recipients=[email]
) )
) )

View File

@ -40,7 +40,7 @@ def init_app(app):
hasSchemaVersionTable = False hasSchemaVersionTable = False
actionWasTaken = False actionWasTaken = False
schemaVersion = 0 schemaVersion = 0
desiredSchemaVersion = 8 desiredSchemaVersion = 9
cursor = connection.cursor() cursor = connection.cursor()

View File

@ -10,18 +10,26 @@ class DBModel:
def login(self, email): def login(self, email):
self.cursor.execute("SELECT * FROM accounts WHERE email = %s", (email, )) self.cursor.execute("SELECT * FROM accounts WHERE email = %s", (email, ))
if len(self.cursor.fetchall()) == 0: hasExactMatch = len(self.cursor.fetchall())
self.cursor.execute("INSERT INTO accounts (email) VALUES (%s)", (email, )) self.cursor.execute("SELECT * FROM accounts WHERE email = %s AND ever_logged_in = TRUE", (email, ))
everLoggedIn = len(self.cursor.fetchall())
ignoreCaseMatches = []
if everLoggedIn == 0:
self.cursor.execute("SELECT email FROM accounts WHERE lower_case_email = %s", (email.lower(), ))
ignoreCaseMatches = self.cursor.fetchall()
if hasExactMatch == 0:
self.cursor.execute("INSERT INTO accounts (email, lower_case_email) VALUES (%s, %s)", (email, email.lower()))
self.cursor.execute("SELECT token FROM login_tokens WHERE email = %s", (email, )) self.cursor.execute("SELECT token FROM login_tokens WHERE email = %s", (email, ))
if len(self.cursor.fetchall()) > 2: if len(self.cursor.fetchall()) > 2:
return None return (None, ignoreCaseMatches)
token = generate() token = generate()
self.cursor.execute("INSERT INTO login_tokens (email, token) VALUES (%s, %s)", (email, token)) self.cursor.execute("INSERT INTO login_tokens (email, token) VALUES (%s, %s)", (email, token))
self.connection.commit() self.connection.commit()
return token return (token, ignoreCaseMatches)
def consume_token(self, token): def consume_token(self, token):
self.cursor.execute("SELECT email FROM login_tokens WHERE token = %s and created > (NOW() - INTERVAL '20 min')", (token, )) self.cursor.execute("SELECT email FROM login_tokens WHERE token = %s and created > (NOW() - INTERVAL '20 min')", (token, ))
@ -29,6 +37,7 @@ class DBModel:
if row: if row:
email = row[0] email = row[0]
self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, )) self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, ))
self.cursor.execute("UPDATE accounts SET ever_logged_in = TRUE WHERE email = %s", (email, ))
self.connection.commit() self.connection.commit()
return email return email
return None return None
@ -228,7 +237,7 @@ class DBModel:
if row: if row:
self.cursor.execute( "DELETE FROM unresolved_btcpay_invoices WHERE id = %s", (id,) ) self.cursor.execute( "DELETE FROM unresolved_btcpay_invoices WHERE id = %s", (id,) )
if not completed: if not completed:
self.cursor.execute("UPDATE payments SET invalidated = True WHERE email = %s id = %s", (row[0], row[1])) self.cursor.execute("UPDATE payments SET invalidated = TRUE WHERE email = %s id = %s", (row[0], row[1]))
self.connection.commit() self.connection.commit()
@ -249,7 +258,7 @@ class DBModel:
self.connection.commit() self.connection.commit()
def all_accounts(self): def all_accounts(self):
self.cursor.execute("SELECT email, account_balance_warning FROM accounts") self.cursor.execute("SELECT email, account_balance_warning FROM accounts WHERE ever_logged_in = TRUE ")
return list(map(lambda row: dict(email=row[0], account_balance_warning=row[1]), self.cursor.fetchall())) return list(map(lambda row: dict(email=row[0], account_balance_warning=row[1]), self.cursor.fetchall()))

View File

@ -0,0 +1,5 @@
ALTER TABLE accounts DROP COLUMN lower_case_email;
ALTER TABLE accounts DROP COLUMN ever_logged_in;
UPDATE schemaversion SET version = 8;

View File

@ -0,0 +1,10 @@
ALTER TABLE accounts
ADD COLUMN lower_case_email TEXT NULL;
ALTER TABLE accounts
ADD COLUMN ever_logged_in BOOLEAN NOT NULL DEFAULT FALSE;
UPDATE accounts set lower_case_email = LOWER(accounts.email);
UPDATE accounts set ever_logged_in = TRUE;
UPDATE schemaversion SET version = 9;