From c969e6919a61d4fbca472ececb299b21b6ae4cd8 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 00:03:25 +0100 Subject: [PATCH 1/9] Use the same design as on extension for custom alias: domains are presented in a dropdown list --- .../templates/dashboard/custom_alias.html | 97 ++++------- app/dashboard/views/custom_alias.py | 157 +++++++++--------- 2 files changed, 109 insertions(+), 145 deletions(-) diff --git a/app/dashboard/templates/dashboard/custom_alias.html b/app/dashboard/templates/dashboard/custom_alias.html index a3dd7126..9c3e80e6 100644 --- a/app/dashboard/templates/dashboard/custom_alias.html +++ b/app/dashboard/templates/dashboard/custom_alias.html @@ -9,82 +9,47 @@ {% block default_content %}
-

New Email Alias

- {% if error %} -
{{ error }}
+

New Email Alias

+ + {% if user_custom_domains|length == 0 %} + {% endif %}
- - -
- {% if custom_domains %} -
-
With SimpleLogin domain
-
- {% endif %} - -
-
- -
-
- -

- {{ email_suffix }}@{{ EMAIL_DOMAIN }} -

-
+
+
+
-
-
- Hint - You can use the name of the website that you plan to use the alias on here.
- This way you can remember where the alias is used. -
+
+ {% if suffixes|length > 1 %} + + {% else %} + {{ suffixes[0] }} + {% endif %}
+
-
- -
- +
+
- - {% if custom_domains %} -
-

Or with your custom domains

