diff --git a/README.md b/README.md index faf537fb..9eae6fff 100644 --- a/README.md +++ b/README.md @@ -1197,6 +1197,7 @@ Input: - `Authentication` header that contains the api key - `mailbox_id`: in url - (optional) `default`: boolean. Set a mailbox as default mailbox. +- (optional) `email`: email address. Change a mailbox email address. Output: - 200 if updated successfully diff --git a/app/api/views/mailbox.py b/app/api/views/mailbox.py index 632f26c1..40dc174a 100644 --- a/app/api/views/mailbox.py +++ b/app/api/views/mailbox.py @@ -1,3 +1,5 @@ +from smtplib import SMTPRecipientsRefused + from flask import g from flask import jsonify from flask import request @@ -5,6 +7,7 @@ from flask_cors import cross_origin from app.api.base import api_bp, require_api_auth from app.dashboard.views.mailbox import send_verification_email +from app.dashboard.views.mailbox_detail import verify_mailbox_change from app.email_utils import ( mailbox_already_used, email_domain_can_be_used_as_mailbox, @@ -93,7 +96,8 @@ def update_mailbox(mailbox_id): Update mailbox Input: mailbox_id: in url - default (optional): in body + (optional) default: in body. Set a mailbox as the default mailbox. + (optional) email: in body. Change a mailbox email. Output: 200 if updated successfully @@ -112,7 +116,29 @@ def update_mailbox(mailbox_id): user.default_mailbox_id = mailbox.id changed = True + if "email" in data: + new_email = data.get("email").lower().strip() + + if mailbox_already_used(new_email, user): + return jsonify(error=f"{new_email} already used"), 400 + elif not email_domain_can_be_used_as_mailbox(new_email): + return ( + jsonify( + error=f"{new_email} cannot be used. Please note a mailbox cannot " + f"be a disposable email address" + ), + 400, + ) + + try: + verify_mailbox_change(user, mailbox, new_email) + except SMTPRecipientsRefused: + return jsonify(error=f"Incorrect mailbox, please recheck {new_email}"), 400 + else: + mailbox.new_email = new_email + changed = True + if changed: db.session.commit() - return jsonify(deleted=True), 200 + return jsonify(updated=True), 200 diff --git a/app/dashboard/views/mailbox_detail.py b/app/dashboard/views/mailbox_detail.py index 53478a33..a3ec4781 100644 --- a/app/dashboard/views/mailbox_detail.py +++ b/app/dashboard/views/mailbox_detail.py @@ -60,33 +60,8 @@ def mailbox_detail_route(mailbox_id): mailbox.new_email = new_email db.session.commit() - s = Signer(MAILBOX_SECRET) - mailbox_id_signed = s.sign(str(mailbox.id)).decode() - verification_url = ( - URL - + "/dashboard/mailbox/confirm_change" - + f"?mailbox_id={mailbox_id_signed}" - ) - try: - send_email( - new_email, - f"Confirm mailbox change on SimpleLogin", - render( - "transactional/verify-mailbox-change.txt", - user=current_user, - link=verification_url, - mailbox_email=mailbox.email, - mailbox_new_email=new_email, - ), - render( - "transactional/verify-mailbox-change.html", - user=current_user, - link=verification_url, - mailbox_email=mailbox.email, - mailbox_new_email=new_email, - ), - ) + verify_mailbox_change(current_user, mailbox, new_email) except SMTPRecipientsRefused: flash( f"Incorrect mailbox, please recheck {mailbox.email}", @@ -151,6 +126,33 @@ def mailbox_detail_route(mailbox_id): return render_template("dashboard/mailbox_detail.html", **locals()) +def verify_mailbox_change(user, mailbox, new_email): + s = Signer(MAILBOX_SECRET) + mailbox_id_signed = s.sign(str(mailbox.id)).decode() + verification_url = ( + URL + "/dashboard/mailbox/confirm_change" + f"?mailbox_id={mailbox_id_signed}" + ) + + send_email( + new_email, + f"Confirm mailbox change on SimpleLogin", + render( + "transactional/verify-mailbox-change.txt", + user=user, + link=verification_url, + mailbox_email=mailbox.email, + mailbox_new_email=new_email, + ), + render( + "transactional/verify-mailbox-change.html", + user=user, + link=verification_url, + mailbox_email=mailbox.email, + mailbox_new_email=new_email, + ), + ) + + @dashboard_bp.route( "/mailbox//cancel_email_change", methods=["GET", "POST"] ) diff --git a/tests/api/test_mailbox.py b/tests/api/test_mailbox.py index 802c1f03..a4bc9910 100644 --- a/tests/api/test_mailbox.py +++ b/tests/api/test_mailbox.py @@ -70,7 +70,7 @@ def test_delete_default_mailbox(flask_client): assert r.status_code == 400 -def test_update_mailbox(flask_client): +def test_set_mailbox_as_default(flask_client): user = User.create( email="a@b.c", password="password", name="Test User", activated=True ) @@ -95,3 +95,29 @@ def test_update_mailbox(flask_client): mb = Mailbox.get(mb.id) assert user.default_mailbox_id == mb.id + + +def test_update_mailbox_email(flask_client): + user = User.create( + email="a@b.c", password="password", name="Test User", activated=True + ) + db.session.commit() + + # create api_key + api_key = ApiKey.create(user.id, "for test") + db.session.commit() + + # create a mailbox + mb = Mailbox.create(user_id=user.id, email="mb@gmail.com") + db.session.commit() + + r = flask_client.put( + url_for("api.delete_mailbox", mailbox_id=mb.id), + headers={"Authentication": api_key.code}, + json={"email": "new-email@gmail.com"}, + ) + + assert r.status_code == 200 + + mb = Mailbox.get(mb.id) + assert mb.new_email == "new-email@gmail.com"