Quarantine
diff --git a/app/dashboard/views/alias_contact_manager.py b/app/dashboard/views/alias_contact_manager.py
index fad783d1..31e3750b 100644
--- a/app/dashboard/views/alias_contact_manager.py
+++ b/app/dashboard/views/alias_contact_manager.py
@@ -203,7 +203,7 @@ def alias_contact_manager(alias_id):
alias_id=alias.id,
website_email=contact_email,
name=contact_name,
- reply_email=generate_reply_email(contact_email),
+ reply_email=generate_reply_email(contact_email, current_user),
)
LOG.d("create reverse-alias for %s", contact_addr)
diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py
index ff0762c1..4e51aeaa 100644
--- a/app/dashboard/views/setting.py
+++ b/app/dashboard/views/setting.py
@@ -255,6 +255,16 @@ def setting():
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
+ elif request.form.get("form-name") == "sender-in-ra":
+ choose = request.form.get("enable")
+ if choose == "on":
+ current_user.include_sender_in_reverse_alias = True
+ else:
+ current_user.include_sender_in_reverse_alias = False
+ db.session.commit()
+ flash("Your preference has been updated", "success")
+ return redirect(url_for("dashboard.setting"))
+
elif request.form.get("form-name") == "export-data":
data = {
"email": current_user.email,
diff --git a/app/email_utils.py b/app/email_utils.py
index 654e16fd..bacc9ce2 100644
--- a/app/email_utils.py
+++ b/app/email_utils.py
@@ -802,7 +802,7 @@ def replace(msg: Message, old, new) -> Message:
return msg
-def generate_reply_email(contact_email: str) -> str:
+def generate_reply_email(contact_email: str, user: User) -> str:
"""
generate a reply_email (aka reverse-alias), make sure it isn't used by any contact
"""
@@ -811,7 +811,14 @@ def generate_reply_email(contact_email: str) -> str:
# "The maximum total length of a user name or other local-part is 64
# octets."
- if contact_email:
+ # todo: turns this to False after Dec 20 2020
+ include_sender_in_reverse_alias = True
+
+ # user has chosen an option explicitly
+ if user.include_sender_in_reverse_alias is not None:
+ include_sender_in_reverse_alias = user.include_sender_in_reverse_alias
+
+ if include_sender_in_reverse_alias and contact_email:
# control char: 4 chars (ra+, +)
# random suffix: max 10 chars
# maximum: 64
@@ -825,13 +832,13 @@ def generate_reply_email(contact_email: str) -> str:
# not use while to avoid infinite loop
for _ in range(1000):
- if contact_email:
+ if include_sender_in_reverse_alias and contact_email:
random_length = random.randint(5, 10)
reply_email = (
f"ra+{contact_email}+{random_string(random_length)}@{EMAIL_DOMAIN}"
)
else:
- random_length = random.randint(10, 50)
+ random_length = random.randint(20, 50)
reply_email = f"ra+{random_string(random_length)}@{EMAIL_DOMAIN}"
if not Contact.get_by(reply_email=reply_email):
diff --git a/app/models.py b/app/models.py
index 5a9c723d..c8315f97 100644
--- a/app/models.py
+++ b/app/models.py
@@ -272,6 +272,11 @@ class User(db.Model, ModelMixin, UserMixin):
db.ForeignKey("alias.id", ondelete="SET NULL"), nullable=True, default=None
)
+ # whether to include the sender address in reverse-alias
+ include_sender_in_reverse_alias = db.Column(
+ db.Boolean, default=False, nullable=True
+ )
+
@classmethod
def create(cls, email, name, password=None, **kwargs):
user: User = super(User, cls).create(email=email, name=name, **kwargs)
diff --git a/email_handler.py b/email_handler.py
index ec47bc5e..2134a6e9 100644
--- a/email_handler.py
+++ b/email_handler.py
@@ -229,7 +229,7 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
name=contact_name,
mail_from=mail_from,
from_header=from_header,
- reply_email=generate_reply_email(contact_email)
+ reply_email=generate_reply_email(contact_email, alias.user)
if is_valid_email(contact_email)
else NOREPLY,
)
@@ -292,7 +292,7 @@ def replace_header_when_forward(msg: Message, alias: Alias, header: str):
alias_id=alias.id,
website_email=contact_email,
name=contact_name,
- reply_email=generate_reply_email(contact_email),
+ reply_email=generate_reply_email(contact_email, alias.user),
is_cc=header.lower() == "cc",
from_header=addr,
)
diff --git a/migrations/versions/2020_120619_c0d91ff18f77_.py b/migrations/versions/2020_120619_c0d91ff18f77_.py
new file mode 100644
index 00000000..abfbd5f3
--- /dev/null
+++ b/migrations/versions/2020_120619_c0d91ff18f77_.py
@@ -0,0 +1,29 @@
+"""empty message
+
+Revision ID: c0d91ff18f77
+Revises: 56c790ec8ab4
+Create Date: 2020-12-06 19:28:11.733022
+
+"""
+import sqlalchemy_utils
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'c0d91ff18f77'
+down_revision = '56c790ec8ab4'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('users', sa.Column('include_sender_in_reverse_alias', sa.Boolean(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('users', 'include_sender_in_reverse_alias')
+ # ### end Alembic commands ###
diff --git a/tests/test_email_utils.py b/tests/test_email_utils.py
index 5f46bffc..1a0646f5 100644
--- a/tests/test_email_utils.py
+++ b/tests/test_email_utils.py
@@ -25,6 +25,7 @@ from app.email_utils import (
)
from app.extensions import db
from app.models import User, CustomDomain
+from tests.utils import login
def test_get_email_domain_part():
@@ -464,23 +465,50 @@ def test_to_bytes():
def test_generate_reply_email(flask_client):
- reply_email = generate_reply_email("test@example.org")
+ user = User.create(
+ email="a@b.c",
+ password="password",
+ name="Test User",
+ activated=True,
+ )
+ reply_email = generate_reply_email("test@example.org", user)
# return something like
- # ra+test.at.example.org+gjbnnddll@sl.local
- assert reply_email.startswith("ra+test.at.example.org+")
+ # ra+
@sl.local
assert reply_email.endswith(EMAIL_DOMAIN)
- reply_email = generate_reply_email("")
+ reply_email = generate_reply_email("", user)
# return something like
# ra+qdrcxzppngmvtajklnhqvvuyyzgkyityrzjwikk@sl.local
assert reply_email.startswith("ra+")
assert reply_email.endswith(EMAIL_DOMAIN)
- reply_email = generate_reply_email("👌汉å—@example.org")
+
+def test_generate_reply_email_include_sender_in_reverse_alias(flask_client):
+ # user enables include_sender_in_reverse_alias
+ user = User.create(
+ email="a@b.c",
+ password="password",
+ name="Test User",
+ activated=True,
+ include_sender_in_reverse_alias=True,
+ )
+ reply_email = generate_reply_email("test@example.org", user)
+ # return something like
+ # ra+test.at.example.org+gjbnnddll@sl.local
+ assert reply_email.startswith("ra+test.at.example.org+")
+ assert reply_email.endswith(EMAIL_DOMAIN)
+
+ reply_email = generate_reply_email("", user)
+ # return something like
+ # ra+qdrcxzppngmvtajklnhqvvuyyzgkyityrzjwikk@sl.local
+ assert reply_email.startswith("ra+")
+ assert reply_email.endswith(EMAIL_DOMAIN)
+
+ reply_email = generate_reply_email("👌汉å—@example.org", user)
assert reply_email.startswith("ra+yizi.at.example.org+")
# make sure reply_email only contain lowercase
- reply_email = generate_reply_email("TEST@example.org")
+ reply_email = generate_reply_email("TEST@example.org", user)
assert reply_email.startswith("ra+test.at.example.org")