mirror of
https://github.com/simple-login/app.git
synced 2024-09-27 20:31:30 +02:00
Merge pull request #302 from TheLastProject/feature/custom_domain_random_suffix
Support random suffix for personal domains
This commit is contained in:
commit
4e45a619cd
@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div>Default Alias name</div>
|
<div>Default Alias Name</div>
|
||||||
<div class="small-text">
|
<div class="small-text">
|
||||||
This name will be used as the default alias name when you send
|
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.
|
or reply from an alias, unless overwritten by the alias specific name.
|
||||||
@ -76,6 +76,32 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>Random Prefix Generation</div>
|
||||||
|
<div class="small-text">
|
||||||
|
A random prefix can be generated for this domain for usage in the New Alias
|
||||||
|
feature.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="form-name" value="switch-random-prefix-generation">
|
||||||
|
<label class="custom-switch cursor mt-2 pl-0"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
{% if custom_domain.random_prefix_generation %}
|
||||||
|
title="Disable random prefix generation"
|
||||||
|
{% else %}
|
||||||
|
title="Enable random prefix generation"
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
<input type="checkbox" class="custom-switch-input"
|
||||||
|
{{ "checked" if custom_domain.random_prefix_generation else "" }}>
|
||||||
|
|
||||||
|
<span class="custom-switch-indicator"></span>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<h3 class="mb-0">Delete Domain</h3>
|
<h3 class="mb-0">Delete Domain</h3>
|
||||||
<div class="small-text mb-3">Please note that this operation is irreversible.
|
<div class="small-text mb-3">Please note that this operation is irreversible.
|
||||||
@ -119,4 +145,4 @@
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -8,7 +8,6 @@ from app.config import (
|
|||||||
CUSTOM_ALIAS_SECRET,
|
CUSTOM_ALIAS_SECRET,
|
||||||
)
|
)
|
||||||
from app.dashboard.base import dashboard_bp
|
from app.dashboard.base import dashboard_bp
|
||||||
from app.email_utils import email_belongs_to_alias_domains
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import (
|
from app.models import (
|
||||||
@ -27,15 +26,19 @@ signer = TimestampSigner(CUSTOM_ALIAS_SECRET)
|
|||||||
|
|
||||||
def available_suffixes(user: User) -> [bool, str, str]:
|
def available_suffixes(user: User) -> [bool, str, str]:
|
||||||
"""Return (is_custom_domain, alias-suffix, time-signed alias-suffix)"""
|
"""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)
|
# List of (is_custom_domain, alias-suffix, time-signed alias-suffix)
|
||||||
suffixes = []
|
suffixes = []
|
||||||
|
|
||||||
# put custom domain first
|
# put custom domain first
|
||||||
|
# for each user domain, generate both the domain and a random suffix version
|
||||||
for alias_domain in user_custom_domains:
|
for alias_domain in user_custom_domains:
|
||||||
suffix = "@" + alias_domain
|
suffix = "@" + alias_domain.domain
|
||||||
suffixes.append((True, suffix, signer.sign(suffix).decode()))
|
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
|
# then default domain
|
||||||
for domain in ALIAS_DOMAINS:
|
for domain in ALIAS_DOMAINS:
|
||||||
@ -193,35 +196,23 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix) -> bool:
|
|||||||
|
|
||||||
# make sure alias_suffix is either .random_word@simplelogin.co or @my-domain.com
|
# make sure alias_suffix is either .random_word@simplelogin.co or @my-domain.com
|
||||||
alias_suffix = alias_suffix.strip()
|
alias_suffix = alias_suffix.strip()
|
||||||
if alias_suffix.startswith("@"):
|
# alias_domain_prefix is either a .random_word or ""
|
||||||
alias_domain = alias_suffix[1:]
|
alias_domain_prefix, alias_domain = alias_suffix.split("@", 1)
|
||||||
# alias_domain can be either custom_domain or if DISABLE_ALIAS_SUFFIX, one of the default ALIAS_DOMAINS
|
|
||||||
if DISABLE_ALIAS_SUFFIX:
|
# alias_domain must be either one of user custom domains or built-in domains
|
||||||
if (
|
if alias_domain not in user_custom_domains and alias_domain not in ALIAS_DOMAINS:
|
||||||
alias_domain not in user_custom_domains
|
LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||||
and alias_domain not in ALIAS_DOMAINS
|
return False
|
||||||
):
|
|
||||||
LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user)
|
# built-in domain case:
|
||||||
return False
|
# 1) alias_suffix must start with "." and
|
||||||
else:
|
# 2) alias_domain_prefix must come from the word list
|
||||||
if alias_domain not in user_custom_domains:
|
if alias_domain in ALIAS_DOMAINS and alias_domain not in user_custom_domains:
|
||||||
LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user)
|
if not alias_domain_prefix.startswith("."):
|
||||||
return False
|
|
||||||
else:
|
|
||||||
if not alias_suffix.startswith("."):
|
|
||||||
LOG.exception("User %s submits a wrong alias suffix %s", user, alias_suffix)
|
LOG.exception("User %s submits a wrong alias suffix %s", user, alias_suffix)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
full_alias = alias_prefix + alias_suffix
|
random_word_part = alias_domain_prefix[1:]
|
||||||
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):
|
if not word_exist(random_word_part):
|
||||||
LOG.exception(
|
LOG.exception(
|
||||||
"alias suffix %s needs to start with a random word, user %s",
|
"alias suffix %s needs to start with a random word, user %s",
|
||||||
@ -229,5 +220,14 @@ def verify_prefix_suffix(user, alias_prefix, alias_suffix) -> bool:
|
|||||||
user,
|
user,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
if alias_domain not in ALIAS_DOMAINS:
|
||||||
|
LOG.exception("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -160,6 +160,25 @@ def domain_detail(custom_domain_id):
|
|||||||
return redirect(
|
return redirect(
|
||||||
url_for("dashboard.domain_detail", custom_domain_id=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
|
||||||
|
)
|
||||||
|
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":
|
elif request.form.get("form-name") == "delete":
|
||||||
name = custom_domain.domain
|
name = custom_domain.domain
|
||||||
CustomDomain.delete(custom_domain_id)
|
CustomDomain.delete(custom_domain_id)
|
||||||
|
@ -1449,6 +1449,11 @@ class CustomDomain(db.Model, ModelMixin):
|
|||||||
# an alias is created automatically the first time it receives an email
|
# 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")
|
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])
|
user = db.relationship(User, foreign_keys=[user_id])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
29
migrations/versions/2020_100922_a90e423c6763_.py
Normal file
29
migrations/versions/2020_100922_a90e423c6763_.py
Normal file
@ -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 ###
|
Loading…
Reference in New Issue
Block a user