(dimail) add reset password method

allow domain owner and admins to reset password for a mailbox
they manage. The request is sent to dimail, which responds with
a new randomly generated password. This new password is sent to
secondary email.
This commit is contained in:
Marie PUPO JEAMMET
2025-03-21 18:13:10 +01:00
committed by Marie
parent b5d86967ff
commit 2d56c57102
6 changed files with 339 additions and 23 deletions

View File

@@ -1,3 +1,5 @@
# pylint: disable=line-too-long
"""A minimalist client to synchronize with mailbox provisioning API."""
import ast
@@ -49,11 +51,20 @@ class DimailAPIClient:
Return Bearer token. Requires MAIL_PROVISIONING_API_CREDENTIALS setting,
to get a token from dimail /token/ endpoint.
"""
response = requests.get(
f"{self.API_URL}/token/",
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
timeout=self.API_TIMEOUT,
)
try:
response = requests.get(
f"{self.API_URL}/token/",
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
timeout=self.API_TIMEOUT,
)
except requests.exceptions.ConnectionError as error:
logger.error(
"Connection error while trying to reach %s.",
self.API_URL,
exc_info=error,
)
raise error
if response.status_code == status.HTTP_200_OK:
headers = {
@@ -254,35 +265,63 @@ class DimailAPIClient:
Send email to confirm mailbox creation
and send new mailbox information.
"""
title = _("Your new mailbox information")
template_name = "new_mailbox"
self._send_mailbox_related_email(
title, template_name, recipient, mailbox_data, issuer
)
def notify_mailbox_password_reset(self, recipient, mailbox_data, issuer=None):
"""
Send email to notify of password reset
and send new password.
"""
title = _("Your password has been updated")
template_name = "reset_password"
self._send_mailbox_related_email(
title, template_name, recipient, mailbox_data, issuer
)
# pylint: disable=too-many-arguments
# pylint: disable=too-many-positional-arguments
def _send_mailbox_related_email(
self, title, template_name, recipient, mailbox_data, issuer=None
):
"""
Send email with new mailbox or password reset information.
"""
context = {
"title": title,
"site": Site.objects.get_current(),
"webmail_url": settings.WEBMAIL_URL,
"mailbox_data": mailbox_data,
}
try:
with override(issuer.language if issuer else settings.LANGUAGE_CODE):
template_vars = {
"title": _("Your new mailbox information"),
"site": Site.objects.get_current(),
"webmail_url": settings.WEBMAIL_URL,
"mailbox_data": mailbox_data,
}
msg_html = render_to_string("mail/html/new_mailbox.html", template_vars)
msg_plain = render_to_string("mail/text/new_mailbox.txt", template_vars)
mail.send_mail(
template_vars["title"],
msg_plain,
context["title"],
render_to_string(f"mail/text/{template_name}.txt", context),
settings.EMAIL_FROM,
[recipient],
html_message=msg_html,
html_message=render_to_string(
f"mail/html/{template_name}.html", context
),
fail_silently=False,
)
except smtplib.SMTPException as exception:
logger.error(
"Failed to send mailbox information to %s was not sent: %s",
recipient,
exception,
)
else:
logger.info(
"Information for mailbox %s sent to %s.",
mailbox_data["email"],
recipient,
)
except smtplib.SMTPException as exception:
logger.error(
"Mailbox confirmation email to %s was not sent: %s",
recipient,
exception,
)
def import_mailboxes(self, domain):
"""Import mailboxes from dimail - open xchange in our database.
@@ -561,3 +600,41 @@ class DimailAPIClient:
exc_info=False,
)
return []
def reset_password(self, mailbox):
"""Send a request to reset mailbox password."""
if not mailbox.secondary_email or mailbox.secondary_email == str(mailbox):
raise exceptions.ValidationError(
"Password reset requires a secondary email address. Please add a valid secondary email before trying again."
)
try:
response = session.post(
f"{self.API_URL}/domains/{mailbox.domain.name}/mailboxes/{mailbox.local_part}/reset_password/",
headers={"Authorization": f"Basic {self.API_CREDENTIALS}"},
verify=True,
timeout=self.API_TIMEOUT,
)
except requests.exceptions.ConnectionError as error:
logger.exception(
"Connection error while trying to reach %s.",
self.API_URL,
exc_info=error,
)
raise error
if response.status_code == status.HTTP_200_OK:
# send new password to secondary email
self.notify_mailbox_password_reset(
recipient=mailbox.secondary_email,
mailbox_data={
"email": response.json()["email"],
"password": response.json()["password"],
},
)
logger.info(
"[DIMAIL] Password reset on mailbox %s.",
mailbox,
)
return response
return self.raise_exception_for_unexpected_response(response)