diff --git a/app/api/views/mailbox.py b/app/api/views/mailbox.py index 1e86b5b6..10bba2c2 100644 --- a/app/api/views/mailbox.py +++ b/app/api/views/mailbox.py @@ -41,7 +41,7 @@ def create_mailbox(): mailbox_email = sanitize_email(request.get_json().get("email")) try: - new_mailbox = mailbox_utils.create_mailbox(user, mailbox_email) + new_mailbox = mailbox_utils.create_mailbox(user, mailbox_email).mailbox except mailbox_utils.MailboxError as e: return jsonify(error=e.msg), 400 diff --git a/app/dashboard/views/mailbox.py b/app/dashboard/views/mailbox.py index bf0ca832..0746387a 100644 --- a/app/dashboard/views/mailbox.py +++ b/app/dashboard/views/mailbox.py @@ -87,7 +87,9 @@ def mailbox_route(): return redirect(request.url) mailbox_email = new_mailbox_form.email.data.lower().strip().replace(" ", "") try: - mailbox = mailbox_utils.create_mailbox(current_user, mailbox_email) + mailbox = mailbox_utils.create_mailbox( + current_user, mailbox_email + ).mailbox except mailbox_utils.MailboxError as e: flash(e.msg, "warning") return redirect(url_for("dashboard.mailbox_route")) diff --git a/app/mailbox_utils.py b/app/mailbox_utils.py index 54a41932..39f364c1 100644 --- a/app/mailbox_utils.py +++ b/app/mailbox_utils.py @@ -1,3 +1,4 @@ +import dataclasses import secrets import random from typing import Optional @@ -17,6 +18,12 @@ from app.log import LOG from app.models import User, Mailbox, Job, MailboxActivation +@dataclasses.dataclass +class CreateMailboxOutput: + mailbox: Mailbox + activation: Optional[MailboxActivation] + + class MailboxError(Exception): def __init__(self, msg: str): self.msg = msg @@ -42,7 +49,7 @@ def create_mailbox( send_email: bool = True, use_digit_codes: bool = False, send_link: bool = True, -) -> Mailbox: +) -> CreateMailboxOutput: if not user.is_premium(): LOG.i( f"User {user} has tried to create mailbox with {email} but is not premium" @@ -69,14 +76,15 @@ def create_mailbox( if verified: LOG.i(f"User {user} as created a pre-verified mailbox with {email}") - return new_mailbox + return CreateMailboxOutput(mailbox=new_mailbox, activation=None) LOG.i(f"User {user} has created mailbox with {email}") activation = generate_activation_code(new_mailbox, use_digit_code=use_digit_codes) + output = CreateMailboxOutput(mailbox=new_mailbox, activation=activation) if not send_email: LOG.i(f"Skipping sending validation email for mailbox {new_mailbox}") - return new_mailbox + return output send_verification_email( user, @@ -84,7 +92,7 @@ def create_mailbox( activation=activation, send_link=send_link, ) - return new_mailbox + return output def delete_mailbox( diff --git a/tests/test_mailbox_utils.py b/tests/test_mailbox_utils.py index 592b626e..bc3bf39e 100644 --- a/tests/test_mailbox_utils.py +++ b/tests/test_mailbox_utils.py @@ -71,7 +71,10 @@ def test_create_mailbox(): @mail_sender.store_emails_test_decorator def test_create_mailbox_verified(): email = random_email() - mailbox_utils.create_mailbox(user, email, verified=True) + output = mailbox_utils.create_mailbox(user, email, verified=True) + assert output.mailbox is not None + assert output.mailbox.verified + assert output.activation is None mailbox = Mailbox.get_by(email=email) assert mailbox is not None assert mailbox.verified @@ -84,19 +87,26 @@ def test_create_mailbox_verified(): @mail_sender.store_emails_test_decorator def test_create_mailbox_with_digits(): email = random_email() - mailbox_utils.create_mailbox(user, email, use_digit_codes=True, send_link=False) + output = mailbox_utils.create_mailbox( + user, email, use_digit_codes=True, send_link=False + ) + assert output.activation is not None + assert output.activation.tries == 0 + assert len(output.activation.code) == 6 + mailbox = Mailbox.get_by(email=email) assert mailbox is not None assert not mailbox.verified + assert output.mailbox.id == mailbox.id + activation = MailboxActivation.get_by(mailbox_id=mailbox.id) assert activation is not None - assert activation.tries == 0 - assert len(activation.code) == 6 + assert output.activation.mailbox_id == activation.mailbox_id assert 1 == len(mail_sender.get_stored_emails()) mail_sent = mail_sender.get_stored_emails()[0] mail_contents = str(mail_sent.msg) - assert mail_contents.find(activation.code) > 0 + assert mail_contents.find(output.activation.code) > 0 assert mail_contents.find(config.URL) == -1 assert mail_sent.envelope_to == email @@ -104,14 +114,18 @@ def test_create_mailbox_with_digits(): @mail_sender.store_emails_test_decorator def test_create_mailbox_without_verification_email(): email = random_email() - mailbox_utils.create_mailbox(user, email, use_digit_codes=True, send_email=False) + output = mailbox_utils.create_mailbox( + user, email, use_digit_codes=True, send_email=False + ) mailbox = Mailbox.get_by(email=email) assert mailbox is not None assert not mailbox.verified + assert mailbox.id == output.mailbox.id activation = MailboxActivation.get_by(mailbox_id=mailbox.id) assert activation is not None assert activation.tries == 0 assert len(activation.code) == 6 + assert activation.code == output.activation.code assert 0 == len(mail_sender.get_stored_emails()) @@ -168,7 +182,7 @@ def test_transfer_to_same_mailbox(): email = random_email() mailbox = mailbox_utils.create_mailbox( user, email, use_digit_codes=True, send_link=False - ) + ).mailbox with pytest.raises(mailbox_utils.MailboxError): mailbox_utils.delete_mailbox(user, mailbox.id, transfer_mailbox_id=mailbox.id) @@ -177,7 +191,7 @@ def test_transfer_to_other_users_mailbox(): email = random_email() mailbox = mailbox_utils.create_mailbox( user, email, use_digit_codes=True, send_link=False - ) + ).mailbox other = create_new_user() other_mailbox = Mailbox.create(user_id=other.id, email=random_email(), commit=True) with pytest.raises(mailbox_utils.MailboxError): @@ -190,7 +204,7 @@ def test_delete_with_no_transfer(): email = random_email() mailbox = mailbox_utils.create_mailbox( user, email, use_digit_codes=True, send_link=False - ) + ).mailbox mailbox_utils.delete_mailbox(user, mailbox.id, transfer_mailbox_id=None) job = Session.query(Job).order_by(Job.id.desc()).first() assert job is not None @@ -202,10 +216,10 @@ def test_delete_with_no_transfer(): def test_delete_with_transfer(): mailbox = mailbox_utils.create_mailbox( user, random_email(), use_digit_codes=True, send_link=False - ) + ).mailbox transfer_mailbox = mailbox_utils.create_mailbox( user, random_email(), use_digit_codes=True, send_link=False - ) + ).mailbox mailbox_utils.delete_mailbox( user, mailbox.id, transfer_mailbox_id=transfer_mailbox.id ) @@ -246,42 +260,45 @@ def test_verify_other_users_mailbox(): @mail_sender.store_emails_test_decorator def test_verify_fail(): - mailbox = mailbox_utils.create_mailbox(user, random_email()) + output = mailbox_utils.create_mailbox(user, random_email()) for i in range(mailbox_utils.MAX_ACTIVATION_TRIES - 1): try: - mailbox_utils.verify_mailbox_code(user, mailbox.id, "9999999") + mailbox_utils.verify_mailbox_code( + user, output.mailbox.id, output.activation.code + "nop" + ) assert False, f"test {i}" except mailbox_utils.CannotVerifyError: - activation = MailboxActivation.get_by(mailbox_id=mailbox.id) + activation = MailboxActivation.get_by(mailbox_id=output.mailbox.id) assert activation.tries == i + 1 @mail_sender.store_emails_test_decorator def test_verify_too_may(): - mailbox = mailbox_utils.create_mailbox(user, random_email()) - activation = MailboxActivation.get_by(mailbox_id=mailbox.id) - activation.tries = mailbox_utils.MAX_ACTIVATION_TRIES + output = mailbox_utils.create_mailbox(user, random_email()) + output.activation.tries = mailbox_utils.MAX_ACTIVATION_TRIES Session.commit() with pytest.raises(mailbox_utils.CannotVerifyError): - mailbox_utils.verify_mailbox_code(user, mailbox.id, activation.code) + mailbox_utils.verify_mailbox_code( + user, output.mailbox.id, output.activation.code + ) @mail_sender.store_emails_test_decorator def test_verify_too_old_code(): - mailbox = mailbox_utils.create_mailbox(user, random_email()) - activation = MailboxActivation.get_by(mailbox_id=mailbox.id) - activation.created_at = arrow.now().shift(minutes=-30) + output = mailbox_utils.create_mailbox(user, random_email()) + output.activation.created_at = arrow.now().shift(minutes=-30) Session.commit() with pytest.raises(mailbox_utils.CannotVerifyError): - mailbox_utils.verify_mailbox_code(user, mailbox.id, activation.code) + mailbox_utils.verify_mailbox_code( + user, output.mailbox.id, output.activation.code + ) @mail_sender.store_emails_test_decorator def test_verify_ok(): - mailbox = mailbox_utils.create_mailbox(user, random_email()) - activation = MailboxActivation.get_by(mailbox_id=mailbox.id) - mailbox_utils.verify_mailbox_code(user, mailbox.id, activation.code) - activation = MailboxActivation.get_by(mailbox_id=mailbox.id) + output = mailbox_utils.create_mailbox(user, random_email()) + mailbox_utils.verify_mailbox_code(user, output.mailbox.id, output.activation.code) + activation = MailboxActivation.get_by(mailbox_id=output.mailbox.id) assert activation is None - mailbox = Mailbox.get(id=mailbox.id) + mailbox = Mailbox.get(id=output.mailbox.id) assert mailbox.verified