-
- {% for custom_domain in custom_domains %} -
- - - -
-
- -
- - @{{ custom_domain.domain }} - -
-
- -
- -
-
- - -
- {% endfor %} -
- {% endif %}
{% endblock %} diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index 8ea9cb73..1965e2f9 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -1,5 +1,7 @@ from flask import render_template, redirect, url_for, flash, request, session from flask_login import login_required, current_user +from flask_wtf import FlaskForm +from wtforms import StringField, validators, SelectField from app.config import EMAIL_DOMAIN, HIGHLIGHT_GEN_EMAIL_ID, DISABLE_ALIAS_SUFFIX from app.dashboard.base import dashboard_bp @@ -12,96 +14,93 @@ from app.utils import convert_to_id, random_word, word_exist @dashboard_bp.route("/custom_alias", methods=["GET", "POST"]) @login_required def custom_alias(): - # check if user has the right to create custom alias + # check if user has not exceeded the alias quota if not current_user.can_create_new_alias(): # notify admin LOG.error("user %s tries to create custom alias", current_user) flash("ony premium user can choose custom alias", "warning") return redirect(url_for("dashboard.index")) - error = "" + user_custom_domains = [cd.domain for cd in current_user.verified_custom_domains()] + suffixes = [] + + # put custom domain first + for alias_domain in user_custom_domains: + suffixes.append("@" + alias_domain) + + # then default domain + suffixes.append( + ("" if DISABLE_ALIAS_SUFFIX else ".") + random_word() + "@" + EMAIL_DOMAIN + ) if request.method == "POST": - if request.form.get("form-name") == "non-custom-domain-name": - email_prefix = request.form.get("email-prefix") - email_prefix = convert_to_id(email_prefix) - email_suffix = request.form.get("email-suffix") + alias_prefix = request.form.get("prefix") + alias_suffix = request.form.get("suffix") - # verify email_suffix: do not verify when DISABLE_ALIAS_SUFFIX is set - if not DISABLE_ALIAS_SUFFIX: - # email suffix must be in the format ".{word}" - if email_suffix[0] != "." or not word_exist(email_suffix[1:]): - flash( - "nice try :). The suffix is there so no one can take all the *nice* aliases though", - "warning", - ) - return redirect(url_for("dashboard.custom_alias")) - - if not email_prefix: - error = "alias prefix cannot be empty" - else: - full_email = f"{email_prefix}{email_suffix}@{EMAIL_DOMAIN}" - # check if email already exists - if GenEmail.get_by(email=full_email) or DeletedAlias.get_by( - email=full_email - ): - error = "email already chosen, please choose another one" - else: - # create the new alias - LOG.d( - "create custom alias %s for user %s", full_email, current_user - ) - gen_email = GenEmail.create( - email=full_email, user_id=current_user.id - ) - db.session.commit() - - flash(f"Alias {full_email} has been created", "success") - session[HIGHLIGHT_GEN_EMAIL_ID] = gen_email.id - - return redirect(url_for("dashboard.index")) - elif request.form.get("form-name") == "custom-domain-name": - custom_domain_id = request.form.get("custom-domain-id") - email = request.form.get("email").lower() - - custom_domain = CustomDomain.get(custom_domain_id) - - if not custom_domain: - flash("Unknown error. Refresh the page", "warning") - return redirect(url_for("dashboard.custom_alias")) - elif custom_domain.user_id != current_user.id: - flash("Unknown error. Refresh the page", "warning") - return redirect(url_for("dashboard.custom_alias")) - elif not custom_domain.verified: - flash("Unknown error. Refresh the page", "warning") - return redirect(url_for("dashboard.custom_alias")) - - full_email = f"{email}@{custom_domain.domain}" - - if GenEmail.get_by(email=full_email): - error = f"{full_email} already exist, please choose another one" - else: - LOG.d( - "create custom alias %s for custom domain %s", - full_email, - custom_domain.domain, - ) - gen_email = GenEmail.create( - email=full_email, - user_id=current_user.id, - custom_domain_id=custom_domain.id, + if verify_prefix_suffix( + current_user, alias_prefix, alias_suffix, user_custom_domains + ): + full_alias = alias_prefix + alias_suffix + if GenEmail.get_by(email=full_alias): + LOG.d("full alias already used %s", full_alias) + flash( + f"Alias {full_alias} already exists, please choose another one", + "warning", ) + else: + gen_email = GenEmail.create(user_id=current_user.id, email=full_alias) db.session.commit() - flash(f"Alias {full_email} has been created", "success") + flash(f"Alias {full_alias} has been created", "success") session[HIGHLIGHT_GEN_EMAIL_ID] = gen_email.id - return redirect(url_for("dashboard.index")) - email_suffix = "" if DISABLE_ALIAS_SUFFIX else "." + random_word() - return render_template( - "dashboard/custom_alias.html", - error=error, - email_suffix=email_suffix, - EMAIL_DOMAIN=EMAIL_DOMAIN, - custom_domains=current_user.verified_custom_domains(), - ) + return redirect(url_for("dashboard.index")) + # only happen if the request has been "hacked" + else: + flash("something went wrong", "warning") + + return render_template("dashboard/custom_alias.html", **locals()) + + +def verify_prefix_suffix(user, alias_prefix, alias_suffix, user_custom_domains) -> bool: + """verify if user could create an alias with the given prefix and suffix""" + alias_prefix = alias_prefix.strip() + alias_prefix = convert_to_id(alias_prefix) + if not alias_prefix: # should be caught on frontend + return False + + # make sure alias_suffix is either .random_word@simplelogin.co or @my-domain.com + alias_suffix = alias_suffix.strip() + if alias_suffix.startswith("@"): + alias_domain = alias_suffix[1:] + # alias_domain can be either custom_domain or if DISABLE_ALIAS_SUFFIX, EMAIL_DOMAIN + if DISABLE_ALIAS_SUFFIX: + if alias_domain not in user_custom_domains and alias_domain != EMAIL_DOMAIN: + LOG.error("wrong alias suffix %s, user %s", alias_suffix, user) + return False + else: + if alias_domain not in user_custom_domains: + LOG.error("wrong alias suffix %s, user %s", alias_suffix, user) + return False + else: + if not alias_suffix.startswith("."): + LOG.error("User %s submits a wrong alias suffix %s", user, alias_suffix) + return False + if not alias_suffix.endswith(EMAIL_DOMAIN): + LOG.error( + "Alias suffix should end with default alias domain %s", + user, + alias_suffix, + ) + return False + + random_word_part = alias_suffix[1 : alias_suffix.find("@")] + if not word_exist(random_word_part): + LOG.error( + "alias suffix %s needs to start with a random word, user %s", + alias_suffix, + user, + ) + return False + + return True From f7f8c6769d15ece2e707c75fcb0aec71ae7af0d7 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 09:17:44 +0100 Subject: [PATCH 2/9] Improve custom alias help text --- app/dashboard/templates/dashboard/custom_alias.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/dashboard/templates/dashboard/custom_alias.html b/app/dashboard/templates/dashboard/custom_alias.html index 9c3e80e6..36e3e380 100644 --- a/app/dashboard/templates/dashboard/custom_alias.html +++ b/app/dashboard/templates/dashboard/custom_alias.html @@ -11,11 +11,12 @@

