ja
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+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