Skip to content
This repository has been archived by the owner on Feb 23, 2022. It is now read-only.

Commit

Permalink
Verify email when changing it from profile.
Browse files Browse the repository at this point in the history
  • Loading branch information
fpereiro committed Jan 20, 2021
1 parent 49b77bd commit bc7d6a3
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 9 deletions.
15 changes: 13 additions & 2 deletions auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,21 +261,30 @@ def update_profile (user):
if body ['gender'] != 'm' and body ['gender'] != 'f' and body ['gender'] != 'o':
return 'body.gender must be m/f/o', 400

resp = {}
if 'email' in body:
email = body ['email'].strip ().lower ()
if email != user ['email']:
exists = db_get ('users', {'email': email}, True)
if exists:
return 'email exists', 403
db_set ('users', {'username': user ['username'], 'email': email})
token = make_salt ()
hashed_token = hash (token, make_salt ())
db_set ('users', {'username': user ['username'], 'email': email, 'verification_pending': hashed_token})
if not env:
# If on local environment, we return email verification token directly instead of emailing it, for test purposes.
resp = {'username': user ['username'], 'token': hashed_token}
else:
send_email_template ('welcome_verify', email, requested_lang (), os.getenv ('BASE_URL') + '/auth/verify?username=' + urllib.parse.quote_plus (username) + '&token=' + urllib.parse.quote_plus (hashed_token))

if 'country' in body:
db_set ('users', {'username': user ['username'], 'country': body ['country']})
if 'birth_year' in body:
db_set ('users', {'username': user ['username'], 'birth_year': body ['birth_year']})
if 'gender' in body:
db_set ('users', {'username': user ['username'], 'gender': body ['gender']})
return '', 200

return jsonify (resp)

@app.route ('/profile', methods=['GET'])
@requires_login
Expand All @@ -287,6 +296,8 @@ def get_profile (user):
output ['country'] = user ['country']
if 'gender' in user:
output ['gender'] = user ['gender']
if 'verification_pending' in user:
output ['verification_pending'] = True

return jsonify (output), 200

Expand Down
3 changes: 2 additions & 1 deletion doc/backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
- `GET /profile`
- This route allows the user to retrieve their profile.
- This route requires a session, otherwise it returns 403.
- If successful, this route returns 200 with a body of the shape `{username: STRING, email: STRING, birth_year: INTEGER|UNDEFINED, country: STRING|UNDEFINED, gender: m|f|o|UNDEFINED}`.
- If successful, this route returns 200 with a body of the shape `{username: STRING, email: STRING, birth_year: INTEGER|UNDEFINED, country: STRING|UNDEFINED, gender: m|f|o|UNDEFINED, verification_pending: UNDEFINED|true}`.

- `POST /profile`
- This route allows the user to change its `email`, `birth_year`, `gender` and/or `country`.
Expand All @@ -80,6 +80,7 @@
- If present, `gender` must be either `m`, `f` or `o`.
- If present, `email` must be a valid email.
- `email` should not be in use by an existing user. Otherwise, the route returns 403.
- If `email` is present and different from the existing email, the route will also send a verification email to the provided `email`.
- If successful, the route returns 200.

- `GET /users`
Expand Down
28 changes: 22 additions & 6 deletions tests_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,31 @@ def successfulLogin(state, response):

def getProfile1(state, response):
profile = response ['body']
if response ['body'] ['username'] != username:
if profile ['username'] != username:
raise Exception ('Invalid username (getProfile1)')
if response ['body'] ['email'] != username + '@domain.com':
if profile ['email'] != username + '@domain.com':
raise Exception ('Invalid username (getProfile1)')

def getProfile2(state, response):
profile = response ['body']
if response ['body'] ['country'] != 'NL':
if profile ['country'] != 'NL':
raise Exception ('Invalid country (getProfile2)')
if response ['body'] ['email'] != username + '@domain2.com':
if profile ['email'] != username + '@domain2.com':
raise Exception ('Invalid country (getProfile2)')
if not 'verification_pending' in profile or profile ['verification_pending'] != True:
raise Exception ('Invalid verification_pending (getProfile2)')

def getProfile3(state, response):
profile = response ['body']
if 'verification_pending' in profile:
raise Exception ('Invalid verification_pending (getProfile3)')

def emailChange(state, response):
if not type_check (response ['body'] ['token'], 'str'):
raise Exception ('Invalid country (emailChange)')
if response ['body'] ['username'] != username:
raise Exception ('Invalid username (emailChange)')
state ['token2'] = response ['body'] ['token']

def recoverPassword(state, response):
if not 'token' in response ['body']:
Expand Down Expand Up @@ -146,8 +160,10 @@ def recoverPassword(state, response):
['get profile before profile update', 'get', '/profile', {}, {}, 200, getProfile1],
invalidMap ('update profile', 'post', '/profile', ['', [], {'email': 'foobar'}, {'birth_year': 'a'}, {'birth_year': 20}, {'country': 'Netherlands'}, {'gender': 0}, {'gender': 'a'}]),
['change profile with same email', 'post', '/profile', {}, {'email': username + '@domain.com', 'country': 'US'}, 200],
['change profile with different email', 'post', '/profile', {}, {'email': username + '@domain2.com', 'country': 'NL'}, 200],
['get profile before profile update', 'get', '/profile', {}, {}, 200, getProfile2],
['change profile with different email', 'post', '/profile', {}, {'email': username + '@domain2.com', 'country': 'NL'}, 200, emailChange],
['get profile after profile update', 'get', '/profile', {}, {}, 200, getProfile2],
['verify email after email change', 'get', lambda state: '/auth/verify?' + urllib.parse.urlencode ({'username': username, 'token': state ['token2']}), {}, '', 302],
['get profile after email verification', 'get', '/profile', {}, {}, 200, getProfile3],
invalidMap ('recover password', 'post', '/auth/recover', ['', [], {}, {'username': 1}]),
['recover password, invalid user', 'post', '/auth/recover', {}, {'username': 'nosuch'}, 403],
['recover password', 'post', '/auth/recover', {}, {'username': username}, 200, recoverPassword],
Expand Down

0 comments on commit bc7d6a3

Please sign in to comment.