diff --git a/app/dashboard/templates/dashboard/setting.html b/app/dashboard/templates/dashboard/setting.html index 6be24b01..da7023ca 100644 --- a/app/dashboard/templates/dashboard/setting.html +++ b/app/dashboard/templates/dashboard/setting.html @@ -132,11 +132,12 @@ -
+
Random Alias
-
Change the way random aliases are generated by default.
-
+ +
Change the way random aliases are generated by default.
+ + + +
+ {% endif %}
diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index 8b3b6442..d1407207 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -10,7 +10,7 @@ from wtforms import StringField, validators from wtforms.fields.html5 import EmailField from app import s3, email_utils -from app.config import URL +from app.config import URL, FIRST_ALIAS_DOMAIN from app.dashboard.base import dashboard_bp from app.email_utils import ( email_domain_can_be_used_as_mailbox, @@ -159,6 +159,30 @@ def setting(): flash("Your preference has been updated", "success") return redirect(url_for("dashboard.setting")) + elif request.form.get("form-name") == "change-random-alias-default-domain": + default_domain = request.form.get("random-alias-default-domain") + if default_domain: + default_domain_id = int(default_domain) + # sanity check + domain = CustomDomain.get(default_domain_id) + if ( + not domain + or domain.user_id != current_user.id + or not domain.verified + ): + flash( + "Something went wrong, sorry for the inconvenience. Please retry. ", + "error", + ) + return redirect(url_for("dashboard.setting")) + current_user.default_random_alias_domain_id = default_domain_id + else: + current_user.default_random_alias_domain_id = None + + db.session.commit() + flash("Your preference has been updated", "success") + return redirect(url_for("dashboard.setting")) + elif request.form.get("form-name") == "change-sender-format": sender_format = int(request.form.get("sender-format")) if SenderFormatEnum.has_value(sender_format): @@ -215,6 +239,7 @@ def setting(): pending_email=pending_email, AliasGeneratorEnum=AliasGeneratorEnum, manual_sub=manual_sub, + FIRST_ALIAS_DOMAIN=FIRST_ALIAS_DOMAIN, ) diff --git a/app/models.py b/app/models.py index ccb7bec1..bc9eb97c 100644 --- a/app/models.py +++ b/app/models.py @@ -166,6 +166,10 @@ class User(db.Model, ModelMixin, UserMixin): # Fields for WebAuthn fido_uuid = db.Column(db.String(), nullable=True, unique=True) + default_random_alias_domain_id = db.Column( + db.ForeignKey("custom_domain.id"), nullable=True, default=None + ) + def fido_enabled(self) -> bool: if self.fido_uuid is not None: return True @@ -430,6 +434,12 @@ class User(db.Model, ModelMixin, UserMixin): def nb_directory(self): return Directory.query.filter_by(user_id=self.id).count() + def has_custom_domain(self): + return CustomDomain.filter_by(user_id=self.id, verified=True).count() > 0 + + def custom_domains(self): + return CustomDomain.filter_by(user_id=self.id, verified=True).all() + def __repr__(self): return f"" @@ -646,17 +656,20 @@ class OauthToken(db.Model, ModelMixin): def generate_email( - scheme: int = AliasGeneratorEnum.word.value, in_hex: bool = False + scheme: int = AliasGeneratorEnum.word.value, + in_hex: bool = False, + alias_domain=FIRST_ALIAS_DOMAIN, ) -> str: """generate an email address that does not exist before + :param alias_domain: the domain used to generate the alias. :param scheme: int, value of AliasGeneratorEnum, indicate how the email is generated :type in_hex: bool, if the generate scheme is uuid, is hex favorable? """ if scheme == AliasGeneratorEnum.uuid.value: name = uuid.uuid4().hex if in_hex else uuid.uuid4().__str__() - random_email = name + "@" + FIRST_ALIAS_DOMAIN + random_email = name + "@" + alias_domain else: - random_email = random_words() + "@" + FIRST_ALIAS_DOMAIN + random_email = random_words() + "@" + alias_domain random_email = random_email.lower().strip() @@ -790,14 +803,28 @@ class Alias(db.Model, ModelMixin): note: str = None, ): """create a new random alias""" - random_email = generate_email(scheme=scheme, in_hex=in_hex) - return Alias.create( + domain = None + + if user.default_random_alias_domain_id: + domain = CustomDomain.get(user.default_random_alias_domain_id) + random_email = generate_email( + scheme=scheme, in_hex=in_hex, alias_domain=domain.domain + ) + else: + random_email = generate_email(scheme=scheme, in_hex=in_hex) + + alias = Alias.create( user_id=user.id, email=random_email, mailbox_id=user.default_mailbox_id, note=note, ) + if domain: + alias.custom_domain_id = domain.id + + return alias + def mailbox_email(self): if self.mailbox_id: return self.mailbox.email @@ -1247,7 +1274,7 @@ 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") - user = db.relationship(User) + user = db.relationship(User, foreign_keys=[user_id]) def nb_alias(self): return Alias.filter_by(custom_domain_id=self.id).count() diff --git a/migrations/versions/2020_062513_a3c9a43e41f4_.py b/migrations/versions/2020_062513_a3c9a43e41f4_.py new file mode 100644 index 00000000..9abf5a85 --- /dev/null +++ b/migrations/versions/2020_062513_a3c9a43e41f4_.py @@ -0,0 +1,31 @@ +"""empty message + +Revision ID: a3c9a43e41f4 +Revises: a5b4dc311a89 +Create Date: 2020-06-25 13:02:21.128994 + +""" +import sqlalchemy_utils +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a3c9a43e41f4' +down_revision = 'a5b4dc311a89' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('default_random_alias_domain_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'users', 'custom_domain', ['default_random_alias_domain_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'users', type_='foreignkey') + op.drop_column('users', 'default_random_alias_domain_id') + # ### end Alembic commands ###