mirror of
https://github.com/simple-login/app.git
synced 2024-11-10 21:27:10 +01:00
Add show domain alias and deleted alias
This commit is contained in:
parent
a8988cb8f6
commit
d5869b849c
@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import annotations
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
@ -30,6 +31,8 @@ from app.models import (
|
|||||||
Newsletter,
|
Newsletter,
|
||||||
PADDLE_SUBSCRIPTION_GRACE_DAYS,
|
PADDLE_SUBSCRIPTION_GRACE_DAYS,
|
||||||
Mailbox,
|
Mailbox,
|
||||||
|
DeletedAlias,
|
||||||
|
DomainDeletedAlias,
|
||||||
)
|
)
|
||||||
from app.newsletter_utils import send_newsletter_to_user, send_newsletter_to_address
|
from app.newsletter_utils import send_newsletter_to_user, send_newsletter_to_address
|
||||||
|
|
||||||
@ -729,6 +732,67 @@ class InvalidMailboxDomainAdmin(SLModelView):
|
|||||||
can_delete = True
|
can_delete = True
|
||||||
|
|
||||||
|
|
||||||
|
class EmailSearchResult:
|
||||||
|
no_match: bool = True
|
||||||
|
alias: Optional[Alias] = None
|
||||||
|
mailbox: Optional[Mailbox] = None
|
||||||
|
deleted_alias: Optional[DeletedAlias] = None
|
||||||
|
deleted_custom_alias: Optional[DomainDeletedAlias] = None
|
||||||
|
user: Optional[User] = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_email(email: str) -> EmailSearchResult:
|
||||||
|
output = EmailSearchResult()
|
||||||
|
alias = Alias.get_by(email=email)
|
||||||
|
if alias:
|
||||||
|
output.alias = alias
|
||||||
|
output.no_match = False
|
||||||
|
return output
|
||||||
|
user = User.get_by(email=email)
|
||||||
|
if user:
|
||||||
|
output.user = user
|
||||||
|
output.no_match = False
|
||||||
|
return output
|
||||||
|
mailbox = Mailbox.get_by(email=email)
|
||||||
|
if mailbox:
|
||||||
|
output.mailbox = mailbox
|
||||||
|
output.no_match = False
|
||||||
|
return output
|
||||||
|
deleted_alias = DeletedAlias.get_by(email=email)
|
||||||
|
if deleted_alias:
|
||||||
|
output.deleted_alias = deleted_alias
|
||||||
|
output.no_match = False
|
||||||
|
return output
|
||||||
|
domain_deleted_alias = DomainDeletedAlias.get_by(email=email)
|
||||||
|
if domain_deleted_alias:
|
||||||
|
output.domain_deleted_alias = domain_deleted_alias
|
||||||
|
output.no_match = False
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class EmailSearchHelpers:
|
||||||
|
@staticmethod
|
||||||
|
def mailbox_list(user: User) -> list[Mailbox]:
|
||||||
|
return (
|
||||||
|
Mailbox.filter_by(user_id=user.id)
|
||||||
|
.order_by(Mailbox.id.asc())
|
||||||
|
.limit(10)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mailbox_count(user: User) -> int:
|
||||||
|
return Mailbox.filter_by(user_id=user.id).order_by(Mailbox.id.asc()).count()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def alias_list(user: User) -> list[Alias]:
|
||||||
|
return Alias.filter_by(user_id=user.id).order_by(Alias.id.asc()).limit(10).all()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def alias_count(user: User) -> int:
|
||||||
|
return Alias.filter_by(user_id=user.id).count()
|
||||||
|
|
||||||
|
|
||||||
class EmailSearchAdmin(BaseView):
|
class EmailSearchAdmin(BaseView):
|
||||||
def is_accessible(self):
|
def is_accessible(self):
|
||||||
return current_user.is_authenticated and current_user.is_admin
|
return current_user.is_authenticated and current_user.is_admin
|
||||||
@ -740,50 +804,16 @@ class EmailSearchAdmin(BaseView):
|
|||||||
|
|
||||||
@expose("/", methods=["GET", "POST"])
|
@expose("/", methods=["GET", "POST"])
|
||||||
def index(self):
|
def index(self):
|
||||||
alias = None
|
search = EmailSearchResult()
|
||||||
user = None
|
email = ""
|
||||||
mailbox = None
|
|
||||||
no_match = False
|
|
||||||
email = None
|
|
||||||
if request.form and request.form["email"]:
|
if request.form and request.form["email"]:
|
||||||
email = request.form["email"]
|
email = request.form["email"]
|
||||||
alias = Alias.get_by(email=email)
|
email = email.strip()
|
||||||
user = User.get_by(email=email)
|
search = EmailSearchResult.from_email(email)
|
||||||
mailbox = Mailbox.get_by(email=email)
|
|
||||||
if not alias and not user and not mailbox:
|
|
||||||
no_match = True
|
|
||||||
|
|
||||||
def user_mailboxes(user_id: int) -> list[Mailbox]:
|
|
||||||
return (
|
|
||||||
Mailbox.filter_by(user_id=user_id)
|
|
||||||
.order_by(Mailbox.id.asc())
|
|
||||||
.limit(10)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
def user_mailboxes_count(user_id: int) -> int:
|
|
||||||
return Mailbox.filter_by(user_id=user_id).order_by(Mailbox.id.asc()).count()
|
|
||||||
|
|
||||||
def user_aliases(user_id: int) -> list[Alias]:
|
|
||||||
return (
|
|
||||||
Alias.filter_by(user_id=user_id)
|
|
||||||
.order_by(Alias.id.asc())
|
|
||||||
.limit(10)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
def user_aliases_count(user_id: int) -> int:
|
|
||||||
return Alias.filter_by(user_id=user_id).count()
|
|
||||||
|
|
||||||
return self.render(
|
return self.render(
|
||||||
"admin/email_search.html",
|
"admin/email_search.html",
|
||||||
email=email,
|
email=email,
|
||||||
no_match=no_match,
|
data=search,
|
||||||
alias=alias,
|
helper=EmailSearchHelpers,
|
||||||
mailbox=mailbox,
|
|
||||||
user=user,
|
|
||||||
user_aliases=user_aliases,
|
|
||||||
user_aliases_count=user_aliases_count,
|
|
||||||
user_mailboxes=user_mailboxes,
|
|
||||||
user_mailboxes_count=user_mailboxes_count,
|
|
||||||
)
|
)
|
||||||
|
@ -63,12 +63,16 @@ def get_user_if_alias_would_auto_create(
|
|||||||
# Prevent addresses with unicode characters (🤯) in them for now.
|
# Prevent addresses with unicode characters (🤯) in them for now.
|
||||||
validate_email(address, check_deliverability=False, allow_smtputf8=False)
|
validate_email(address, check_deliverability=False, allow_smtputf8=False)
|
||||||
except EmailNotValidError:
|
except EmailNotValidError:
|
||||||
|
LOG.i(f"Not creating alias for {address} because email is invalid")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
domain_and_rule = check_if_alias_can_be_auto_created_for_custom_domain(
|
domain_and_rule = check_if_alias_can_be_auto_created_for_custom_domain(
|
||||||
address, notify_user=notify_user
|
address, notify_user=notify_user
|
||||||
)
|
)
|
||||||
if DomainDeletedAlias.get_by(email=address):
|
if DomainDeletedAlias.get_by(email=address):
|
||||||
|
LOG.i(
|
||||||
|
f"Not creating alias for {address} because it was previously deleted for this domain"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
if domain_and_rule:
|
if domain_and_rule:
|
||||||
return domain_and_rule[0].user
|
return domain_and_rule[0].user
|
||||||
@ -93,6 +97,9 @@ def check_if_alias_can_be_auto_created_for_custom_domain(
|
|||||||
custom_domain: CustomDomain = CustomDomain.get_by(domain=alias_domain)
|
custom_domain: CustomDomain = CustomDomain.get_by(domain=alias_domain)
|
||||||
|
|
||||||
if not custom_domain:
|
if not custom_domain:
|
||||||
|
LOG.i(
|
||||||
|
f"Cannot auto-create custom domain alias for {address} because there's no custom domain for {alias_domain}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
user: User = custom_domain.user
|
user: User = custom_domain.user
|
||||||
@ -108,6 +115,9 @@ def check_if_alias_can_be_auto_created_for_custom_domain(
|
|||||||
|
|
||||||
if not custom_domain.catch_all:
|
if not custom_domain.catch_all:
|
||||||
if len(custom_domain.auto_create_rules) == 0:
|
if len(custom_domain.auto_create_rules) == 0:
|
||||||
|
LOG.i(
|
||||||
|
f"Cannot create alias {address} for domain {custom_domain} because it has no catch-all and no rules"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
local = get_email_local_part(address)
|
local = get_email_local_part(address)
|
||||||
|
|
||||||
@ -121,7 +131,7 @@ def check_if_alias_can_be_auto_created_for_custom_domain(
|
|||||||
)
|
)
|
||||||
return custom_domain, rule
|
return custom_domain, rule
|
||||||
else: # no rule passes
|
else: # no rule passes
|
||||||
LOG.d("no rule passed to create %s", local)
|
LOG.d(f"No rule matches auto-create {address} for domain {custom_domain}")
|
||||||
return None
|
return None
|
||||||
LOG.d("Create alias via catchall")
|
LOG.d("Create alias via catchall")
|
||||||
|
|
||||||
@ -148,6 +158,7 @@ def check_if_alias_can_be_auto_created_for_a_directory(
|
|||||||
sep = "#"
|
sep = "#"
|
||||||
else:
|
else:
|
||||||
# if there's no directory separator in the alias, no way to auto-create it
|
# if there's no directory separator in the alias, no way to auto-create it
|
||||||
|
LOG.info(f"Cannot auto-create {address} since it has no directory separator")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
directory_name = address[: address.find(sep)]
|
directory_name = address[: address.find(sep)]
|
||||||
@ -155,6 +166,9 @@ def check_if_alias_can_be_auto_created_for_a_directory(
|
|||||||
|
|
||||||
directory = Directory.get_by(name=directory_name)
|
directory = Directory.get_by(name=directory_name)
|
||||||
if not directory:
|
if not directory:
|
||||||
|
LOG.info(
|
||||||
|
f"Cannot auto-create {address} because there is no directory for {directory_name}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
user: User = directory.user
|
user: User = directory.user
|
||||||
@ -163,12 +177,17 @@ def check_if_alias_can_be_auto_created_for_a_directory(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if not user.can_create_new_alias():
|
if not user.can_create_new_alias():
|
||||||
LOG.d(f"{user} can't create new directory alias {address}")
|
LOG.d(
|
||||||
|
f"{user} can't create new directory alias {address} because user cannot create aliases"
|
||||||
|
)
|
||||||
if notify_user:
|
if notify_user:
|
||||||
send_cannot_create_directory_alias(user, address, directory_name)
|
send_cannot_create_directory_alias(user, address, directory_name)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if directory.disabled:
|
if directory.disabled:
|
||||||
|
LOG.d(
|
||||||
|
f"{user} can't create new directory alias {address} bcause directory is disabled"
|
||||||
|
)
|
||||||
if notify_user:
|
if notify_user:
|
||||||
send_cannot_create_directory_alias_disabled(user, address, directory_name)
|
send_cannot_create_directory_alias_disabled(user, address, directory_name)
|
||||||
return None
|
return None
|
||||||
|
@ -548,7 +548,9 @@ def can_create_directory_for_address(email_address: str) -> bool:
|
|||||||
for domain in config.ALIAS_DOMAINS:
|
for domain in config.ALIAS_DOMAINS:
|
||||||
if email_address.endswith("@" + domain):
|
if email_address.endswith("@" + domain):
|
||||||
return True
|
return True
|
||||||
|
LOG.i(
|
||||||
|
f"Cannot create address in directory for {email_address} since it does not belong to a valid directory domain"
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,6 +79,100 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
{% macro show_deleted_alias(deleted_alias) -%}
|
||||||
|
<h4>
|
||||||
|
Deleted Alias {{ deleted_alias.email }} with ID {{ deleted_alias.id }}.
|
||||||
|
</h4>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
Deleted Alias ID
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Deleted At
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Reason
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ deleted_alias.id }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ deleted_alias.email }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ deleted_alias.created_at }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ deleted_alias.reason }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{%- endmacro %}
|
||||||
|
{% macro show_domain_deleted_alias(dom_deleted_alias) -%}
|
||||||
|
<h4>
|
||||||
|
Domain Deleted Alias {{ dom_deleted_alias.email }} with ID {{ dom_deleted_alias.id }} for domain {{ dom_deleted_alias.domain.domain }}
|
||||||
|
</h4>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
Deleted Alias ID
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Domain
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Domain ID
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Domain owner user ID
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Domain owner user email
|
||||||
|
</th>
|
||||||
|
<th scope="col">
|
||||||
|
Deleted At
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.id }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.email }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.domain.domain }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.domain.id }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.domain.user_id }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ dom_deleted_alias.created_at }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ show_user(data.domain_deleted_alias.domain.user) }}
|
||||||
|
{%- endmacro %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
||||||
@ -104,36 +198,54 @@
|
|||||||
No user, alias or mailbox found for {{ email }}
|
No user, alias or mailbox found for {{ email }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if alias %}
|
{% if data.alias %}
|
||||||
|
|
||||||
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
||||||
<h3 class="mb-3">
|
<h3 class="mb-3">
|
||||||
Found Alias {{ alias.email }}
|
Found Alias {{ data.alias.email }}
|
||||||
</h3>
|
</h3>
|
||||||
{{ list_alias(1,[alias]) }}
|
{{ list_alias(1,[data.alias]) }}
|
||||||
{{ show_user(alias.user) }}
|
{{ show_user(data.alias.user) }}
|
||||||
{{ list_mailboxes(user_mailboxes_count(alias.user.id), user_mailboxes(alias.user.id) ) }}
|
{{ list_mailboxes(helper.mailbox_count(data.alias.user), helper.mailbox_list(data.alias.user) ) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user %}
|
{% if data.user %}
|
||||||
|
|
||||||
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
<div class="border border-dark border-2 mt-1 mb-2 p-3">
|
||||||
<h3 class="mb-3">
|
<h3 class="mb-3">
|
||||||
Found User {{ user.email }}
|
Found User {{ data.user.email }}
|
||||||
</h3>
|
</h3>
|
||||||
{{ show_user(user) }}
|
{{ show_user(data.user) }}
|
||||||
{{ list_mailboxes(user_mailboxes_count(user.id), user_mailboxes(user.id) ) }}
|
{{ list_mailboxes(helper.mailbox_count(data.user), helper.mailbox_list(data.user) ) }}
|
||||||
{{ list_alias(user_aliases_count(user.id),user_aliases(user.id)) }}
|
{{ list_alias(helper.alias_count(data.user),helper.alias_list(data.user)) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if mailbox %}
|
{% if data.mailbox %}
|
||||||
|
|
||||||
<div class="border border-dark mt-1 mb-2 p-3">
|
<div class="border border-dark mt-1 mb-2 p-3">
|
||||||
<h3 class="mb-3">
|
<h3 class="mb-3">
|
||||||
Found Mailbox {{ mailbox.email }}
|
Found Mailbox {{ data.mailbox.email }}
|
||||||
</h3>
|
</h3>
|
||||||
{{ list_mailboxes(1, [mailbox] ) }}
|
{{ list_mailboxes(1, [data.mailbox] ) }}
|
||||||
{{ show_user(mailbox.user) }}
|
{{ show_user(data.mailbox.user) }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if data.deleted_alias %}
|
||||||
|
|
||||||
|
<div class="border border-dark mt-1 mb-2 p-3">
|
||||||
|
<h3 class="mb-3">
|
||||||
|
Found DeletedAlias {{ data.deleted_alias.email }}
|
||||||
|
</h3>
|
||||||
|
{{ show_deleted_alias(data.deleted_alias) }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if data.domain_deleted_alias %}
|
||||||
|
|
||||||
|
<div class="border border-dark mt-1 mb-2 p-3">
|
||||||
|
<h3 class="mb-3">
|
||||||
|
Found DomainDeletedAlias {{ data.domain_deleted_alias.email }}
|
||||||
|
</h3>
|
||||||
|
{{ show_domain_deleted_alias(data.domain_deleted_alias) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user