New Email Alias

- {% if user_custom_domains|length == 0 %} + {% if user_custom_domains|length == 0 and not DISABLE_ALIAS_SUFFIX %} {% endif %} From a6507a39e4cf62df087d4454f758ca6a94835826 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 09:46:26 +0100 Subject: [PATCH 3/9] Add optional OTHER_ALIAS_DOMAINS setting. Create ALIAS_DOMAINS setting that is the list of domain user can use when creating alias. --- .env.example | 3 +++ app/config.py | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/.env.example b/.env.example index aaeb00d5..040bd6ed 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,9 @@ URL=http://localhost:7777 # domain used to create alias EMAIL_DOMAIN=sl.local +# other domains that can be used to create aliases, in addition to EMAIL_DOMAIN +OTHER_ALIAS_DOMAINS=["domain1.com", "domain2.com"] + # transactional email is sent from this email address SUPPORT_EMAIL=support@sl.local diff --git a/app/config.py b/app/config.py index a3ebba8a..d6aaf465 100644 --- a/app/config.py +++ b/app/config.py @@ -47,6 +47,16 @@ MAX_NB_EMAIL_FREE_PLAN = int(os.environ["MAX_NB_EMAIL_FREE_PLAN"]) # allow to override postfix server locally POSTFIX_SERVER = os.environ.get("POSTFIX_SERVER", "240.0.0.1") +if "OTHER_ALIAS_DOMAINS" in os.environ: + OTHER_ALIAS_DOMAINS = eval( + os.environ["OTHER_ALIAS_DOMAINS"] + ) # ["domain1.com", "domain2.com"] +else: + OTHER_ALIAS_DOMAINS = [] + +# List of domains user can use to create alias +ALIAS_DOMAINS = OTHER_ALIAS_DOMAINS + [EMAIL_DOMAIN] + # list of (priority, email server) EMAIL_SERVERS_WITH_PRIORITY = eval( os.environ["EMAIL_SERVERS_WITH_PRIORITY"] From d7ed0d77bdfe2757446f18eb4fc2a70fd770cf35 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 10:13:58 +0100 Subject: [PATCH 4/9] add email_belongs_to_alias_domains() to verify if an email belongs to one of the alias domains --- app/email_utils.py | 10 ++++++++++ tests/test_email_utils.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/email_utils.py b/app/email_utils.py index 3a205ff6..334fa4c0 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -14,6 +14,7 @@ from app.config import ( DKIM_SELECTOR, DKIM_PRIVATE_KEY, DKIM_HEADERS, + ALIAS_DOMAINS, ) from app.log import LOG @@ -253,3 +254,12 @@ def delete_header(msg: Message, header: str): for h in msg._headers: if h[0].lower() == header.lower(): msg._headers.remove(h) + + +def email_belongs_to_alias_domains(email: str) -> bool: + """return True if an emails ends with one of the alias domains provided by SimpleLogin""" + for domain in ALIAS_DOMAINS: + if email.endswith("@" + domain): + return True + + return False diff --git a/tests/test_email_utils.py b/tests/test_email_utils.py index 93106747..0b5aad55 100644 --- a/tests/test_email_utils.py +++ b/tests/test_email_utils.py @@ -3,6 +3,7 @@ from app.email_utils import ( get_email_part, get_email_local_part, get_email_domain_part, + email_belongs_to_alias_domains, ) @@ -26,3 +27,12 @@ def test_get_email_local_part(): def test_get_email_domain_part(): assert get_email_domain_part("ab@cd.com") == "cd.com" + + +def test_email_belongs_to_alias_domains(): + # default alias domain + assert email_belongs_to_alias_domains("ab@sl.local") + assert not email_belongs_to_alias_domains("ab@not-exist.local") + + assert email_belongs_to_alias_domains("hey@d1.test") + assert not email_belongs_to_alias_domains("hey@d3.test") From bdf75951f1132ca10e9771c34c159057df9bc520 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 10:22:59 +0100 Subject: [PATCH 5/9] support ALIAS_DOMAINS - use verify_prefix_suffix() in /api/alias/custom/new - --- app/api/views/alias_options.py | 10 ++++++--- app/api/views/new_custom_alias.py | 34 +++++------------------------ app/auth/views/register.py | 6 ++--- app/dashboard/views/custom_alias.py | 31 ++++++++++++++++---------- app/dashboard/views/setting.py | 5 +++-- email_handler.py | 17 ++++++++------- tests/api/test_alias_options.py | 3 ++- tests/api/test_new_custom_alias.py | 19 +++++++++------- tests/env.test | 1 + 9 files changed, 62 insertions(+), 64 deletions(-) diff --git a/app/api/views/alias_options.py b/app/api/views/alias_options.py index d3d3dcf4..c1015028 100644 --- a/app/api/views/alias_options.py +++ b/app/api/views/alias_options.py @@ -3,7 +3,7 @@ from flask_cors import cross_origin from sqlalchemy import desc from app.api.base import api_bp, verify_api_key -from app.config import EMAIL_DOMAIN +from app.config import ALIAS_DOMAINS from app.extensions import db from app.log import LOG from app.models import AliasUsedOn, GenEmail, User @@ -67,9 +67,11 @@ def options(): else: ret["custom"]["suggestion"] = "" + ret["custom"]["suffixes"] = [] # maybe better to make sure the suffix is never used before # but this is ok as there's a check when creating a new custom alias - ret["custom"]["suffixes"] = [f".{random_word()}@{EMAIL_DOMAIN}"] + for domain in ALIAS_DOMAINS: + ret["custom"]["suffixes"].append(f".{random_word()}@{domain}") for custom_domain in user.verified_custom_domains(): ret["custom"]["suffixes"].append("@" + custom_domain.domain) @@ -144,7 +146,9 @@ def options_v2(): # maybe better to make sure the suffix is never used before # but this is ok as there's a check when creating a new custom alias - ret["suffixes"] = [f".{random_word()}@{EMAIL_DOMAIN}"] + # todo: take into account DISABLE_ALIAS_SUFFIX + for domain in ALIAS_DOMAINS: + ret["suffixes"].append(f".{random_word()}@{domain}") for custom_domain in user.verified_custom_domains(): ret["suffixes"].append("@" + custom_domain.domain) diff --git a/app/api/views/new_custom_alias.py b/app/api/views/new_custom_alias.py index 767020f5..e920ddf1 100644 --- a/app/api/views/new_custom_alias.py +++ b/app/api/views/new_custom_alias.py @@ -3,7 +3,8 @@ from flask import jsonify, request from flask_cors import cross_origin from app.api.base import api_bp, verify_api_key -from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN +from app.config import MAX_NB_EMAIL_FREE_PLAN +from app.dashboard.views.custom_alias import verify_prefix_suffix from app.extensions import db from app.log import LOG from app.models import GenEmail, AliasUsedOn @@ -43,35 +44,12 @@ def new_custom_alias(): if not data: return jsonify(error="request body cannot be empty"), 400 - alias_prefix = data.get("alias_prefix", "") - alias_suffix = data.get("alias_suffix", "") - - # make sure alias_prefix is not empty - alias_prefix = alias_prefix.strip() + alias_prefix = data.get("alias_prefix", "").strip() + alias_suffix = data.get("alias_suffix", "").strip() alias_prefix = convert_to_id(alias_prefix) - if not alias_prefix: # should be checked on frontend - LOG.d("user %s submits an empty alias with the prefix %s", user, alias_prefix) - return jsonify(error="alias prefix cannot be empty"), 400 - # make sure alias_suffix is either .random_letters@simplelogin.co or @my-domain.com - alias_suffix = alias_suffix.strip() - if alias_suffix.startswith("@"): - custom_domain = alias_suffix[1:] - if custom_domain not in user_custom_domains: - LOG.d("user %s submits a wrong custom domain %s ", user, custom_domain) - return jsonify(error="error"), 400 - else: - if not alias_suffix.startswith("."): - LOG.d("user %s submits a wrong alias suffix %s", user, alias_suffix) - return jsonify(error="error"), 400 - if not alias_suffix.endswith(EMAIL_DOMAIN): - LOG.d("user %s submits a wrong alias suffix %s", user, alias_suffix) - return jsonify(error="error"), 400 - - random_letters = alias_suffix[1 : alias_suffix.find("@")] - if len(random_letters) < 5: - LOG.d("user %s submits a wrong alias suffix %s", user, alias_suffix) - return jsonify(error="error"), 400 + if not verify_prefix_suffix(user, alias_prefix, alias_suffix, user_custom_domains): + return jsonify(error="wrong alias prefix or suffix"), 400 full_alias = alias_prefix + alias_suffix if GenEmail.get_by(email=full_alias): diff --git a/app/auth/views/register.py b/app/auth/views/register.py index 8e706942..29f216f9 100644 --- a/app/auth/views/register.py +++ b/app/auth/views/register.py @@ -5,7 +5,8 @@ from wtforms import StringField, validators from app import email_utils from app.auth.base import auth_bp -from app.config import URL, EMAIL_DOMAIN +from app.config import URL +from app.email_utils import email_belongs_to_alias_domains from app.extensions import db from app.log import LOG from app.models import User, ActivationCode @@ -31,8 +32,7 @@ def register(): if form.validate_on_submit(): email = form.email.data - - if email.endswith(EMAIL_DOMAIN): + if email_belongs_to_alias_domains(email): flash( "You cannot use alias as your personal inbox. Nice try though 😉", "error", diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index 1965e2f9..ebae7e8a 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -1,13 +1,16 @@ from flask import render_template, redirect, url_for, flash, request, session from flask_login import login_required, current_user -from flask_wtf import FlaskForm -from wtforms import StringField, validators, SelectField -from app.config import EMAIL_DOMAIN, HIGHLIGHT_GEN_EMAIL_ID, DISABLE_ALIAS_SUFFIX +from app.config import ( + HIGHLIGHT_GEN_EMAIL_ID, + DISABLE_ALIAS_SUFFIX, + ALIAS_DOMAINS, +) from app.dashboard.base import dashboard_bp +from app.email_utils import email_belongs_to_alias_domains from app.extensions import db from app.log import LOG -from app.models import GenEmail, DeletedAlias, CustomDomain +from app.models import GenEmail from app.utils import convert_to_id, random_word, word_exist @@ -29,9 +32,10 @@ def custom_alias(): suffixes.append("@" + alias_domain) # then default domain - suffixes.append( - ("" if DISABLE_ALIAS_SUFFIX else ".") + random_word() + "@" + EMAIL_DOMAIN - ) + for domain in ALIAS_DOMAINS: + suffixes.append( + ("" if DISABLE_ALIAS_SUFFIX else "." + random_word()) + "@" + domain + ) if request.method == "POST": alias_prefix = request.form.get("prefix") @@ -73,9 +77,12 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix, user_custom_domains) alias_suffix = alias_suffix.strip() if alias_suffix.startswith("@"): alias_domain = alias_suffix[1:] - # alias_domain can be either custom_domain or if DISABLE_ALIAS_SUFFIX, EMAIL_DOMAIN + # alias_domain can be either custom_domain or if DISABLE_ALIAS_SUFFIX, one of the default ALIAS_DOMAINS if DISABLE_ALIAS_SUFFIX: - if alias_domain not in user_custom_domains and alias_domain != EMAIL_DOMAIN: + if ( + alias_domain not in user_custom_domains + and alias_domain not in ALIAS_DOMAINS + ): LOG.error("wrong alias suffix %s, user %s", alias_suffix, user) return False else: @@ -86,9 +93,11 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix, user_custom_domains) if not alias_suffix.startswith("."): LOG.error("User %s submits a wrong alias suffix %s", user, alias_suffix) return False - if not alias_suffix.endswith(EMAIL_DOMAIN): + + full_alias = alias_prefix + alias_suffix + if not email_belongs_to_alias_domains(full_alias): LOG.error( - "Alias suffix should end with default alias domain %s", + "Alias suffix should end with one of the alias domains %s", user, alias_suffix, ) diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index c9c59e58..f70d2fb8 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -9,8 +9,9 @@ from flask_wtf.file import FileField from wtforms import StringField, validators from app import s3, email_utils -from app.config import URL, EMAIL_DOMAIN +from app.config import URL from app.dashboard.base import dashboard_bp +from app.email_utils import email_belongs_to_alias_domains from app.extensions import db from app.log import LOG from app.models import ( @@ -92,7 +93,7 @@ def setting(): or DeletedAlias.get_by(email=new_email) ): flash(f"Email {new_email} already used", "error") - elif new_email.endswith(EMAIL_DOMAIN): + elif email_belongs_to_alias_domains(new_email): flash( "You cannot use alias as your personal inbox. Nice try though 😉", "error", diff --git a/email_handler.py b/email_handler.py index c281ce67..91d1718c 100644 --- a/email_handler.py +++ b/email_handler.py @@ -38,7 +38,7 @@ from smtplib import SMTP from aiosmtpd.controller import Controller -from app.config import EMAIL_DOMAIN, POSTFIX_SERVER, URL +from app.config import EMAIL_DOMAIN, POSTFIX_SERVER, URL, ALIAS_DOMAINS from app.email_utils import ( get_email_name, get_email_part, @@ -49,6 +49,7 @@ from app.email_utils import ( delete_header, send_cannot_create_directory_alias, send_cannot_create_domain_alias, + email_belongs_to_alias_domains, ) from app.extensions import db from app.log import LOG @@ -120,7 +121,7 @@ class MailHandler: on_the_fly = False # check if alias belongs to a directory, ie having directory/anything@EMAIL_DOMAIN format - if alias.endswith(EMAIL_DOMAIN): + if email_belongs_to_alias_domains(alias): if "/" in alias or "+" in alias or "#" in alias: if "/" in alias: sep = "/" @@ -284,10 +285,10 @@ class MailHandler: forward_email = ForwardEmail.get_by(reply_email=reply_email) alias: str = forward_email.gen_email.email - - # alias must end with EMAIL_DOMAIN or custom-domain alias_domain = alias[alias.find("@") + 1 :] - if alias_domain != EMAIL_DOMAIN: + + # alias must end with one of the ALIAS_DOMAINS or custom-domain + if not email_belongs_to_alias_domains(alias): if not CustomDomain.get_by(domain=alias_domain): return "550 alias unknown by SimpleLogin" @@ -338,9 +339,9 @@ class MailHandler: envelope.rcpt_options, ) - if alias_domain == EMAIL_DOMAIN: - add_dkim_signature(msg, EMAIL_DOMAIN) - # add DKIM-Signature for non-custom-domain alias + if alias_domain in ALIAS_DOMAINS: + add_dkim_signature(msg, alias_domain) + # add DKIM-Signature for custom-domain alias else: custom_domain: CustomDomain = CustomDomain.get_by(domain=alias_domain) if custom_domain.dkim_verified: diff --git a/tests/api/test_alias_options.py b/tests/api/test_alias_options.py index b7ac4e72..47b34ab7 100644 --- a/tests/api/test_alias_options.py +++ b/tests/api/test_alias_options.py @@ -27,7 +27,8 @@ def test_different_scenarios(flask_client): assert r.status_code == 200 assert r.json["can_create_custom"] assert len(r.json["existing"]) == 1 - assert r.json["custom"]["suffixes"] + assert len(r.json["custom"]["suffixes"]) == 3 + assert r.json["custom"]["suggestion"] == "" # no hostname => no suggestion # <<< with hostname >>> diff --git a/tests/api/test_new_custom_alias.py b/tests/api/test_new_custom_alias.py index 4dabd645..e5107956 100644 --- a/tests/api/test_new_custom_alias.py +++ b/tests/api/test_new_custom_alias.py @@ -1,8 +1,9 @@ from flask import url_for -from app.config import EMAIL_DOMAIN +from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN from app.extensions import db from app.models import User, ApiKey, GenEmail +from app.utils import random_word def test_success(flask_client): @@ -15,14 +16,16 @@ def test_success(flask_client): api_key = ApiKey.create(user.id, "for test") db.session.commit() + word = random_word() + r = flask_client.post( url_for("api.new_custom_alias", hostname="www.test.com"), headers={"Authentication": api_key.code}, - json={"alias_prefix": "prefix", "alias_suffix": f".abcdef@{EMAIL_DOMAIN}"}, + json={"alias_prefix": "prefix", "alias_suffix": f".{word}@{EMAIL_DOMAIN}"}, ) assert r.status_code == 201 - assert r.json["alias"] == f"prefix.abcdef@{EMAIL_DOMAIN}" + assert r.json["alias"] == f"prefix.{word}@{EMAIL_DOMAIN}" def test_out_of_quota(flask_client): @@ -35,15 +38,15 @@ def test_out_of_quota(flask_client): api_key = ApiKey.create(user.id, "for test") db.session.commit() - # create 3 custom alias to run out of quota - GenEmail.create_new(user.id, prefix="test") - GenEmail.create_new(user.id, prefix="test") - GenEmail.create_new(user.id, prefix="test") + # create MAX_NB_EMAIL_FREE_PLAN custom alias to run out of quota + for _ in range(MAX_NB_EMAIL_FREE_PLAN): + GenEmail.create_new(user.id, prefix="test") + word = random_word() r = flask_client.post( url_for("api.new_custom_alias", hostname="www.test.com"), headers={"Authentication": api_key.code}, - json={"alias_prefix": "prefix", "alias_suffix": f".abcdef@{EMAIL_DOMAIN}"}, + json={"alias_prefix": "prefix", "alias_suffix": f".{word}@{EMAIL_DOMAIN}"}, ) assert r.status_code == 400 diff --git a/tests/env.test b/tests/env.test index 470aeb13..7835aae4 100644 --- a/tests/env.test +++ b/tests/env.test @@ -5,6 +5,7 @@ URL=http://localhost # Only print email content, not sending it NOT_SEND_EMAIL=true EMAIL_DOMAIN=sl.local +OTHER_ALIAS_DOMAINS=["d1.test", "d2.test"] SUPPORT_EMAIL=support@sl.local ADMIN_EMAIL=to_fill # Max number emails user can generate for free plan From 8e7f5e56d66f51bb727bf6acee7a64cf4c7ba998 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 14:04:38 +0100 Subject: [PATCH 6/9] add "your domain" to user's owned domain --- app/dashboard/templates/dashboard/custom_alias.html | 10 +++++++--- app/dashboard/views/custom_alias.py | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/dashboard/templates/dashboard/custom_alias.html b/app/dashboard/templates/dashboard/custom_alias.html index 36e3e380..4bab5151 100644 --- a/app/dashboard/templates/dashboard/custom_alias.html +++ b/app/dashboard/templates/dashboard/custom_alias.html @@ -36,13 +36,17 @@ {% if suffixes|length > 1 %} {% else %} - {{ suffixes[0] }} + {{ suffixes[0][1] }} {% endif %}
diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index ebae7e8a..6996ded7 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -25,16 +25,17 @@ def custom_alias(): return redirect(url_for("dashboard.index")) user_custom_domains = [cd.domain for cd in current_user.verified_custom_domains()] + # List of (is_custom_domain, alias-suffix) suffixes = [] # put custom domain first for alias_domain in user_custom_domains: - suffixes.append("@" + alias_domain) + suffixes.append((True, "@" + alias_domain)) # then default domain for domain in ALIAS_DOMAINS: suffixes.append( - ("" if DISABLE_ALIAS_SUFFIX else "." + random_word()) + "@" + domain + (False, ("" if DISABLE_ALIAS_SUFFIX else "." + random_word()) + "@" + domain) ) if request.method == "POST": From da8b110839b53bdc1a50aec672ebb49c55c97dec Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 14:05:42 +0100 Subject: [PATCH 7/9] small fix --- app/dashboard/templates/dashboard/custom_alias.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/dashboard/templates/dashboard/custom_alias.html b/app/dashboard/templates/dashboard/custom_alias.html index 4bab5151..90074a3e 100644 --- a/app/dashboard/templates/dashboard/custom_alias.html +++ b/app/dashboard/templates/dashboard/custom_alias.html @@ -23,7 +23,8 @@
- Date: Wed, 22 Jan 2020 14:08:00 +0100 Subject: [PATCH 8/9] fix styling --- app/dashboard/views/custom_alias.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index 6996ded7..984b1cd7 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -35,7 +35,10 @@ def custom_alias(): # then default domain for domain in ALIAS_DOMAINS: suffixes.append( - (False, ("" if DISABLE_ALIAS_SUFFIX else "." + random_word()) + "@" + domain) + ( + False, + ("" if DISABLE_ALIAS_SUFFIX else "." + random_word()) + "@" + domain, + ) ) if request.method == "POST": From 926cc2935c8b5e0d4fef7958b72f2c5c87516994 Mon Sep 17 00:00:00 2001 From: Son NK Date: Wed, 22 Jan 2020 14:21:01 +0100 Subject: [PATCH 9/9] take into account DISABLE_ALIAS_SUFFIX in /api/alias/options --- app/api/views/alias_options.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/api/views/alias_options.py b/app/api/views/alias_options.py index c1015028..1d9c7040 100644 --- a/app/api/views/alias_options.py +++ b/app/api/views/alias_options.py @@ -3,7 +3,7 @@ from flask_cors import cross_origin from sqlalchemy import desc from app.api.base import api_bp, verify_api_key -from app.config import ALIAS_DOMAINS +from app.config import ALIAS_DOMAINS, DISABLE_ALIAS_SUFFIX from app.extensions import db from app.log import LOG from app.models import AliasUsedOn, GenEmail, User @@ -71,7 +71,10 @@ def options(): # maybe better to make sure the suffix is never used before # but this is ok as there's a check when creating a new custom alias for domain in ALIAS_DOMAINS: - ret["custom"]["suffixes"].append(f".{random_word()}@{domain}") + if DISABLE_ALIAS_SUFFIX: + ret["custom"]["suffixes"].append(f"@{domain}") + else: + ret["custom"]["suffixes"].append(f".{random_word()}@{domain}") for custom_domain in user.verified_custom_domains(): ret["custom"]["suffixes"].append("@" + custom_domain.domain) @@ -146,9 +149,11 @@ def options_v2(): # maybe better to make sure the suffix is never used before # but this is ok as there's a check when creating a new custom alias - # todo: take into account DISABLE_ALIAS_SUFFIX for domain in ALIAS_DOMAINS: - ret["suffixes"].append(f".{random_word()}@{domain}") + if DISABLE_ALIAS_SUFFIX: + ret["suffixes"].append(f"@{domain}") + else: + ret["suffixes"].append(f".{random_word()}@{domain}") for custom_domain in user.verified_custom_domains(): ret["suffixes"].append("@" + custom_domain.domain)