Add sensitive flag

This commit is contained in:
Raphael Michel
2026-01-29 22:47:11 +01:00
parent 164eac2e3f
commit da8c3711f9
9 changed files with 46 additions and 5 deletions

View File

@@ -32,6 +32,7 @@ class Migration(migrations.Migration):
("retry_after", models.DateTimeField(blank=True, null=True)), ("retry_after", models.DateTimeField(blank=True, null=True)),
("error", models.TextField(null=True)), ("error", models.TextField(null=True)),
("error_detail", models.TextField(null=True)), ("error_detail", models.TextField(null=True)),
("sensitive", models.BooleanField(default=False)),
("subject", models.TextField()), ("subject", models.TextField()),
("body_plain", models.TextField()), ("body_plain", models.TextField()),
("body_html", models.TextField(null=True)), ("body_html", models.TextField(null=True)),
@@ -40,7 +41,7 @@ class Migration(migrations.Migration):
("to", models.JSONField(default=list)), ("to", models.JSONField(default=list)),
("cc", models.JSONField(default=list)), ("cc", models.JSONField(default=list)),
("bcc", models.JSONField(default=list)), ("bcc", models.JSONField(default=list)),
("recipient_count", models.IntegerField(default=1)), ("recipient_count", models.IntegerField()),
("should_attach_tickets", models.BooleanField(default=False)), ("should_attach_tickets", models.BooleanField(default=False)),
("should_attach_ical", models.BooleanField(default=False)), ("should_attach_ical", models.BooleanField(default=False)),
("should_attach_other_files", models.JSONField(default=list)), ("should_attach_other_files", models.JSONField(default=list)),

View File

@@ -293,6 +293,7 @@ class Customer(LoggedModel):
locale=self.locale, locale=self.locale,
customer=self, customer=self,
organizer=self.organizer, organizer=self.organizer,
sensitive=True,
) )
def usable_gift_cards(self, used_cards=[]): def usable_gift_cards(self, used_cards=[]):

View File

@@ -135,6 +135,7 @@ class OutgoingMail(models.Model):
null=True, blank=True, null=True, blank=True,
) )
sensitive = models.BooleanField(default=False)
subject = models.TextField() subject = models.TextField()
body_plain = models.TextField() body_plain = models.TextField()
body_html = models.TextField(null=True) body_html = models.TextField(null=True)

View File

