From aeef9ccca90a6dfacfc2abc1db7ceb9584d84271 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 5 Oct 2020 21:00:52 +0200 Subject: [PATCH 1/5] Support random suffix for personal domains --- app/dashboard/views/custom_alias.py | 61 ++++++++++++----------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index 30880a73..ca462feb 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -8,7 +8,6 @@ from app.config import ( CUSTOM_ALIAS_SECRET, ) 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 ( @@ -33,9 +32,14 @@ def available_suffixes(user: User) -> [bool, str, str]: suffixes = [] # put custom domain first + # for each user domain, generate both the domain and a random suffix version for alias_domain in user_custom_domains: - suffix = "@" + alias_domain - suffixes.append((True, suffix, signer.sign(suffix).decode())) + domain_suffixes = [ + "@" + alias_domain, + "." + random_word() + "@" + alias_domain + ] + for suffix in domain_suffixes: + suffixes.append((True, suffix, signer.sign(suffix).decode())) # then default domain for domain in ALIAS_DOMAINS: @@ -174,41 +178,24 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix) -> bool: # 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, one of the default ALIAS_DOMAINS - if DISABLE_ALIAS_SUFFIX: - if ( - alias_domain not in user_custom_domains - and alias_domain not in ALIAS_DOMAINS - ): - LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) - return False - else: - if alias_domain not in user_custom_domains: - LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) - return False + alias_domain_prefix, alias_domain = alias_suffix.split("@", 1) + + if alias_domain_prefix: + if not alias_domain_prefix.startswith(".") or len(alias_domain_prefix) < 2: + LOG.exception("nonsensical alias suffix %s, user %s", alias_domain_prefix, user) + return False + + if alias_domain not in user_custom_domains and alias_domain not in ALIAS_DOMAINS: + LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) + return False else: - if not alias_suffix.startswith("."): - LOG.exception("User %s submits a wrong alias suffix %s", user, alias_suffix) - return False + if alias_domain not in user_custom_domains: + if not DISABLE_ALIAS_SUFFIX: + LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) + return False - full_alias = alias_prefix + alias_suffix - if not email_belongs_to_alias_domains(full_alias): - LOG.exception( - "Alias suffix should end with one of the alias domains %s", - user, - alias_suffix, - ) - return False - - random_word_part = alias_suffix[1 : alias_suffix.find("@")] - if not word_exist(random_word_part): - LOG.exception( - "alias suffix %s needs to start with a random word, user %s", - alias_suffix, - user, - ) - return False + if alias_domain not in ALIAS_DOMAINS: + LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) + return False return True From 739fb50b04585fea310ba212fb6f4fbcdc4872a6 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Mon, 5 Oct 2020 21:12:13 +0200 Subject: [PATCH 2/5] Format with Black --- app/dashboard/views/custom_alias.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index ca462feb..ec8fb308 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -34,10 +34,7 @@ def available_suffixes(user: User) -> [bool, str, str]: # put custom domain first # for each user domain, generate both the domain and a random suffix version for alias_domain in user_custom_domains: - domain_suffixes = [ - "@" + alias_domain, - "." + random_word() + "@" + alias_domain - ] + domain_suffixes = ["@" + alias_domain, "." + random_word() + "@" + alias_domain] for suffix in domain_suffixes: suffixes.append((True, suffix, signer.sign(suffix).decode())) @@ -182,10 +179,15 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix) -> bool: if alias_domain_prefix: if not alias_domain_prefix.startswith(".") or len(alias_domain_prefix) < 2: - LOG.exception("nonsensical alias suffix %s, user %s", alias_domain_prefix, user) + LOG.exception( + "nonsensical alias suffix %s, user %s", alias_domain_prefix, user + ) return False - if alias_domain not in user_custom_domains and alias_domain not in ALIAS_DOMAINS: + if ( + alias_domain not in user_custom_domains + and alias_domain not in ALIAS_DOMAINS + ): LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) return False else: From 5486f5495573f9b96d1c1bbac1d0d6d959612a5c Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 7 Oct 2020 17:57:02 +0200 Subject: [PATCH 3/5] refactor verify_prefix --- app/dashboard/views/custom_alias.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index ec8fb308..0d82cc25 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -175,20 +175,29 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix) -> bool: # make sure alias_suffix is either .random_word@simplelogin.co or @my-domain.com alias_suffix = alias_suffix.strip() + # alias_domain_prefix is either a .random_word or "" alias_domain_prefix, alias_domain = alias_suffix.split("@", 1) - if alias_domain_prefix: - if not alias_domain_prefix.startswith(".") or len(alias_domain_prefix) < 2: - LOG.exception( - "nonsensical alias suffix %s, user %s", alias_domain_prefix, user - ) + # alias_domain must be either one of user custom domains or built-in domains + if alias_domain not in user_custom_domains and alias_domain not in ALIAS_DOMAINS: + LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) + return False + + # built-in domain case: + # 1) alias_suffix must start with "." and + # 2) alias_domain_prefix must come from the word list + if alias_domain in ALIAS_DOMAINS and alias_domain not in user_custom_domains: + if not alias_domain_prefix.startswith("."): + LOG.exception("User %s submits a wrong alias suffix %s", user, alias_suffix) return False - if ( - alias_domain not in user_custom_domains - and alias_domain not in ALIAS_DOMAINS - ): - LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user) + random_word_part = alias_domain_prefix[1:] + if not word_exist(random_word_part): + LOG.exception( + "alias suffix %s needs to start with a random word, user %s", + alias_suffix, + user, + ) return False else: if alias_domain not in user_custom_domains: From 26d043700942ec1d936ca32b4ddd15cde8f7512d Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Fri, 9 Oct 2020 22:54:13 +0200 Subject: [PATCH 4/5] Make prefix generation configurable per domain --- .../dashboard/domain_detail/info.html | 30 +++++++++++++++++-- app/dashboard/views/custom_alias.py | 8 +++-- app/dashboard/views/domain_detail.py | 17 +++++++++++ app/models.py | 3 ++ .../versions/2020_100922_a90e423c6763_.py | 29 ++++++++++++++++++ 5 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 migrations/versions/2020_100922_a90e423c6763_.py diff --git a/app/dashboard/templates/dashboard/domain_detail/info.html b/app/dashboard/templates/dashboard/domain_detail/info.html index 6274e85f..f279e91e 100644 --- a/app/dashboard/templates/dashboard/domain_detail/info.html +++ b/app/dashboard/templates/dashboard/domain_detail/info.html @@ -57,7 +57,7 @@
-
Default Alias name
+
Default Alias Name
This name will be used as the default alias name when you send or reply from an alias, unless overwritten by the alias specific name. @@ -76,6 +76,32 @@
+
+
Random Prefix Generation
+
+ A random prefix can be generated for this domain for usage in the New Alias + feature. +
+ +
+
+ + +
+
+

Delete Domain

Please note that this operation is irreversible. @@ -119,4 +145,4 @@ }) }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/app/dashboard/views/custom_alias.py b/app/dashboard/views/custom_alias.py index 0d82cc25..a6cb8ef7 100644 --- a/app/dashboard/views/custom_alias.py +++ b/app/dashboard/views/custom_alias.py @@ -26,7 +26,7 @@ signer = TimestampSigner(CUSTOM_ALIAS_SECRET) def available_suffixes(user: User) -> [bool, str, str]: """Return (is_custom_domain, alias-suffix, time-signed alias-suffix)""" - user_custom_domains = [cd.domain for cd in user.verified_custom_domains()] + user_custom_domains = user.verified_custom_domains() # List of (is_custom_domain, alias-suffix, time-signed alias-suffix) suffixes = [] @@ -34,8 +34,10 @@ def available_suffixes(user: User) -> [bool, str, str]: # put custom domain first # for each user domain, generate both the domain and a random suffix version for alias_domain in user_custom_domains: - domain_suffixes = ["@" + alias_domain, "." + random_word() + "@" + alias_domain] - for suffix in domain_suffixes: + suffix = "@" + alias_domain.domain + suffixes.append((True, suffix, signer.sign(suffix).decode())) + if alias_domain.random_prefix_generation: + suffix = "." + random_word() + "@" + alias_domain.domain suffixes.append((True, suffix, signer.sign(suffix).decode())) # then default domain diff --git a/app/dashboard/views/domain_detail.py b/app/dashboard/views/domain_detail.py index 4d539175..f1327650 100644 --- a/app/dashboard/views/domain_detail.py +++ b/app/dashboard/views/domain_detail.py @@ -160,6 +160,23 @@ def domain_detail(custom_domain_id): return redirect( url_for("dashboard.domain_detail", custom_domain_id=custom_domain.id) ) + elif request.form.get("form-name") == "switch-random-prefix-generation": + custom_domain.random_prefix_generation = not custom_domain.random_prefix_generation + db.session.commit() + + if custom_domain.random_prefix_generation: + flash( + f"Random prefix generation has been enabled for {custom_domain.domain}", + "success", + ) + else: + flash( + f"Random prefix generation has been disabled for {custom_domain.domain}", + "warning", + ) + return redirect( + url_for("dashboard.domain_detail", custom_domain_id=custom_domain.id) + ) elif request.form.get("form-name") == "delete": name = custom_domain.domain CustomDomain.delete(custom_domain_id) diff --git a/app/models.py b/app/models.py index bbddd73d..242cea88 100644 --- a/app/models.py +++ b/app/models.py @@ -1449,6 +1449,9 @@ class CustomDomain(db.Model, ModelMixin): # an alias is created automatically the first time it receives an email catch_all = db.Column(db.Boolean, nullable=False, default=False, server_default="0") + # option to generate random prefix version automatically + random_prefix_generation = db.Column(db.Boolean, nullable=False, default=False, server_default="0") + user = db.relationship(User, foreign_keys=[user_id]) @property diff --git a/migrations/versions/2020_100922_a90e423c6763_.py b/migrations/versions/2020_100922_a90e423c6763_.py new file mode 100644 index 00000000..1014020c --- /dev/null +++ b/migrations/versions/2020_100922_a90e423c6763_.py @@ -0,0 +1,29 @@ +"""empty message + +Revision ID: a90e423c6763 +Revises: 1abfc9e14d7e +Create Date: 2020-10-09 22:35:11.359186 + +""" +import sqlalchemy_utils +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a90e423c6763' +down_revision = '1abfc9e14d7e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('custom_domain', sa.Column('random_prefix_generation', sa.Boolean(), server_default='0', nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('custom_domain', 'random_prefix_generation') + # ### end Alembic commands ### From 97020375732ec4808677781050d97e9db4bd3cb1 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Fri, 9 Oct 2020 23:00:10 +0200 Subject: [PATCH 5/5] Reformat with Black --- app/dashboard/views/domain_detail.py | 4 +++- app/models.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/dashboard/views/domain_detail.py b/app/dashboard/views/domain_detail.py index f1327650..09f5c604 100644 --- a/app/dashboard/views/domain_detail.py +++ b/app/dashboard/views/domain_detail.py @@ -161,7 +161,9 @@ def domain_detail(custom_domain_id): url_for("dashboard.domain_detail", custom_domain_id=custom_domain.id) ) elif request.form.get("form-name") == "switch-random-prefix-generation": - custom_domain.random_prefix_generation = not custom_domain.random_prefix_generation + custom_domain.random_prefix_generation = ( + not custom_domain.random_prefix_generation + ) db.session.commit() if custom_domain.random_prefix_generation: diff --git a/app/models.py b/app/models.py index 242cea88..855276b2 100644 --- a/app/models.py +++ b/app/models.py @@ -1450,7 +1450,9 @@ class CustomDomain(db.Model, ModelMixin): catch_all = db.Column(db.Boolean, nullable=False, default=False, server_default="0") # option to generate random prefix version automatically - random_prefix_generation = db.Column(db.Boolean, nullable=False, default=False, server_default="0") + random_prefix_generation = db.Column( + db.Boolean, nullable=False, default=False, server_default="0" + ) user = db.relationship(User, foreign_keys=[user_id])