Skip to content

Commit

Permalink
Refactor email token decoding (#434)
Browse files Browse the repository at this point in the history
* refactor email token decoding

* auto patch increment

* bump flask-cors

* improve description

* improve docstring

* actually move the decoding outside the session

* improve docstring

* decode docstring

* remove email from logs

* auto patch increment

---------

Co-authored-by: ras-rm-pr-bot <rasrm.team@ons.gov.uk>
Co-authored-by: anwilkie <47449873+anwilkie@users.noreply.github.com>
  • Loading branch information
3 people authored Sep 19, 2024
1 parent cbbca05 commit 02c1bb4
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 32 deletions.
12 changes: 6 additions & 6 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions _infra/helm/party/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 2.5.3
version: 2.5.4

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
appVersion: 2.5.3
appVersion: 2.5.4
44 changes: 24 additions & 20 deletions ras_party/controllers/account_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,19 +389,33 @@ def _send_account_email_changed_notification(email_address, new_email_address, r
logger.info("Verification email sent for changing respondents email", respondent_id=str(respondent.party_uuid))


@with_query_only_db_session
def verify_token(token, session):
def decode_token(token):
"""
Decode the email verification token.
:param token: The email token to decode
:return: Email address associated with decoded token
"""
duration = current_app.config["EMAIL_TOKEN_EXPIRY"]
try:
duration = current_app.config["EMAIL_TOKEN_EXPIRY"]
email_address = decode_email_token(token, duration)
except SignatureExpired:
logger.info("Expired email verification token")
raise Conflict("Expired email verification token")
except (BadSignature, BadData):
logger.exception("Bad token in verify_token")
logger.exception("Bad token in decode_token")
raise NotFound("Unknown email verification token")
return email_address

respondent = query_respondent_by_email(email_address, session)

@with_query_only_db_session
def verify_respondent_by_email(email, session):
"""
Verify respondent by email address
:param email: The email for verification
:param session: Database session
:return: Verification response
"""
respondent = query_respondent_by_email(email, session)
if not respondent:
logger.info("Respondent with Email from token does not exist")
raise NotFound("Respondent does not exist")
Expand Down Expand Up @@ -698,31 +712,21 @@ def notify_change_account_status(payload, party_id: str, session):

@transactional
@with_db_session
def put_email_verification(token, tran, session):
def put_email_verification(email, tran, session):
"""
Verify email address, this method can be reached when registering or updating email address
:param token:
:param email:
:param tran:
:param session: db session
:return: Verified respondent details
"""
logger.info("Attempting to verify email", token=token)
try:
duration = current_app.config["EMAIL_TOKEN_EXPIRY"]
email_address = decode_email_token(token, duration)
except SignatureExpired:
logger.info("Expired email verification token")
raise Conflict("Expired email verification token")
except (BadSignature, BadData):
logger.exception("Bad token in put_email_verification")
raise NotFound("Unknown email verification token")

respondent = query_respondent_by_email(email_address, session)
respondent = query_respondent_by_email(email, session)

if not respondent:
logger.info("Attempting to find respondent by pending email address")
# When changing contact details, unverified new email is in pending_email_address
respondent = query_respondent_by_pending_email(email_address, session)
respondent = query_respondent_by_pending_email(email, session)

if respondent:
update_verified_email_address(respondent, tran)
Expand All @@ -748,7 +752,7 @@ def put_email_verification(token, tran, session):
)

# We set the user as verified on the OAuth2 server.
set_user_verified(email_address)
set_user_verified(email)

return respondent.to_respondent_with_associations_dict()

Expand Down
6 changes: 4 additions & 2 deletions ras_party/views/account_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def put_respondent_by_email():

@account_view.route("/tokens/verify/<token>", methods=["GET"])
def get_verify_token(token):
response = account_controller.verify_token(token)
email = account_controller.decode_token(token)
response = account_controller.verify_respondent_by_email(email)
return make_response(jsonify(response), 200)


Expand All @@ -66,7 +67,8 @@ def post_respondent():

@account_view.route("/emailverification/<token>", methods=["PUT"])
def put_email_verification(token):
response = account_controller.put_email_verification(token)
email = account_controller.decode_token(token)
response = account_controller.put_email_verification(email)
return make_response(jsonify(response), 200)


Expand Down
6 changes: 4 additions & 2 deletions test/test_respondent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,8 @@ def test_verify_token_uses_case_insensitive_email_query():
"ras_party.support.session_decorator.current_app.db"
) as db:
token = generate_email_token("test@example.test")
account_controller.verify_token(token)
email = account_controller.decode_token(token)
account_controller.verify_respondent_by_email(email)
query.assert_called_once_with("test@example.test", db.session())

def test_put_respondent_email_returns_400_when_no_email(self):
Expand Down Expand Up @@ -1214,7 +1215,8 @@ def test_put_email_verification_uses_case_insensitive_email_query(self):
"ras_party.support.session_decorator.current_app.db"
) as db:
token = self.generate_valid_token_from_email("test@example.test")
account_controller.put_email_verification(token)
email = account_controller.decode_token(token)
account_controller.put_email_verification(email)
query.assert_called_once_with("test@example.test", db.session())

def test_token_removed_on_email_update(self):
Expand Down

0 comments on commit 02c1bb4

Please sign in to comment.