@@ -152,7 +152,8 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None, position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None,
customer: Customer = None, invoices: Sequence = None, attach_tickets=False, auto_email=True, user=None, customer: Customer = None, invoices: Sequence = None, attach_tickets=False, auto_email=True, user=None,
attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None, attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None,
plain_text_only=False, no_order_links=False, cc: Sequence[str]=None, bcc: Sequence[str]=None): plain_text_only=False, no_order_links=False, cc: Sequence[str]=None, bcc: Sequence[str]=None,
sensitive: bool=False):
""" """
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation. Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
@@ -208,6 +209,9 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
only allowed to use together with ``plain_text_only`` since HTML renderers add their own only allowed to use together with ``plain_text_only`` since HTML renderers add their own
links. links.
:param sensitive: If set to ``True``, the email content will not be shown as part of log entries, used e.g. for
password resets. Bcc will also not be used.
:raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean :raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
that the email has been sent, just that it has been queued by the email backend. that the email has been sent, just that it has been queued by the email backend.
""" """
@@ -228,7 +232,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
headers.setdefault('X-PX-Correlation', str(guid)) headers.setdefault('X-PX-Correlation', str(guid))
bcc = list(bcc or []) bcc = list(bcc or [])
if settings_holder and settings_holder.settings.mail_bcc: if settings_holder and settings_holder.settings.mail_bcc and not sensitive:
for bcc_mail in settings_holder.settings.mail_bcc.split(','): for bcc_mail in settings_holder.settings.mail_bcc.split(','):
bcc.append(bcc_mail.strip()) bcc.append(bcc_mail.strip())
@@ -321,6 +325,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
should_attach_tickets=attach_tickets, should_attach_tickets=attach_tickets,
should_attach_ical=attach_ical, should_attach_ical=attach_ical,
should_attach_other_files=attach_other_files or [], should_attach_other_files=attach_other_files or [],
sensitive=sensitive,
) )
if invoices and not position: if invoices and not position:
m.should_attach_invoices.add(*invoices) m.should_attach_invoices.add(*invoices)
@@ -721,6 +726,7 @@ def mail_send_task(self, *args, outgoing_mail: int) -> bool:
}, },
} }
) )
return True
def mail_send(to: List[str], subject: str, body: str, html: Optional[str], sender: str, def mail_send(to: List[str], subject: str, body: str, html: Optional[str], sender: str,

View File

@@ -585,6 +585,7 @@ class MailSettingsForm(SettingsForm):
help_text=''.join([ help_text=''.join([
str(_("All emails will be sent to this address as a Bcc copy.")), str(_("All emails will be sent to this address as a Bcc copy.")),
str(_("You can specify multiple recipients separated by commas.")), str(_("You can specify multiple recipients separated by commas.")),
str(_("Sensitive emails like password resets will not be sent in Bcc.")),
]), ]),
validators=[multimail_validate], validators=[multimail_validate],
required=False, required=False,

View File

@@ -171,13 +171,39 @@
<div role="tabpanel" <div role="tabpanel"
class="tab-pane {% if not mail.is_failed %}active{% endif %}" class="tab-pane {% if not mail.is_failed %}active{% endif %}"
id="tab-html"> id="tab-html">
{{ data_url|json_script:"mail_body_html" }} {% if mail.sensitive %}
<div class="empty-collection">
<p>
{% icon "eye-slash 4x" %}
</p>
<p>
{% blocktrans trimmed %}
Sensitive content not shown for security reasons
{% endblocktrans %}
</p>
</div>
{% else %}
{{ data_url|json_script:"mail_body_html" }}
{% endif %}
</div> </div>
{% endif %} {% endif %}
<div role="tabpanel" <div role="tabpanel"
class="tab-pane {% if not mail.is_failed and not mail.body_html %}active{% endif %}" class="tab-pane {% if not mail.is_failed and not mail.body_html %}active{% endif %}"
id="tab-text"> id="tab-text">
<pre><code>{{ mail.body_plain }}</code></pre> {% if mail.sensitive %}
<div class="empty-collection">
<p>
{% icon "eye-slash 4x" %}
</p>
<p>
{% blocktrans trimmed %}
Sensitive content not shown for security reasons
{% endblocktrans %}
</p>
</div>
{% else %}
<pre><code>{{ mail.body_plain }}</code></pre>
{% endif %}
</div> </div>
<div role="tabpanel" <div role="tabpanel"
class="tab-pane" class="tab-pane"

View File

@@ -96,6 +96,9 @@
<a href="{% url "control:organizer.outgoingmail" organizer=request.organizer.slug mail=m.id %}"> <a href="{% url "control:organizer.outgoingmail" organizer=request.organizer.slug mail=m.id %}">
{{ m.subject }} {{ m.subject }}
</a> </a>
{% if m.sensitive %}
<span class="text-muted">{% icon "eye-slash" %}</span>
{% endif %}
</td> </td>
<td> <td>
{{ m.to|join:", " }} {{ m.to|join:", " }}

View File

@@ -3011,6 +3011,7 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
locale=self.customer.locale, locale=self.customer.locale,
customer=self.customer, customer=self.customer,
organizer=self.request.organizer, organizer=self.request.organizer,
sensitive=True,
) )
messages.success( messages.success(
self.request, self.request,

View File

@@ -325,6 +325,7 @@ class ResetPasswordView(FormView):
locale=customer.locale, locale=customer.locale,
customer=customer, customer=customer,
organizer=self.request.organizer, organizer=self.request.organizer,
sensitive=True,
) )
messages.success( messages.success(
self.request, self.request,