Skip to content
This repository has been archived by the owner on Dec 2, 2024. It is now read-only.

Commit

Permalink
feat: email templates & SMTP
Browse files Browse the repository at this point in the history
  • Loading branch information
tnix100 committed Aug 18, 2024
1 parent f1ea572 commit 79cf8a6
Show file tree
Hide file tree
Showing 21 changed files with 216 additions and 1 deletion.
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ INTERNAL_API_TOKEN="" # used for authenticating internal API requests (gives ac
CAPTCHA_SITEKEY=
CAPTCHA_SECRET=

EMAIL_SMTP_HOST=
EMAIL_SMTP_PORT=
EMAIL_SMTP_TLS=
EMAIL_SMTP_USERNAME=
EMAIL_SMTP_PASSWORD=
EMAIL_FROM_NAME=
EMAIL_FROM_ADDRESS=
EMAIL_PLATFORM_NAME="Meower"
EMAIL_PLATFORM_LOGO=""
EMAIL_PLATFORM_BRAND="Meower Media"
EMAIL_PLATFORM_FRONTEND="https://meower.org"
EMAIL_PLATFORM_SUPPORT="[email protected]"

GRPC_AUTH_ADDRESS="0.0.0.0:5000"
GRPC_AUTH_TOKEN=

Expand Down
23 changes: 23 additions & 0 deletions email_templates/_base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 720px; margin-left: auto; margin-right: auto; color: black;">
<div style="width: max-content; margin-left: auto; margin-right: auto; padding-bottom: 25px;">
<img src="{{ env['EMAIL_PLATFORM_LOGO'] }}" alt="{{ env['EMAIL_PLATFORM_NAME'] }} Logo" />
</div>
<div style="display: flex; flex-direction: column; gap: 25px; background-color: #f2f2f2; padding: 20px; border-radius: 6px; border-top: 7px solid #f29e2e; font-family: Arial, Helvetica, sans-serif; box-shadow: 0 0 5px rgba(0,0,0,.1);">
<span style="font-size: 32px; font-weight: 600;">{{ subject }}</span>
<span>Hey {{ name }}!</span>
{% block body %}{% endblock %}

