From 2d841e9bc0327bff35fc109c08f896ef817d75ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Casaj=C3=BAs?= Date: Wed, 3 Jul 2024 14:59:16 +0200 Subject: [PATCH] Update render function to receive user always as a param (#2141) * Update render function to receive user always as a param (cherry picked from commit fb53632298b08ab40bb82b8c8724a0bf254b2632) * Add user to the kwargs --- app/alias_utils.py | 2 + app/api/views/auth.py | 8 ++-- app/auth/views/register.py | 2 +- app/dashboard/views/account_setting.py | 4 +- app/email_utils.py | 53 ++++++++++++++++++-------- app/handler/dmarc.py | 4 ++ app/handler/provider_complaint.py | 2 + app/jobs/export_user_data_job.py | 4 +- app/onboarding/views/final.py | 2 +- app/paddle_callback.py | 1 + cron.py | 6 +++ email_handler.py | 22 ++++++++++- job_runner.py | 9 ++--- server.py | 5 +++ 14 files changed, 94 insertions(+), 30 deletions(-) diff --git a/app/alias_utils.py b/app/alias_utils.py index 99f291eb..64bae459 100644 --- a/app/alias_utils.py +++ b/app/alias_utils.py @@ -453,10 +453,12 @@ def transfer_alias(alias, new_user, new_mailboxes: [Mailbox]): f"Alias {alias.email} has been received", render( "transactional/alias-transferred.txt", + user=old_user, alias=alias, ), render( "transactional/alias-transferred.html", + user=old_user, alias=alias, ), ) diff --git a/app/api/views/auth.py b/app/api/views/auth.py index af180d83..a22b30df 100644 --- a/app/api/views/auth.py +++ b/app/api/views/auth.py @@ -129,8 +129,8 @@ def auth_register(): send_email( email, "Just one more step to join SimpleLogin", - render("transactional/code-activation.txt.jinja2", code=code), - render("transactional/code-activation.html", code=code), + render("transactional/code-activation.txt.jinja2", user=user, code=code), + render("transactional/code-activation.html", user=user, code=code), ) RegisterEvent(RegisterEvent.ActionType.success, RegisterEvent.Source.api).send() @@ -226,8 +226,8 @@ def auth_reactivate(): send_email( email, "Just one more step to join SimpleLogin", - render("transactional/code-activation.txt.jinja2", code=code), - render("transactional/code-activation.html", code=code), + render("transactional/code-activation.txt.jinja2", user=user, code=code), + render("transactional/code-activation.html", user=user, code=code), ) return jsonify(msg="User needs to confirm their account"), 200 diff --git a/app/auth/views/register.py b/app/auth/views/register.py index 75053039..6740e579 100644 --- a/app/auth/views/register.py +++ b/app/auth/views/register.py @@ -125,4 +125,4 @@ def send_activation_email(user, next_url): LOG.d("redirect user to %s after activation", next_url) activation_link = activation_link + "&next=" + encode_url(next_url) - email_utils.send_activation_email(user.email, activation_link) + email_utils.send_activation_email(user, activation_link) diff --git a/app/dashboard/views/account_setting.py b/app/dashboard/views/account_setting.py index 5e3e0583..28c0241b 100644 --- a/app/dashboard/views/account_setting.py +++ b/app/dashboard/views/account_setting.py @@ -169,7 +169,7 @@ def send_reset_password_email(user): reset_password_link = f"{URL}/auth/reset_password?code={reset_password_code.code}" - email_utils.send_reset_password_email(user.email, reset_password_link) + email_utils.send_reset_password_email(user, reset_password_link) def send_change_email_confirmation(user: User, email_change: EmailChange): @@ -179,7 +179,7 @@ def send_change_email_confirmation(user: User, email_change: EmailChange): link = f"{URL}/auth/change_email?code={email_change.code}" - email_utils.send_change_email(email_change.new_email, user.email, link) + email_utils.send_change_email(user, email_change.new_email, link) @dashboard_bp.route("/resend_email_change", methods=["GET", "POST"]) diff --git a/app/email_utils.py b/app/email_utils.py index d962ac54..6048a99e 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -69,15 +69,20 @@ VERP_TIME_START = 1640995200 VERP_HMAC_ALGO = "sha3-224" -def render(template_name, **kwargs) -> str: +def render(template_name: str, user: Optional[User], **kwargs) -> str: templates_dir = os.path.join(config.ROOT_DIR, "templates", "emails") env = Environment(loader=FileSystemLoader(templates_dir)) template = env.get_template(template_name) + if user is None: + if current_user and current_user.is_authenticated: + user = current_user + use_partner_template = False - if current_user and current_user.is_authenticated: - use_partner_template = current_user.has_used_alias_from_partner() + if user: + use_partner_template = user.has_used_alias_from_partner() + kwargs["user"] = user return template.render( MAX_NB_EMAIL_FREE_PLAN=config.MAX_NB_EMAIL_FREE_PLAN, @@ -117,53 +122,59 @@ def send_trial_end_soon_email(user): ) -def send_activation_email(email, activation_link): +def send_activation_email(user: User, activation_link): send_email( - email, + user.email, "Just one more step to join SimpleLogin", render( "transactional/activation.txt", + user=user, activation_link=activation_link, - email=email, + email=user.email, ), render( "transactional/activation.html", + user=user, activation_link=activation_link, - email=email, + email=user.email, ), ) -def send_reset_password_email(email, reset_password_link): +def send_reset_password_email(user: User, reset_password_link): send_email( - email, + user.email, "Reset your password on SimpleLogin", render( "transactional/reset-password.txt", + user=user, reset_password_link=reset_password_link, ), render( "transactional/reset-password.html", + user=user, reset_password_link=reset_password_link, ), ) -def send_change_email(new_email, current_email, link): +def send_change_email(user: User, new_email, link): send_email( new_email, "Confirm email update on SimpleLogin", render( "transactional/change-email.txt", + user=user, link=link, new_email=new_email, - current_email=current_email, + current_email=user.email, ), render( "transactional/change-email.html", + user=user, link=link, new_email=new_email, - current_email=current_email, + current_email=user.email, ), ) @@ -176,28 +187,32 @@ def send_invalid_totp_login_email(user, totp_type): "Unsuccessful attempt to login to your SimpleLogin account", render( "transactional/invalid-totp-login.txt", + user=user, type=totp_type, ), render( "transactional/invalid-totp-login.html", + user=user, type=totp_type, ), 1, ) -def send_test_email_alias(email, name): +def send_test_email_alias(user: User, email: str): send_email( email, f"This email is sent to {email}", render( "transactional/test-email.txt", - name=name, + user=user, + name=user.name, alias=email, ), render( "transactional/test-email.html", - name=name, + user=user, + name=user.name, alias=email, ), ) @@ -212,11 +227,13 @@ def send_cannot_create_directory_alias(user, alias_address, directory_name): f"Alias {alias_address} cannot be created", render( "transactional/cannot-create-alias-directory.txt", + user=user, alias=alias_address, directory=directory_name, ), render( "transactional/cannot-create-alias-directory.html", + user=user, alias=alias_address, directory=directory_name, ), @@ -234,11 +251,13 @@ def send_cannot_create_directory_alias_disabled(user, alias_address, directory_n f"Alias {alias_address} cannot be created", render( "transactional/cannot-create-alias-directory-disabled.txt", + user=user, alias=alias_address, directory=directory_name, ), render( "transactional/cannot-create-alias-directory-disabled.html", + user=user, alias=alias_address, directory=directory_name, ), @@ -254,11 +273,13 @@ def send_cannot_create_domain_alias(user, alias, domain): f"Alias {alias} cannot be created", render( "transactional/cannot-create-alias-domain.txt", + user=user, alias=alias, domain=domain, ), render( "transactional/cannot-create-alias-domain.html", + user=user, alias=alias, domain=domain, ), @@ -1269,6 +1290,7 @@ def spf_pass( f"SimpleLogin Alert: attempt to send emails from your alias {alias.email} from unknown IP Address", render( "transactional/spf-fail.txt", + user=user, alias=alias.email, ip=ip, mailbox_url=config.URL + f"/dashboard/mailbox/{mailbox.id}#spf", @@ -1278,6 +1300,7 @@ def spf_pass( ), render( "transactional/spf-fail.html", + user=user, ip=ip, mailbox_url=config.URL + f"/dashboard/mailbox/{mailbox.id}#spf", to_email=contact_email, diff --git a/app/handler/dmarc.py b/app/handler/dmarc.py index 3d25196c..8d139b20 100644 --- a/app/handler/dmarc.py +++ b/app/handler/dmarc.py @@ -106,12 +106,14 @@ More info on https://simplelogin.io/docs/getting-started/anti-phishing/ f"An email sent to {alias.email} has been quarantined", render( "transactional/message-quarantine-dmarc.txt.jinja2", + user=user, from_header=from_header, alias=alias, refused_email_url=email_log.get_dashboard_url(), ), render( "transactional/message-quarantine-dmarc.html", + user=user, from_header=from_header, alias=alias, refused_email_url=email_log.get_dashboard_url(), @@ -176,12 +178,14 @@ def apply_dmarc_policy_for_reply_phase( f"Attempt to send an email to your contact {contact_recipient.email} from {envelope.mail_from}", render( "transactional/spoof-reply.txt.jinja2", + user=alias_from.user, contact=contact_recipient, alias=alias_from, sender=envelope.mail_from, ), render( "transactional/spoof-reply.html", + user=alias_from.user, contact=contact_recipient, alias=alias_from, sender=envelope.mail_from, diff --git a/app/handler/provider_complaint.py b/app/handler/provider_complaint.py index 3a10ef78..7454d5c9 100644 --- a/app/handler/provider_complaint.py +++ b/app/handler/provider_complaint.py @@ -319,11 +319,13 @@ def report_complaint_to_user_in_forward_phase( f"Abuse report from {capitalized_name}", render( "transactional/provider-complaint-forward-phase.txt.jinja2", + user=user, email=mailbox_email, provider=capitalized_name, ), render( "transactional/provider-complaint-forward-phase.html", + user=user, email=mailbox_email, provider=capitalized_name, ), diff --git a/app/jobs/export_user_data_job.py b/app/jobs/export_user_data_job.py index 48219304..665f9fde 100644 --- a/app/jobs/export_user_data_job.py +++ b/app/jobs/export_user_data_job.py @@ -137,7 +137,9 @@ class ExportUserDataJob: msg[headers.SUBJECT] = "Your SimpleLogin data" msg[headers.FROM] = f'"SimpleLogin (noreply)" <{config.NOREPLY}>' msg[headers.TO] = to_email - msg.attach(MIMEText(render("transactional/user-report.html"), "html")) + msg.attach( + MIMEText(render("transactional/user-report.html", user=self._user), "html") + ) attachment = MIMEApplication(zipped_contents.read()) attachment.add_header( "Content-Disposition", "attachment", filename="user_report.zip" diff --git a/app/onboarding/views/final.py b/app/onboarding/views/final.py index 64c271c2..d7f532bb 100644 --- a/app/onboarding/views/final.py +++ b/app/onboarding/views/final.py @@ -20,7 +20,7 @@ def final(): if form.validate_on_submit(): alias = Alias.get_by(email=form.email.data) if alias and alias.user_id == current_user.id: - send_test_email_alias(alias.email, current_user.name) + send_test_email_alias(current_user, alias.email) flash("An email is sent to your alias", "success") return render_template( diff --git a/app/paddle_callback.py b/app/paddle_callback.py index 7d7402a7..b0efee48 100644 --- a/app/paddle_callback.py +++ b/app/paddle_callback.py @@ -27,6 +27,7 @@ def failed_payment(sub: Subscription, subscription_id: str): "SimpleLogin - your subscription has failed to be renewed", render( "transactional/subscription-cancel.txt", + user=user, end_date=arrow.arrow.datetime.utcnow(), ), ) diff --git a/cron.py b/cron.py index 2f1e6c88..bfc150d9 100644 --- a/cron.py +++ b/cron.py @@ -266,11 +266,13 @@ def notify_manual_sub_end(): "Your SimpleLogin subscription will end soon", render( "transactional/coinbase/reminder-subscription.txt", + user=user, coinbase_subscription=coinbase_subscription, extend_subscription_url=extend_subscription_url, ), render( "transactional/coinbase/reminder-subscription.html", + user=user, coinbase_subscription=coinbase_subscription, extend_subscription_url=extend_subscription_url, ), @@ -826,10 +828,12 @@ def check_mailbox_valid_domain(): f"Mailbox {mailbox.email} is disabled", render( "transactional/disable-mailbox-warning.txt.jinja2", + user=mailbox.user, mailbox=mailbox, ), render( "transactional/disable-mailbox-warning.html", + user=mailbox.user, mailbox=mailbox, ), retries=3, @@ -884,6 +888,7 @@ def check_mailbox_valid_pgp_keys(): f"Mailbox {mailbox.email}'s PGP Key is invalid", render( "transactional/invalid-mailbox-pgp-key.txt.jinja2", + user=mailbox.user, mailbox=mailbox, ), retries=3, @@ -924,6 +929,7 @@ def check_single_custom_domain(custom_domain): f"Please update {custom_domain.domain} DNS on SimpleLogin", render( "transactional/custom-domain-dns-issue.txt.jinja2", + user=user, custom_domain=custom_domain, domain_dns_url=domain_dns_url, ), diff --git a/email_handler.py b/email_handler.py index 0319cf4d..2882e567 100644 --- a/email_handler.py +++ b/email_handler.py @@ -601,12 +601,14 @@ def handle_email_sent_to_ourself(alias, from_addr: str, msg: Message, user): f"Email sent to {alias.email} from its own mailbox {from_addr}", render( "transactional/cycle-email.txt.jinja2", + user=user, alias=alias, from_addr=from_addr, refused_email_url=refused_email_url, ), render( "transactional/cycle-email.html", + user=user, alias=alias, from_addr=from_addr, refused_email_url=refused_email_url, @@ -728,12 +730,14 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str f"Your mailbox {mailbox.email} is an alias", render( "transactional/mailbox-invalid.txt.jinja2", + user=mailbox.user, mailbox=mailbox, mailbox_url=mailbox_url, alias=alias, ), render( "transactional/mailbox-invalid.html", + user=mailbox.user, mailbox=mailbox, mailbox_url=mailbox_url, alias=alias, @@ -786,12 +790,14 @@ def forward_email_to_mailbox( f"Your mailbox {mailbox.email} and alias {alias.email} use the same domain", render( "transactional/mailbox-invalid.txt.jinja2", + user=mailbox.user, mailbox=mailbox, mailbox_url=mailbox_url, alias=alias, ), render( "transactional/mailbox-invalid.html", + user=mailbox.user, mailbox=mailbox, mailbox_url=mailbox_url, alias=alias, @@ -1276,6 +1282,7 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): f"Email sent to {contact.email} contains non reverse-alias addresses", render( "transactional/non-reverse-alias-reply-phase.txt.jinja2", + user=alias.user, destination=contact.email, alias=alias.email, subject=msg[headers.SUBJECT], @@ -1497,6 +1504,7 @@ def handle_unknown_mailbox( f"Attempt to use your alias {alias.email} from {envelope.mail_from}", render( "transactional/reply-must-use-personal-email.txt", + user=user, alias=alias, sender=envelope.mail_from, authorize_address_link=authorize_address_link, @@ -1504,6 +1512,7 @@ def handle_unknown_mailbox( ), render( "transactional/reply-must-use-personal-email.html", + user=user, alias=alias, sender=envelope.mail_from, authorize_address_link=authorize_address_link, @@ -1604,12 +1613,14 @@ def handle_bounce_forward_phase(msg: Message, email_log: EmailLog): f"Alias {alias.email} has been disabled due to multiple bounces", render( "transactional/bounce/automatic-disable-alias.txt", + user=alias.user, alias=alias, refused_email_url=refused_email_url, mailbox_email=mailbox.email, ), render( "transactional/bounce/automatic-disable-alias.html", + user=alias.user, alias=alias, refused_email_url=refused_email_url, mailbox_email=mailbox.email, @@ -1648,6 +1659,7 @@ def handle_bounce_forward_phase(msg: Message, email_log: EmailLog): f"An email sent to {alias.email} cannot be delivered to your mailbox", render( "transactional/bounce/bounced-email.txt.jinja2", + user=alias.user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -1657,6 +1669,7 @@ def handle_bounce_forward_phase(msg: Message, email_log: EmailLog): ), render( "transactional/bounce/bounced-email.html", + user=alias.user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -1749,12 +1762,14 @@ def handle_bounce_reply_phase(envelope, msg: Message, email_log: EmailLog): f"Email cannot be sent to { contact.email } from your alias { alias.email }", render( "transactional/bounce/bounce-email-reply-phase.txt", + user=user, alias=alias, contact=contact, refused_email_url=refused_email_url, ), render( "transactional/bounce/bounce-email-reply-phase.html", + user=user, alias=alias, contact=contact, refused_email_url=refused_email_url, @@ -1817,6 +1832,7 @@ def handle_spam( f"Email from {alias.email} to {contact.website_email} is detected as spam", render( "transactional/spam-email-reply-phase.txt", + user=user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -1824,6 +1840,7 @@ def handle_spam( ), render( "transactional/spam-email-reply-phase.html", + user=user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -1846,6 +1863,7 @@ def handle_spam( f"Email from {contact.website_email} to {alias.email} is detected as spam", render( "transactional/spam-email.txt", + user=user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -1853,6 +1871,7 @@ def handle_spam( ), render( "transactional/spam-email.html", + user=user, alias=alias, website_email=contact.website_email, disable_alias_link=disable_alias_link, @@ -2009,7 +2028,7 @@ def send_no_reply_response(mail_from: str, msg: Message): ALERT_TO_NOREPLY, mailbox.user.email, "Auto: {}".format(msg[headers.SUBJECT] or "No subject"), - render("transactional/noreply.text.jinja2"), + render("transactional/noreply.text.jinja2", user=mailbox.user), ) @@ -2091,6 +2110,7 @@ def handle(envelope: Envelope, msg: Message) -> str: "SimpleLogin shouldn't be used with another email forwarding system", render( "transactional/email-sent-from-reverse-alias.txt.jinja2", + user=user, ), ) diff --git a/job_runner.py b/job_runner.py index 90b018ab..1c3fdd3a 100644 --- a/job_runner.py +++ b/job_runner.py @@ -225,16 +225,15 @@ def process_job(job: Job): user_email = user.email LOG.w("Delete user %s", user) - User.delete(user.id) - Session.commit() - send_email( user_email, "Your SimpleLogin account has been deleted", - render("transactional/account-delete.txt"), - render("transactional/account-delete.html"), + render("transactional/account-delete.txt", user=user), + render("transactional/account-delete.html", user=user), retries=3, ) + User.delete(user.id) + Session.commit() elif job.name == config.JOB_DELETE_MAILBOX: delete_mailbox_job(job) diff --git a/server.py b/server.py index 57e9cf19..ecbe00a6 100644 --- a/server.py +++ b/server.py @@ -542,6 +542,7 @@ def setup_paddle_callback(app: Flask): "SimpleLogin - your subscription is canceled", render( "transactional/subscription-cancel.txt", + user=user, end_date=request.form.get("cancellation_effective_date"), ), ) @@ -722,10 +723,12 @@ def handle_coinbase_event(event) -> bool: "Your SimpleLogin account has been upgraded", render( "transactional/coinbase/new-subscription.txt", + user=user, coinbase_subscription=coinbase_subscription, ), render( "transactional/coinbase/new-subscription.html", + user=user, coinbase_subscription=coinbase_subscription, ), ) @@ -746,10 +749,12 @@ def handle_coinbase_event(event) -> bool: "Your SimpleLogin account has been extended", render( "transactional/coinbase/extend-subscription.txt", + user=user, coinbase_subscription=coinbase_subscription, ), render( "transactional/coinbase/extend-subscription.html", + user=user, coinbase_subscription=coinbase_subscription, ), )