ja
This commit is contained in:
+230
-124
@@ -1,17 +1,19 @@
|
||||
import hashlib
|
||||
import secrets
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import os
|
||||
import secrets
|
||||
import smtplib
|
||||
import sqlite3
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from email.message import EmailMessage
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
DB_NAME = Path(__file__).parent.parent / "database" / "calendar.db"
|
||||
|
||||
|
||||
@@ -28,7 +30,207 @@ def hash_token(token: str) -> str:
|
||||
).hexdigest()
|
||||
|
||||
|
||||
def update_email(user_id: int, new_email: str):
|
||||
# ============================================================
|
||||
# EMAIL
|
||||
# ============================================================
|
||||
|
||||
def send_email(
|
||||
email: str,
|
||||
subject: str,
|
||||
plain_text: str,
|
||||
html: str,
|
||||
):
|
||||
smtp_host = os.getenv("SMTP_HOST")
|
||||
smtp_port = int(os.getenv("SMTP_PORT", "587"))
|
||||
smtp_username = os.getenv("SMTP_USERNAME")
|
||||
smtp_password = os.getenv("SMTP_PASSWORD")
|
||||
smtp_from = os.getenv("SMTP_FROM")
|
||||
|
||||
if not all([
|
||||
smtp_host,
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
smtp_from,
|
||||
]):
|
||||
raise RuntimeError("SMTP configuration is incomplete")
|
||||
|
||||
message = EmailMessage()
|
||||
|
||||
message["From"] = smtp_from
|
||||
message["To"] = email
|
||||
message["Subject"] = subject
|
||||
|
||||
# Plain-text fallback
|
||||
message.set_content(plain_text)
|
||||
|
||||
# HTML version
|
||||
message.add_alternative(
|
||||
html,
|
||||
subtype="html",
|
||||
)
|
||||
|
||||
print(f"Sending email to {email}")
|
||||
|
||||
with smtplib.SMTP(
|
||||
smtp_host,
|
||||
smtp_port,
|
||||
) as smtp:
|
||||
|
||||
print("SMTP connected")
|
||||
|
||||
smtp.starttls()
|
||||
|
||||
print("TLS started")
|
||||
|
||||
smtp.login(
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
)
|
||||
|
||||
print("SMTP login successful")
|
||||
|
||||
smtp.send_message(message)
|
||||
|
||||
print("Email sent")
|
||||
|
||||
|
||||
def send_verification_email(
|
||||
email: str,
|
||||
token: str,
|
||||
):
|
||||
verification_url = (
|
||||
"https://ben.de-roo.org"
|
||||
f"/api/verify-email?token={token}"
|
||||
)
|
||||
|
||||
plain_text = f"""Hello,
|
||||
|
||||
You requested to use this email address for your Calendar account.
|
||||
|
||||
Verify your email address using this link:
|
||||
|
||||
{verification_url}
|
||||
|
||||
This link expires after 24 hours.
|
||||
|
||||
If you did not request this, you can ignore this email.
|
||||
"""
|
||||
|
||||
html = f"""\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>
|
||||
You requested to use this email address
|
||||
for your Calendar account.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{verification_url}">
|
||||
Verify your email address
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This link expires after 24 hours.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you did not request this,
|
||||
you can ignore this email.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
send_email(
|
||||
email=email,
|
||||
subject="Verify your Calendar email address",
|
||||
plain_text=plain_text,
|
||||
html=html,
|
||||
)
|
||||
|
||||
|
||||
def send_password_reset_email(
|
||||
email: str,
|
||||
token: str,
|
||||
):
|
||||
password_reset_url = (
|
||||
"https://ben.de-roo.org"
|
||||
f"/api/update-password_page?token={token}"
|
||||
)
|
||||
|
||||
plain_text = f"""Hello,
|
||||
|
||||
You requested to reset your Calendar password.
|
||||
|
||||
Reset your password using this link:
|
||||
|
||||
{password_reset_url}
|
||||
|
||||
This link expires after 1 hour.
|
||||
|
||||
If you did not request this, you can ignore this email.
|
||||
"""
|
||||
|
||||
html = f"""\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>
|
||||
You requested to reset your Calendar password.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{password_reset_url}">
|
||||
Reset your password
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This link expires after 1 hour.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you did not request this,
|
||||
you can ignore this email.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
send_email(
|
||||
email=email,
|
||||
subject="Reset your password",
|
||||
plain_text=plain_text,
|
||||
html=html,
|
||||
)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# EMAIL ADDRESS VERIFICATION
|
||||
# ============================================================
|
||||
|
||||
def update_email(
|
||||
user_id: int,
|
||||
new_email: str,
|
||||
):
|
||||
new_email = new_email.strip().lower()
|
||||
|
||||
conn = get_connection()
|
||||
@@ -57,7 +259,10 @@ def update_email(user_id: int, new_email: str):
|
||||
WHERE email = ?
|
||||
AND id != ?
|
||||
""",
|
||||
(new_email, user_id),
|
||||
(
|
||||
new_email,
|
||||
user_id,
|
||||
),
|
||||
)
|
||||
|
||||
if cur.fetchone() is not None:
|
||||
@@ -73,7 +278,10 @@ def update_email(user_id: int, new_email: str):
|
||||
email_verified = 0
|
||||
WHERE id = ?
|
||||
""",
|
||||
(new_email, user_id),
|
||||
(
|
||||
new_email,
|
||||
user_id,
|
||||
),
|
||||
)
|
||||
|
||||
cur.execute(
|
||||
@@ -104,7 +312,9 @@ def update_email(user_id: int, new_email: str):
|
||||
conn.close()
|
||||
|
||||
|
||||
def create_verification(user_id: int):
|
||||
def create_verification(
|
||||
user_id: int,
|
||||
):
|
||||
token = secrets.token_urlsafe(32)
|
||||
token_hash = hash_token(token)
|
||||
|
||||
@@ -149,7 +359,9 @@ def create_verification(user_id: int):
|
||||
conn.close()
|
||||
|
||||
|
||||
def verify_email(token: str):
|
||||
def verify_email(
|
||||
token: str,
|
||||
):
|
||||
token_hash = hash_token(token)
|
||||
|
||||
conn = get_connection()
|
||||
@@ -181,6 +393,7 @@ def verify_email(token: str):
|
||||
)
|
||||
|
||||
if datetime.now(timezone.utc) >= expires_at:
|
||||
|
||||
cur.execute(
|
||||
"""
|
||||
DELETE FROM email_verifications
|
||||
@@ -222,121 +435,14 @@ def verify_email(token: str):
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def send_verification_email(email: str, token: str):
|
||||
smtp_host = os.getenv("SMTP_HOST")
|
||||
smtp_port = int(os.getenv("SMTP_PORT", "587"))
|
||||
smtp_username = os.getenv("SMTP_USERNAME")
|
||||
smtp_password = os.getenv("SMTP_PASSWORD")
|
||||
smtp_from = os.getenv("SMTP_FROM")
|
||||
|
||||
if not all([
|
||||
smtp_host,
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
smtp_from,
|
||||
]):
|
||||
raise RuntimeError("SMTP configuration is incomplete")
|
||||
# ============================================================
|
||||
# PASSWORD RESET
|
||||
# ============================================================
|
||||
|
||||
verification_url = (
|
||||
f"https://ben.de-roo.org/api/verify-email?token={token}"
|
||||
)
|
||||
|
||||
message = EmailMessage()
|
||||
|
||||
message["From"] = smtp_from
|
||||
message["To"] = email
|
||||
message["Subject"] = "Verify your Calendar email address"
|
||||
|
||||
message.set_content(
|
||||
f"""Hello,
|
||||
|
||||
You requested to use this email address for your Calendar account.
|
||||
|
||||
Verify your email address using this link:
|
||||
|
||||
{verification_url}
|
||||
|
||||
This link expires after 24 hours.
|
||||
|
||||
If you did not request this, you can ignore this email.
|
||||
"""
|
||||
)
|
||||
|
||||
print(f"Sending verification email to {email}")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as smtp:
|
||||
print("SMTP connected")
|
||||
|
||||
smtp.starttls()
|
||||
print("TLS started")
|
||||
|
||||
smtp.login(
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
)
|
||||
print("SMTP login successful")
|
||||
|
||||
smtp.send_message(message)
|
||||
print("Verification email sent")
|
||||
|
||||
def send_password_reset_email(email: str, token: str):
|
||||
smtp_host = os.getenv("SMTP_HOST")
|
||||
smtp_port = int(os.getenv("SMTP_PORT", "587"))
|
||||
smtp_username = os.getenv("SMTP_USERNAME")
|
||||
smtp_password = os.getenv("SMTP_PASSWORD")
|
||||
smtp_from = os.getenv("SMTP_FROM")
|
||||
|
||||
if not all([
|
||||
smtp_host,
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
smtp_from,
|
||||
]):
|
||||
raise RuntimeError("SMTP configuration is incomplete")
|
||||
|
||||
password_reset_url = (
|
||||
f"https://ben.de-roo.org/api/update-password_page?token={token}"
|
||||
)
|
||||
|
||||
message = EmailMessage()
|
||||
|
||||
message["From"] = smtp_from
|
||||
message["To"] = email
|
||||
message["Subject"] = "Reset your password"
|
||||
|
||||
message.set_content(
|
||||
f"""Hello,
|
||||
|
||||
You requested to use this email address for reseting your Calendar password.
|
||||
|
||||
Reset your password using this link:
|
||||
|
||||
{password_reset_url}
|
||||
|
||||
This link expires after 24 hours.
|
||||
|
||||
If you did not request this, you can ignore this email.
|
||||
"""
|
||||
)
|
||||
|
||||
print(f"Sending password reset email to {email}")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as smtp:
|
||||
print("SMTP connected")
|
||||
|
||||
smtp.starttls()
|
||||
print("TLS started")
|
||||
|
||||
smtp.login(
|
||||
smtp_username,
|
||||
smtp_password,
|
||||
)
|
||||
print("SMTP login successful")
|
||||
|
||||
smtp.send_message(message)
|
||||
print("Password reset email sent")
|
||||
|
||||
def create_password_reset(user_id: int):
|
||||
def create_password_reset(
|
||||
user_id: int,
|
||||
):
|
||||
token = secrets.token_urlsafe(32)
|
||||
token_hash = hash_token(token)
|
||||
|
||||
@@ -378,4 +484,4 @@ def create_password_reset(user_id: int):
|
||||
return token
|
||||
|
||||
finally:
|
||||
conn.close()
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user