mirror of
https://github.com/simple-login/app.git
synced 2024-09-27 20:31:30 +02:00
make sure a deleted subdomain can't be recreated
This commit is contained in:
parent
5a81c08e32
commit
a8c86785d1
@ -3,6 +3,7 @@ from flask_login import login_required, current_user
|
||||
|
||||
from app.config import MAX_NB_SUBDOMAIN
|
||||
from app.dashboard.base import dashboard_bp
|
||||
from app.errors import SubdomainInTrashError
|
||||
from app.log import LOG
|
||||
from app.models import CustomDomain, Mailbox, SLDomain
|
||||
|
||||
@ -51,6 +52,7 @@ def subdomain_route():
|
||||
).first():
|
||||
flash(f"{full_domain} already used in a SimpleLogin mailbox", "error")
|
||||
else:
|
||||
try:
|
||||
new_custom_domain = CustomDomain.create(
|
||||
is_sl_subdomain=True,
|
||||
catch_all=True, # by default catch-all is enabled
|
||||
@ -63,7 +65,12 @@ def subdomain_route():
|
||||
ownership_verified=True,
|
||||
commit=True,
|
||||
)
|
||||
|
||||
except SubdomainInTrashError:
|
||||
flash(
|
||||
f"{full_domain} has been used before and cannot be reused",
|
||||
"error",
|
||||
)
|
||||
else:
|
||||
flash(
|
||||
f"New subdomain {new_custom_domain.domain} is created",
|
||||
"success",
|
||||
|
@ -8,3 +8,9 @@ class DirectoryInTrashError(Exception):
|
||||
"""raised when a directory is deleted before """
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SubdomainInTrashError(Exception):
|
||||
"""raised when a subdomain is deleted before """
|
||||
|
||||
pass
|
||||
|
@ -32,7 +32,7 @@ from app.config import (
|
||||
ALIAS_RANDOM_SUFFIX_LENGTH,
|
||||
)
|
||||
from app.db import Session
|
||||
from app.errors import AliasInTrashError, DirectoryInTrashError
|
||||
from app.errors import AliasInTrashError, DirectoryInTrashError, SubdomainInTrashError
|
||||
from app.log import LOG
|
||||
from app.oauth_models import Scope
|
||||
from app.pw_models import PasswordOracle
|
||||
@ -1979,8 +1979,12 @@ class CustomDomain(Base, ModelMixin):
|
||||
return f"sl-verification={self.ownership_txt_token}"
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kw):
|
||||
domain: CustomDomain = super(CustomDomain, cls).create(**kw)
|
||||
def create(cls, **kwargs):
|
||||
domain = kwargs.get("domain")
|
||||
if DeletedSubdomain.get_by(domain=domain):
|
||||
raise SubdomainInTrashError
|
||||
|
||||
domain: CustomDomain = super(CustomDomain, cls).create(**kwargs)
|
||||
|
||||
# generate a domain ownership txt token
|
||||
if not domain.ownership_txt_token:
|
||||
@ -1989,6 +1993,14 @@ class CustomDomain(Base, ModelMixin):
|
||||
|
||||
return domain
|
||||
|
||||
@classmethod
|
||||
def delete(cls, obj_id):
|
||||
obj: CustomDomain = cls.get(obj_id)
|
||||
if obj.is_sl_subdomain:
|
||||
DeletedSubdomain.create(domain=obj.domain)
|
||||
|
||||
return super(CustomDomain, cls).delete(obj_id)
|
||||
|
||||
@property
|
||||
def auto_create_rules(self):
|
||||
return sorted(self._auto_create_rules, key=lambda rule: rule.order)
|
||||
|
83
tests/dashboard/test_subdomain.py
Normal file
83
tests/dashboard/test_subdomain.py
Normal file
@ -0,0 +1,83 @@
|
||||
from flask import url_for
|
||||
|
||||
from app.db import Session
|
||||
from app.models import SLDomain, CustomDomain, Job
|
||||
from tests.utils import login
|
||||
|
||||
|
||||
def setup_sl_domain() -> SLDomain:
|
||||
"""Take the first SLDomain and set its can_use_subdomain=True"""
|
||||
sl_domain: SLDomain = SLDomain.first()
|
||||
sl_domain.can_use_subdomain = True
|
||||
Session.commit()
|
||||
|
||||
return sl_domain
|
||||
|
||||
|
||||
def test_create_subdomain(flask_client):
|
||||
login(flask_client)
|
||||
sl_domain = setup_sl_domain()
|
||||
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.subdomain_route"),
|
||||
data={"form-name": "create", "subdomain": "test", "domain": sl_domain.domain},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert f"New subdomain test.{sl_domain.domain} is created" in r.data.decode()
|
||||
assert CustomDomain.get_by(domain=f"test.{sl_domain.domain}") is not None
|
||||
|
||||
|
||||
def test_delete_subdomain(flask_client):
|
||||
user = login(flask_client)
|
||||
sl_domain = setup_sl_domain()
|
||||
|
||||
subdomain = CustomDomain.create(
|
||||
domain=f"test.{sl_domain.domain}",
|
||||
user_id=user.id,
|
||||
is_sl_subdomain=True,
|
||||
commit=True,
|
||||
)
|
||||
|
||||
nb_job = Job.count()
|
||||
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.domain_detail", custom_domain_id=subdomain.id),
|
||||
data={"form-name": "delete"},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert f"test.{sl_domain.domain} scheduled for deletion." in r.data.decode()
|
||||
|
||||
# a domain deletion job is scheduled
|
||||
assert Job.count() == nb_job + 1
|
||||
|
||||
|
||||
def test_create_subdomain_in_trash(flask_client):
|
||||
user = login(flask_client)
|
||||
sl_domain = setup_sl_domain()
|
||||
|
||||
subdomain = CustomDomain.create(
|
||||
domain=f"test.{sl_domain.domain}",
|
||||
user_id=user.id,
|
||||
is_sl_subdomain=True,
|
||||
commit=True,
|
||||
)
|
||||
|
||||
# delete the subdomain
|
||||
CustomDomain.delete(subdomain.id)
|
||||
assert CustomDomain.get_by(domain=f"test.{sl_domain.domain}") is None
|
||||
|
||||
r = flask_client.post(
|
||||
url_for("dashboard.subdomain_route"),
|
||||
data={"form-name": "create", "subdomain": "test", "domain": sl_domain.domain},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert (
|
||||
f"test.{sl_domain.domain} has been used before and cannot be reused"
|
||||
in r.data.decode()
|
||||
)
|
Loading…
Reference in New Issue
Block a user