mirror of
https://github.com/simple-login/app.git
synced 2024-09-29 21:21:29 +02:00
Merge pull request #473 from developStorm/master
Able to config random suffix
This commit is contained in:
commit
68c1463707
@ -385,6 +385,8 @@ ALIAS_LIMIT = os.environ.get("ALIAS_LIMIT") or "100/day;50/hour;5/minute"
|
|||||||
|
|
||||||
ENABLE_SPAM_ASSASSIN = "ENABLE_SPAM_ASSASSIN" in os.environ
|
ENABLE_SPAM_ASSASSIN = "ENABLE_SPAM_ASSASSIN" in os.environ
|
||||||
|
|
||||||
|
ALIAS_RANDOM_SUFFIX_LENGTH = int(os.environ.get("ALIAS_RAND_SUFFIX_LENGTH", 5))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
HIBP_SCAN_INTERVAL_DAYS = int(os.environ.get("HIBP_SCAN_INTERVAL_DAYS"))
|
HIBP_SCAN_INTERVAL_DAYS = int(os.environ.get("HIBP_SCAN_INTERVAL_DAYS"))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -247,6 +247,22 @@
|
|||||||
</select>
|
</select>
|
||||||
<button class="btn btn-outline-primary">Update</button>
|
<button class="btn btn-outline-primary">Update</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div id="random-alias-suffix" class="mt-3 mb-1">Select the default suffix generator for aliases.</div>
|
||||||
|
<form method="post" action="#random-alias-suffix" class="form-inline">
|
||||||
|
<input type="hidden" name="form-name" value="random-alias-suffix">
|
||||||
|
<select class="form-control mr-sm-2" name="random-alias-suffix-generator">
|
||||||
|
|
||||||
|
<option value="0" {% if current_user.random_alias_suffix==0 %} selected {% endif %}>
|
||||||
|
Random word from our dictionary
|
||||||
|
</option>
|
||||||
|
<option value="1" {% if current_user.random_alias_suffix==1 %} selected {% endif %}>
|
||||||
|
Random combination of {{ ALIAS_RAND_SUFFIX_LENGTH }} letter and digits
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-outline-primary">Update</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- END Random alias -->
|
<!-- END Random alias -->
|
||||||
|
@ -23,7 +23,7 @@ from app.models import (
|
|||||||
AliasMailbox,
|
AliasMailbox,
|
||||||
DomainDeletedAlias,
|
DomainDeletedAlias,
|
||||||
)
|
)
|
||||||
from app.utils import random_word, word_exist
|
from app.utils import get_suffix
|
||||||
|
|
||||||
signer = TimestampSigner(CUSTOM_ALIAS_SECRET)
|
signer = TimestampSigner(CUSTOM_ALIAS_SECRET)
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ def get_available_suffixes(user: User) -> [SuffixInfo]:
|
|||||||
# for each user domain, generate both the domain and a random suffix version
|
# for each user domain, generate both the domain and a random suffix version
|
||||||
for custom_domain in user_custom_domains:
|
for custom_domain in user_custom_domains:
|
||||||
if custom_domain.random_prefix_generation:
|
if custom_domain.random_prefix_generation:
|
||||||
suffix = "." + random_word() + "@" + custom_domain.domain
|
suffix = "." + get_suffix(user) + "@" + custom_domain.domain
|
||||||
suffix_info = SuffixInfo(True, suffix, signer.sign(suffix).decode(), False)
|
suffix_info = SuffixInfo(True, suffix, signer.sign(suffix).decode(), False)
|
||||||
if user.default_alias_custom_domain_id == custom_domain.id:
|
if user.default_alias_custom_domain_id == custom_domain.id:
|
||||||
suffixes.insert(0, suffix_info)
|
suffixes.insert(0, suffix_info)
|
||||||
@ -77,7 +77,7 @@ def get_available_suffixes(user: User) -> [SuffixInfo]:
|
|||||||
# then SimpleLogin domain
|
# then SimpleLogin domain
|
||||||
for sl_domain in user.get_sl_domains():
|
for sl_domain in user.get_sl_domains():
|
||||||
suffix = (
|
suffix = (
|
||||||
("" if DISABLE_ALIAS_SUFFIX else "." + random_word())
|
("" if DISABLE_ALIAS_SUFFIX else "." + get_suffix(user))
|
||||||
+ "@"
|
+ "@"
|
||||||
+ sl_domain.domain
|
+ sl_domain.domain
|
||||||
)
|
)
|
||||||
@ -281,14 +281,6 @@ def verify_prefix_suffix(user: User, alias_prefix, alias_suffix) -> bool:
|
|||||||
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
|
||||||
|
|
||||||
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:
|
else:
|
||||||
if alias_domain not in user_custom_domains:
|
if alias_domain not in user_custom_domains:
|
||||||
if not DISABLE_ALIAS_SUFFIX:
|
if not DISABLE_ALIAS_SUFFIX:
|
||||||
|
@ -15,7 +15,12 @@ from wtforms import StringField, validators
|
|||||||
from wtforms.fields.html5 import EmailField
|
from wtforms.fields.html5 import EmailField
|
||||||
|
|
||||||
from app import s3, email_utils
|
from app import s3, email_utils
|
||||||
from app.config import URL, FIRST_ALIAS_DOMAIN, JOB_DELETE_ACCOUNT
|
from app.config import (
|
||||||
|
URL,
|
||||||
|
FIRST_ALIAS_DOMAIN,
|
||||||
|
JOB_DELETE_ACCOUNT,
|
||||||
|
ALIAS_RANDOM_SUFFIX_LENGTH,
|
||||||
|
)
|
||||||
from app.dashboard.base import dashboard_bp
|
from app.dashboard.base import dashboard_bp
|
||||||
from app.email_utils import (
|
from app.email_utils import (
|
||||||
email_can_be_used_as_mailbox,
|
email_can_be_used_as_mailbox,
|
||||||
@ -32,6 +37,7 @@ from app.models import (
|
|||||||
Alias,
|
Alias,
|
||||||
CustomDomain,
|
CustomDomain,
|
||||||
AliasGeneratorEnum,
|
AliasGeneratorEnum,
|
||||||
|
AliasSuffixEnum,
|
||||||
ManualSubscription,
|
ManualSubscription,
|
||||||
SenderFormatEnum,
|
SenderFormatEnum,
|
||||||
SLDomain,
|
SLDomain,
|
||||||
@ -240,6 +246,14 @@ def setting():
|
|||||||
flash("Your preference has been updated", "success")
|
flash("Your preference has been updated", "success")
|
||||||
return redirect(url_for("dashboard.setting"))
|
return redirect(url_for("dashboard.setting"))
|
||||||
|
|
||||||
|
elif request.form.get("form-name") == "random-alias-suffix":
|
||||||
|
scheme = int(request.form.get("random-alias-suffix-generator"))
|
||||||
|
if AliasSuffixEnum.has_value(scheme):
|
||||||
|
current_user.random_alias_suffix = scheme
|
||||||
|
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":
|
elif request.form.get("form-name") == "change-sender-format":
|
||||||
sender_format = int(request.form.get("sender-format"))
|
sender_format = int(request.form.get("sender-format"))
|
||||||
if SenderFormatEnum.has_value(sender_format):
|
if SenderFormatEnum.has_value(sender_format):
|
||||||
@ -292,6 +306,7 @@ def setting():
|
|||||||
apple_sub=apple_sub,
|
apple_sub=apple_sub,
|
||||||
coinbase_sub=coinbase_sub,
|
coinbase_sub=coinbase_sub,
|
||||||
FIRST_ALIAS_DOMAIN=FIRST_ALIAS_DOMAIN,
|
FIRST_ALIAS_DOMAIN=FIRST_ALIAS_DOMAIN,
|
||||||
|
ALIAS_RAND_SUFFIX_LENGTH=ALIAS_RANDOM_SUFFIX_LENGTH,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ from app.utils import (
|
|||||||
convert_to_id,
|
convert_to_id,
|
||||||
random_string,
|
random_string,
|
||||||
random_words,
|
random_words,
|
||||||
random_word,
|
|
||||||
sanitize_email,
|
sanitize_email,
|
||||||
|
get_suffix,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -162,6 +162,11 @@ class AliasGeneratorEnum(EnumE):
|
|||||||
uuid = 2 # aliases are generated based on uuid
|
uuid = 2 # aliases are generated based on uuid
|
||||||
|
|
||||||
|
|
||||||
|
class AliasSuffixEnum(EnumE):
|
||||||
|
word = 0 # Random word from dictionary file
|
||||||
|
random_string = 1 # Completely random string
|
||||||
|
|
||||||
|
|
||||||
class Hibp(db.Model, ModelMixin):
|
class Hibp(db.Model, ModelMixin):
|
||||||
__tablename__ = "hibp"
|
__tablename__ = "hibp"
|
||||||
name = db.Column(db.String(), nullable=False, unique=True, index=True)
|
name = db.Column(db.String(), nullable=False, unique=True, index=True)
|
||||||
@ -293,6 +298,16 @@ class User(db.Model, ModelMixin, UserMixin, PasswordOracle):
|
|||||||
db.Boolean, default=False, nullable=False, server_default="0"
|
db.Boolean, default=False, nullable=False, server_default="0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# whether to use random string or random word as suffix
|
||||||
|
# Random word from dictionary file -> 0
|
||||||
|
# Completely random string -> 1
|
||||||
|
random_alias_suffix = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
nullable=False,
|
||||||
|
default=AliasSuffixEnum.random_string.value,
|
||||||
|
server_default=str(AliasSuffixEnum.random_string.value),
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, email, name="", password=None, **kwargs):
|
def create(cls, email, name="", password=None, **kwargs):
|
||||||
user: User = super(User, cls).create(email=email, name=name, **kwargs)
|
user: User = super(User, cls).create(email=email, name=name, **kwargs)
|
||||||
@ -1132,7 +1147,7 @@ class Alias(db.Model, ModelMixin):
|
|||||||
|
|
||||||
# find the right suffix - avoid infinite loop by running this at max 1000 times
|
# find the right suffix - avoid infinite loop by running this at max 1000 times
|
||||||
for i in range(1000):
|
for i in range(1000):
|
||||||
suffix = random_word()
|
suffix = get_suffix(user)
|
||||||
email = f"{prefix}.{suffix}@{FIRST_ALIAS_DOMAIN}"
|
email = f"{prefix}.{suffix}@{FIRST_ALIAS_DOMAIN}"
|
||||||
|
|
||||||
if not cls.get_by(email=email) and not DeletedAlias.get_by(email=email):
|
if not cls.get_by(email=email) and not DeletedAlias.get_by(email=email):
|
||||||
|
22
app/utils.py
22
app/utils.py
@ -4,8 +4,9 @@ import urllib.parse
|
|||||||
|
|
||||||
from unidecode import unidecode
|
from unidecode import unidecode
|
||||||
|
|
||||||
from .config import WORDS_FILE_PATH
|
from .config import WORDS_FILE_PATH, ALIAS_RANDOM_SUFFIX_LENGTH
|
||||||
from .log import LOG
|
from .log import LOG
|
||||||
|
from .models import User, AliasSuffixEnum
|
||||||
|
|
||||||
with open(WORDS_FILE_PATH) as f:
|
with open(WORDS_FILE_PATH) as f:
|
||||||
LOG.d("load words file: %s", WORDS_FILE_PATH)
|
LOG.d("load words file: %s", WORDS_FILE_PATH)
|
||||||
@ -16,6 +17,20 @@ def random_word():
|
|||||||
return random.choice(_words)
|
return random.choice(_words)
|
||||||
|
|
||||||
|
|
||||||
|
def get_suffix(user: User) -> str:
|
||||||
|
"""Get random suffix for an alias based on user's preference.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): the user who is trying to create an alias
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: the random suffix generated
|
||||||
|
"""
|
||||||
|
if user.random_alias_suffix == AliasSuffixEnum.random_string.value:
|
||||||
|
return random_string(ALIAS_RANDOM_SUFFIX_LENGTH, include_digits=True)
|
||||||
|
return random_word()
|
||||||
|
|
||||||
|
|
||||||
def word_exist(word):
|
def word_exist(word):
|
||||||
return word in _words
|
return word in _words
|
||||||
|
|
||||||
@ -27,9 +42,12 @@ def random_words():
|
|||||||
return "_".join([random.choice(_words) for i in range(nb_words)])
|
return "_".join([random.choice(_words) for i in range(nb_words)])
|
||||||
|
|
||||||
|
|
||||||
def random_string(length=10):
|
def random_string(length=10, include_digits=False):
|
||||||
"""Generate a random string of fixed length """
|
"""Generate a random string of fixed length """
|
||||||
letters = string.ascii_lowercase
|
letters = string.ascii_lowercase
|
||||||
|
if include_digits:
|
||||||
|
letters += string.digits
|
||||||
|
|
||||||
return "".join(random.choice(letters) for _ in range(length))
|
return "".join(random.choice(letters) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user