{# We include the platform brand in the _lockdown.html file as well #}
{# because doing extends seems to cut-off the rest of the file. #}
{% if include_lockdown %}
{% extends "_lockdown.html" %}
{% else %}
<span>- {{ env['EMAIL_PLATFORM_BRAND'] }}</span>
{% endif %}
</div>
</div>
</body>
</html>
11 changes: 11 additions & 0 deletions email_templates/_base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Hey {{ name }}!

{% block body %}{% endblock %}

{# We include the platform brand in the _lockdown.txt file as well #}
{# because doing extends seems to cut-off the rest of the file. #}
{% if include_lockdown %}
{% extends "_lockdown.txt" %}
{% else %}
- {{ env['EMAIL_PLATFORM_BRAND'] }}
{% endif %}
9 changes: 9 additions & 0 deletions email_templates/_lockdown.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<span>If you didn't request this, please click the button below within the next 24 hours to revert this change and secure your account.</span>
<a
href="{{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/lockdown#{{ token }}"
target="_blank"
style="padding: 12px; background-color: #f29e2e; border-radius: 6px; text-decoration: none; color: white; max-width: fit-content;"
>
<b>This wasn't me!</b>
</a>
<span>- {{ env['EMAIL_PLATFORM_BRAND'] }}</span>
3 changes: 3 additions & 0 deletions email_templates/_lockdown.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
If you didn't request this, please follow this link within the next 24 hours to revert this change and secure your account: {{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/lockdown#{{ token }}

- {{ env['EMAIL_PLATFORM_BRAND'] }}
4 changes: 4 additions & 0 deletions email_templates/email_changed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.html" %}
{% block body %}
<span>The email on your {{ env['EMAIL_PLATFORM_NAME'] }} account was recently changed.</span>
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/email_changed.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.txt" %}
{% block body %}
The email on your {{ env['EMAIL_PLATFORM_NAME'] }} account was recently changed.
{% endblock %}
6 changes: 6 additions & 0 deletions email_templates/locked.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "_base.html" %}
{% block body %}
<span>Your {{ env['EMAIL_PLATFORM_NAME'] }} account has been locked because we believe it may have been compromised. This can happen if your {{ env['EMAIL_PLATFORM_NAME'] }} password is weak, you used the same password on another website and that website was hacked, or you accidentally gave an access token to someone else.</span>
<span>You will be required to reset your password using this email address ({{ address }}) before logging back in to {{ env['EMAIL_PLATFORM_NAME'] }}.</span>
<span>If you have any questions, please reach out to <a href="mailto:{{ env['EMAIL_PLATFORM_SUPPORT'] }}" target="_blank" style="color: black;">{{ env['EMAIL_PLATFORM_SUPPORT'] }}</a>.</span>
{% endblock %}
8 changes: 8 additions & 0 deletions email_templates/locked.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "_base.txt" %}
{% block body %}
Your {{ env['EMAIL_PLATFORM_NAME'] }} account has been locked because we believe it may have been compromised. This can happen if your {{ env['EMAIL_PLATFORM_NAME'] }} password is weak, you used the same password on another website and that website was hacked, or you accidentally gave an access token to someone else.

You will be required to reset your password using this email address ({{ address }}) before logging back in to {{ env['EMAIL_PLATFORM_NAME'] }}.

If you have any questions, please reach out to {{ env['EMAIL_PLATFORM_SUPPORT'] }}.
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/mfa_added.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.html" %}
{% block body %}
<span>A multi-factor authenticator was recently added to your {{ env['EMAIL_PLATFORM_NAME'] }} account.</span>
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/mfa_added.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.txt" %}
{% block body %}
A multi-factor authenticator was recently added to your {{ env['EMAIL_PLATFORM_NAME'] }} account.
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/mfa_removed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.html" %}
{% block body %}
<span>A multi-factor authenticator was recently removed from your {{ env['EMAIL_PLATFORM_NAME'] }} account.</span>
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/mfa_removed.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.txt" %}
{% block body %}
A multi-factor authenticator was recently removed from your {{ env['EMAIL_PLATFORM_NAME'] }} account.
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/password_changed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.html" %}
{% block body %}
<span>The password to your {{ env['EMAIL_PLATFORM_NAME'] }} account was recently changed.</span>
{% endblock %}
4 changes: 4 additions & 0 deletions email_templates/password_changed.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "_base.txt" %}
{% block body %}
The password to your {{ env['EMAIL_PLATFORM_NAME'] }} account was recently changed.
{% endblock %}
12 changes: 12 additions & 0 deletions email_templates/recovery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends "_base.html" %}
{% block body %}
<span>To reset your {{ env['EMAIL_PLATFORM_NAME'] }} account password, please click the button below.</span>
<span>If you didn't request this, please ignore this email, no further action is required.</span>
<a
href="{{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/recovery#{{ token }}"
target="_blank"
style="padding: 12px; background-color: #f29e2e; border-radius: 6px; text-decoration: none; color: white; max-width: fit-content;"
>
<b>Reset Password</b>
</a>
{% endblock %}
6 changes: 6 additions & 0 deletions email_templates/recovery.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "_base.txt" %}
{% block body %}
To reset your {{ env['EMAIL_PLATFORM_NAME'] }} account password, please follow this link: {{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/recovery#{{ token }}

If you didn't request this, please ignore this email, no further action is required.
{% endblock %}
12 changes: 12 additions & 0 deletions email_templates/verify.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends "_base.html" %}
{% block body %}
<span>To confirm adding your email address ({{ email }}) to your {{ env['EMAIL_PLATFORM_NAME'] }} account, please click the button below.</span>
<span>If you didn't request this, please ignore this email, no further action is required.</span>
<a
href="{{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/verify#{{ token }}"
target="_blank"
style="padding: 12px; background-color: #f29e2e; border-radius: 6px; text-decoration: none; color: white; max-width: fit-content;"
>
<b>Verify Email Address</b>
</a>
{% endblock %}
6 changes: 6 additions & 0 deletions email_templates/verify.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "_base.txt" %}
{% block body %}
To confirm adding your email address ({{ email }}) to your {{ env['EMAIL_PLATFORM_NAME'] }} account, please follow this link: {{ env['EMAIL_PLATFORM_FRONTEND'] }}/emails/verify#{{ token }}

