mirror of
https://github.com/simple-login/app.git
synced 2024-09-30 05:31:30 +02:00
Merge pull request #234 from simple-login/random-alias-domain
Random alias domain
This commit is contained in:
commit
5e464a824c
@ -150,24 +150,19 @@
|
|||||||
<button class="btn btn-outline-primary">Update</button>
|
<button class="btn btn-outline-primary">Update</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if current_user.has_custom_domain() %}
|
|
||||||
<div class="mt-3 mb-1">Select the domain for random aliases.</div>
|
<div class="mt-3 mb-1">Select the domain for random aliases.</div>
|
||||||
<form method="post" action="#random-alias" class="form-inline">
|
<form method="post" action="#random-alias" class="form-inline">
|
||||||
<input type="hidden" name="form-name" value="change-random-alias-default-domain">
|
<input type="hidden" name="form-name" value="change-random-alias-default-domain">
|
||||||
<select class="form-control mr-sm-2" name="random-alias-default-domain">
|
<select class="form-control mr-sm-2" name="random-alias-default-domain">
|
||||||
<option value="" {% if not current_user.default_random_alias_domain_id %} selected {% endif %}>
|
{% for is_public, domain in current_user.available_domains_for_random_alias() %}
|
||||||
{{ FIRST_ALIAS_DOMAIN }} (SimpleLogin domain)
|
<option value="{{ domain }}"
|
||||||
</option>
|
{% if current_user.default_random_alias_domain() == domain %} selected {% endif %} >
|
||||||
{% for domain in current_user.custom_domains() %}
|
{{ domain }} ({% if is_public %} SimpleLogin domain {% else %} your domain {% endif %})
|
||||||
<option value="{{ domain.id }}"
|
|
||||||
{% if current_user.default_random_alias_domain_id == domain.id %} selected {% endif %} >
|
|
||||||
{{ domain.domain }} (your domain)
|
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<button class="btn btn-outline-primary">Update</button>
|
<button class="btn btn-outline-primary">Update</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ from app.models import (
|
|||||||
AliasGeneratorEnum,
|
AliasGeneratorEnum,
|
||||||
ManualSubscription,
|
ManualSubscription,
|
||||||
SenderFormatEnum,
|
SenderFormatEnum,
|
||||||
|
PublicDomain,
|
||||||
)
|
)
|
||||||
from app.utils import random_string
|
from app.utils import random_string
|
||||||
|
|
||||||
@ -162,20 +163,28 @@ def setting():
|
|||||||
elif request.form.get("form-name") == "change-random-alias-default-domain":
|
elif request.form.get("form-name") == "change-random-alias-default-domain":
|
||||||
default_domain = request.form.get("random-alias-default-domain")
|
default_domain = request.form.get("random-alias-default-domain")
|
||||||
if default_domain:
|
if default_domain:
|
||||||
default_domain_id = int(default_domain)
|
custom_domain = CustomDomain.get_by(domain=default_domain)
|
||||||
|
if custom_domain:
|
||||||
# sanity check
|
# sanity check
|
||||||
domain = CustomDomain.get(default_domain_id)
|
|
||||||
if (
|
if (
|
||||||
not domain
|
custom_domain.user_id != current_user.id
|
||||||
or domain.user_id != current_user.id
|
or not custom_domain.verified
|
||||||
or not domain.verified
|
|
||||||
):
|
):
|
||||||
flash(
|
LOG.error(
|
||||||
"Something went wrong, sorry for the inconvenience. Please retry. ",
|
"%s cannot use domain %s", current_user, default_domain
|
||||||
"error",
|
|
||||||
)
|
)
|
||||||
return redirect(url_for("dashboard.setting"))
|
else:
|
||||||
current_user.default_random_alias_domain_id = default_domain_id
|
# make sure only default_random_alias_domain_id or default_random_alias_public_domain_id is set
|
||||||
|
current_user.default_random_alias_domain_id = custom_domain.id
|
||||||
|
current_user.default_random_alias_public_domain_id = None
|
||||||
|
else:
|
||||||
|
public_domain = PublicDomain.get_by(domain=default_domain)
|
||||||
|
if public_domain:
|
||||||
|
# make sure only default_random_alias_domain_id or default_random_alias_public_domain_id is set
|
||||||
|
current_user.default_random_alias_public_domain_id = (
|
||||||
|
public_domain.id
|
||||||
|
)
|
||||||
|
current_user.default_random_alias_domain_id = None
|
||||||
else:
|
else:
|
||||||
current_user.default_random_alias_domain_id = None
|
current_user.default_random_alias_domain_id = None
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import enum
|
|||||||
import random
|
import random
|
||||||
import uuid
|
import uuid
|
||||||
from email.utils import formataddr
|
from email.utils import formataddr
|
||||||
from typing import List
|
from typing import List, Tuple
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import bcrypt
|
import bcrypt
|
||||||
@ -166,8 +166,18 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||||||
# Fields for WebAuthn
|
# Fields for WebAuthn
|
||||||
fido_uuid = db.Column(db.String(), nullable=True, unique=True)
|
fido_uuid = db.Column(db.String(), nullable=True, unique=True)
|
||||||
|
|
||||||
|
# the default domain that's used when user creates a new random alias
|
||||||
|
# default_random_alias_domain_id XOR default_random_alias_public_domain_id
|
||||||
default_random_alias_domain_id = db.Column(
|
default_random_alias_domain_id = db.Column(
|
||||||
db.ForeignKey("custom_domain.id"), nullable=True, default=None
|
db.ForeignKey("custom_domain.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
default_random_alias_public_domain_id = db.Column(
|
||||||
|
db.ForeignKey("public_domain.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
default=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# some users could have lifetime premium
|
# some users could have lifetime premium
|
||||||
@ -452,6 +462,49 @@ class User(db.Model, ModelMixin, UserMixin):
|
|||||||
def custom_domains(self):
|
def custom_domains(self):
|
||||||
return CustomDomain.filter_by(user_id=self.id, verified=True).all()
|
return CustomDomain.filter_by(user_id=self.id, verified=True).all()
|
||||||
|
|
||||||
|
def available_domains_for_random_alias(self) -> List[Tuple[bool, str]]:
|
||||||
|
"""Return available domains for user to create random aliases
|
||||||
|
Each result record contains:
|
||||||
|
- whether the domain is public (i.e. belongs to SimpleLogin)
|
||||||
|
- the domain
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
for public_domain in PublicDomain.query.all():
|
||||||
|
res.append((True, public_domain.domain))
|
||||||
|
|
||||||
|
for custom_domain in CustomDomain.filter_by(
|
||||||
|
user_id=self.id, verified=True
|
||||||
|
).all():
|
||||||
|
res.append((False, custom_domain.domain))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def default_random_alias_domain(self) -> str:
|
||||||
|
"""return the domain used for the random alias"""
|
||||||
|
if self.default_random_alias_domain_id:
|
||||||
|
custom_domain = CustomDomain.get(self.default_random_alias_domain_id)
|
||||||
|
# sanity check
|
||||||
|
if (
|
||||||
|
not custom_domain
|
||||||
|
or not custom_domain.verified
|
||||||
|
or custom_domain.user_id != self.id
|
||||||
|
):
|
||||||
|
LOG.error("Problem with %s default random alias domain", self)
|
||||||
|
return FIRST_ALIAS_DOMAIN
|
||||||
|
|
||||||
|
return custom_domain.domain
|
||||||
|
|
||||||
|
if self.default_random_alias_public_domain_id:
|
||||||
|
public_domain = PublicDomain.get(self.default_random_alias_public_domain_id)
|
||||||
|
# sanity check
|
||||||
|
if not public_domain:
|
||||||
|
LOG.error("Problem with %s public random alias domain", self)
|
||||||
|
return FIRST_ALIAS_DOMAIN
|
||||||
|
|
||||||
|
return public_domain.domain
|
||||||
|
|
||||||
|
return FIRST_ALIAS_DOMAIN
|
||||||
|
|
||||||
def fido_enabled(self) -> bool:
|
def fido_enabled(self) -> bool:
|
||||||
if self.fido_uuid is not None:
|
if self.fido_uuid is not None:
|
||||||
return True
|
return True
|
||||||
@ -824,12 +877,17 @@ class Alias(db.Model, ModelMixin):
|
|||||||
note: str = None,
|
note: str = None,
|
||||||
):
|
):
|
||||||
"""create a new random alias"""
|
"""create a new random alias"""
|
||||||
domain = None
|
custom_domain = None
|
||||||
|
|
||||||
if user.default_random_alias_domain_id:
|
if user.default_random_alias_domain_id:
|
||||||
domain = CustomDomain.get(user.default_random_alias_domain_id)
|
custom_domain = CustomDomain.get(user.default_random_alias_domain_id)
|
||||||
random_email = generate_email(
|
random_email = generate_email(
|
||||||
scheme=scheme, in_hex=in_hex, alias_domain=domain.domain
|
scheme=scheme, in_hex=in_hex, alias_domain=custom_domain.domain
|
||||||
|
)
|
||||||
|
elif user.default_random_alias_public_domain_id:
|
||||||
|
public_domain = PublicDomain.get(user.default_random_alias_public_domain_id)
|
||||||
|
random_email = generate_email(
|
||||||
|
scheme=scheme, in_hex=in_hex, alias_domain=public_domain.domain
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
random_email = generate_email(scheme=scheme, in_hex=in_hex)
|
random_email = generate_email(scheme=scheme, in_hex=in_hex)
|
||||||
@ -841,8 +899,8 @@ class Alias(db.Model, ModelMixin):
|
|||||||
note=note,
|
note=note,
|
||||||
)
|
)
|
||||||
|
|
||||||
if domain:
|
if custom_domain:
|
||||||
alias.custom_domain_id = domain.id
|
alias.custom_domain_id = custom_domain.id
|
||||||
|
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
@ -1610,3 +1668,9 @@ class Notification(db.Model, ModelMixin):
|
|||||||
|
|
||||||
# whether user has marked the notification as read
|
# whether user has marked the notification as read
|
||||||
read = db.Column(db.Boolean, nullable=False, default=False)
|
read = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
|
||||||
|
class PublicDomain(db.Model, ModelMixin):
|
||||||
|
"""SimpleLogin domains that all users can use"""
|
||||||
|
|
||||||
|
domain = db.Column(db.String(128), unique=True, nullable=False)
|
||||||
|
15
init_app.py
15
init_app.py
@ -1,5 +1,6 @@
|
|||||||
"""Initial loading script"""
|
"""Initial loading script"""
|
||||||
from app.models import Mailbox, Contact
|
from app.config import ALIAS_DOMAINS
|
||||||
|
from app.models import Mailbox, Contact, PublicDomain
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.pgp_utils import load_public_key
|
from app.pgp_utils import load_public_key
|
||||||
@ -32,8 +33,20 @@ def load_pgp_public_keys():
|
|||||||
LOG.d("Finish load_pgp_public_keys")
|
LOG.d("Finish load_pgp_public_keys")
|
||||||
|
|
||||||
|
|
||||||
|
def add_public_domains():
|
||||||
|
for alias_domain in ALIAS_DOMAINS:
|
||||||
|
if PublicDomain.get_by(domain=alias_domain):
|
||||||
|
LOG.d("%s is already a public domain", alias_domain)
|
||||||
|
else:
|
||||||
|
LOG.info("Add %s to public domain", alias_domain)
|
||||||
|
PublicDomain.create(domain=alias_domain)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
load_pgp_public_keys()
|
load_pgp_public_keys()
|
||||||
|
add_public_domains()
|
||||||
|
44
migrations/versions/2020_070423_270d598c51e3_.py
Normal file
44
migrations/versions/2020_070423_270d598c51e3_.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 270d598c51e3
|
||||||
|
Revises: 7128f87af701
|
||||||
|
Create Date: 2020-07-04 23:32:25.297082
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sqlalchemy_utils
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '270d598c51e3'
|
||||||
|
down_revision = '7128f87af701'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('public_domain',
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('created_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=False),
|
||||||
|
sa.Column('updated_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=True),
|
||||||
|
sa.Column('domain', sa.String(length=128), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('domain')
|
||||||
|
)
|
||||||
|
op.add_column('users', sa.Column('default_random_alias_public_domain_id', sa.Integer(), nullable=True))
|
||||||
|
op.drop_constraint('users_default_random_alias_domain_id_fkey', 'users', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'users', 'custom_domain', ['default_random_alias_domain_id'], ['id'], ondelete='SET NULL')
|
||||||
|
op.create_foreign_key(None, 'users', 'public_domain', ['default_random_alias_public_domain_id'], ['id'], ondelete='SET NULL')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint(None, 'users', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'users', type_='foreignkey')
|
||||||
|
op.create_foreign_key('users_default_random_alias_domain_id_fkey', 'users', 'custom_domain', ['default_random_alias_domain_id'], ['id'])
|
||||||
|
op.drop_column('users', 'default_random_alias_public_domain_id')
|
||||||
|
op.drop_table('public_domain')
|
||||||
|
# ### end Alembic commands ###
|
@ -64,6 +64,7 @@ from app.models import (
|
|||||||
Referral,
|
Referral,
|
||||||
AliasMailbox,
|
AliasMailbox,
|
||||||
Notification,
|
Notification,
|
||||||
|
PublicDomain,
|
||||||
)
|
)
|
||||||
from app.monitor.base import monitor_bp
|
from app.monitor.base import monitor_bp
|
||||||
from app.oauth.base import oauth_bp
|
from app.oauth.base import oauth_bp
|
||||||
@ -286,6 +287,10 @@ def fake_data():
|
|||||||
)
|
)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
for d in ["d1.localhost", "d2.localhost"]:
|
||||||
|
PublicDomain.create(domain=d)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
|
Loading…
Reference in New Issue
Block a user