If you didn't request this, please ignore this email, no further action is required.
{% endblock %}
50 changes: 49 additions & 1 deletion security.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from hashlib import sha256
from typing import Optional
import time, requests, os, uuid, secrets, bcrypt, msgpack
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr
import time, requests, os, uuid, secrets, bcrypt, msgpack, jinja2, smtplib

from database import db, rdb
from utils import log
Expand Down Expand Up @@ -43,6 +46,10 @@
TOKEN_BYTES = 64


email_file_loader = jinja2.FileSystemLoader("email_templates")
email_env = jinja2.Environment(loader=email_file_loader)


class UserFlags:
SYSTEM = 1
DELETED = 2
Expand Down Expand Up @@ -536,3 +543,44 @@ def hash_password(password: str) -> str:

def check_password_hash(password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(password.encode(), hashed_password.encode())


def send_email(template: str, to_name: str, to_address: str, token: Optional[str] = ""):
txt_tmpl = email_env.get_template(f"{template}.txt")
html_tmpl = email_env.get_template(f"{template}.html")

message = MIMEMultipart("alternative")
message["From"] = formataddr((os.environ["EMAIL_FROM_NAME"], os.environ["EMAIL_FROM_ADDRESS"]))
message["To"] = formataddr((to_name, to_address))

match template:
case "verify":
message["Subject"] = "Verify your email address"
case "recovery":
message["Subject"] = "Reset your password"
case "email_changed":
message["Subject"] = "Your email has been changed"
case "password_changed":
message["Subject"] = "Your password has been changed"
case "mfa_added":
message["Subject"] = "Multi-factor authenticator added"
case "mfa_removed":
message["Subject"] = "Multi-factor authenticator removed"
case "locked":
message["Subject"] = "Your account has been locked"

data = {
"subject": message["Subject"],
"name": to_name,
"address": to_address,
"token": token,
"env": os.environ
}
message.attach(MIMEText(txt_tmpl.render(data), "plain"))
message.attach(MIMEText(html_tmpl.render(data), "html"))

with smtplib.SMTP(os.environ["EMAIL_SMTP_HOST"], int(os.environ["EMAIL_SMTP_PORT"])) as server:
if os.getenv("EMAIL_SMTP_TLS"):
server.starttls()
server.login(os.environ["EMAIL_SMTP_USERNAME"], os.environ["EMAIL_SMTP_PASSWORD"])
server.sendmail(os.environ["EMAIL_FROM_ADDRESS"], to_address, message.as_string())
26 changes: 26 additions & 0 deletions test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 720px; margin-left: auto; margin-right: auto; color: black;">
<div style="width: max-content; margin-left: auto; margin-right: auto; padding-bottom: 25px;">
<img src="" alt=" Logo" />
</div>
<div style="display: flex; flex-direction: column; gap: 25px; background-color: #f2f2f2; padding: 20px; border-radius: 6px; border-top: 7px solid #f29e2e; font-family: Arial, Helvetica, sans-serif; box-shadow: 0 0 5px rgba(0,0,0,.1);">
<span style="font-size: 32px; font-weight: 600;">Your email has been changed</span>
<span>Hey !</span>

<span>The email on your account was recently changed.</span>





<span>If you didn't request this, please click the button below within the next 24 hours to revert this change and secure your account.</span>
<a
href="/emails/lockdown#"
target="_blank"
style="padding: 12px; background-color: #f29e2e; border-radius: 6px; text-decoration: none; color: white; max-width: fit-content;"
>
<b>This wasn't me!</b>
</a>
<span>- Meower Media</span>

0 comments on commit 79cf8a6

Please sign in to comment.