Fixed module export + inc'd version
This commit is contained in:
211
src/myauth/emailing/smtp.py
Normal file
211
src/myauth/emailing/smtp.py
Normal file
@@ -0,0 +1,211 @@
|
||||
"""
|
||||
SMTP email service implementation.
|
||||
|
||||
This module provides an SMTP-based implementation of the email service
|
||||
for sending authentication-related emails.
|
||||
"""
|
||||
|
||||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Optional
|
||||
|
||||
from .base import EmailService
|
||||
|
||||
# Default email templates
|
||||
DEFAULT_VERIFICATION_TEMPLATE = """
|
||||
<html>
|
||||
<body>
|
||||
<h2>Email Verification</h2>
|
||||
<p>Please click the link below to verify your email address:</p>
|
||||
<p><a href="{verification_link}">Verify Email</a></p>
|
||||
<p>Or copy and paste this link into your browser:</p>
|
||||
<p>{verification_link}</p>
|
||||
<p>This link will expire in 7 days.</p>
|
||||
<p>If you did not request this verification, please ignore this email.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
DEFAULT_PASSWORD_RESET_TEMPLATE = """
|
||||
<html>
|
||||
<body>
|
||||
<h2>Password Reset</h2>
|
||||
<p>You have requested to reset your password. Please click the link below:</p>
|
||||
<p><a href="{reset_link}">Reset Password</a></p>
|
||||
<p>Or copy and paste this link into your browser:</p>
|
||||
<p>{reset_link}</p>
|
||||
<p>This link will expire in 15 minutes.</p>
|
||||
<p>If you did not request a password reset, please ignore this email.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
class SMTPEmailService(EmailService):
|
||||
"""
|
||||
SMTP implementation of EmailService.
|
||||
|
||||
This implementation uses standard SMTP protocol to send emails.
|
||||
It supports both TLS and SSL connections and allows custom HTML
|
||||
templates for email content.
|
||||
|
||||
Attributes:
|
||||
host: SMTP server hostname.
|
||||
port: SMTP server port (587 for TLS, 465 for SSL).
|
||||
username: SMTP authentication username.
|
||||
password: SMTP authentication password.
|
||||
use_tls: Whether to use TLS (default: True).
|
||||
from_email: Email address to use as sender.
|
||||
from_name: Display name for sender (optional).
|
||||
base_url: Base URL for constructing verification/reset links.
|
||||
verification_template: HTML template for verification emails.
|
||||
password_reset_template: HTML template for password reset emails.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
host: str,
|
||||
port: int,
|
||||
username: str,
|
||||
password: str,
|
||||
from_email: str,
|
||||
base_url: str,
|
||||
use_tls: bool = True,
|
||||
from_name: Optional[str] = None,
|
||||
verification_template: Optional[str] = None,
|
||||
password_reset_template: Optional[str] = None
|
||||
):
|
||||
"""
|
||||
Initialize SMTP email service.
|
||||
|
||||
Args:
|
||||
host: SMTP server hostname (e.g., "smtp.gmail.com").
|
||||
port: SMTP server port (587 for TLS, 465 for SSL).
|
||||
username: SMTP authentication username.
|
||||
password: SMTP authentication password.
|
||||
from_email: Email address to use as sender.
|
||||
base_url: Base URL for your application (e.g., "https://myapp.com").
|
||||
use_tls: Whether to use TLS encryption (default: True).
|
||||
from_name: Display name for sender (default: None).
|
||||
verification_template: Custom HTML template for verification emails
|
||||
(must include {verification_link} placeholder).
|
||||
password_reset_template: Custom HTML template for reset emails
|
||||
(must include {reset_link} placeholder).
|
||||
|
||||
Example:
|
||||
>>> email_service = SMTPEmailService(
|
||||
... host="smtp.gmail.com",
|
||||
... port=587,
|
||||
... username="noreply@myapp.com",
|
||||
... password="app_password",
|
||||
... from_email="noreply@myapp.com",
|
||||
... base_url="https://myapp.com",
|
||||
... from_name="My Application"
|
||||
... )
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.use_tls = use_tls
|
||||
self.from_email = from_email
|
||||
self.from_name = from_name
|
||||
self.base_url = base_url.rstrip('/')
|
||||
|
||||
# Use custom templates or defaults
|
||||
self.verification_template = verification_template or DEFAULT_VERIFICATION_TEMPLATE
|
||||
self.password_reset_template = password_reset_template or DEFAULT_PASSWORD_RESET_TEMPLATE
|
||||
|
||||
def _send_email(self, to_email: str, subject: str, html_content: str) -> None:
|
||||
"""
|
||||
Send an email via SMTP.
|
||||
|
||||
Internal method that handles the actual SMTP connection and sending.
|
||||
|
||||
Args:
|
||||
to_email: Recipient email address.
|
||||
subject: Email subject line.
|
||||
html_content: HTML content of the email.
|
||||
|
||||
Raises:
|
||||
smtplib.SMTPException: If email sending fails.
|
||||
"""
|
||||
# Create message
|
||||
message = MIMEMultipart("alternative")
|
||||
message["Subject"] = subject
|
||||
message["From"] = f"{self.from_name} <{self.from_email}>" if self.from_name else self.from_email
|
||||
message["To"] = to_email
|
||||
|
||||
# Attach HTML content
|
||||
html_part = MIMEText(html_content, "html")
|
||||
message.attach(html_part)
|
||||
|
||||
# Send email
|
||||
if self.use_tls:
|
||||
with smtplib.SMTP(self.host, self.port) as server:
|
||||
server.starttls()
|
||||
server.login(self.username, self.password)
|
||||
server.send_message(message)
|
||||
else:
|
||||
with smtplib.SMTP_SSL(self.host, self.port) as server:
|
||||
server.login(self.username, self.password)
|
||||
server.send_message(message)
|
||||
|
||||
def send_verification_email(self, email: str, token: str) -> None:
|
||||
"""
|
||||
Send an email verification link to the user.
|
||||
|
||||
Constructs a verification link using the base_url and token,
|
||||
then sends an email using the configured verification template.
|
||||
|
||||
Args:
|
||||
email: The recipient's email address.
|
||||
token: The verification token (JWT).
|
||||
|
||||
Raises:
|
||||
smtplib.SMTPException: If email sending fails.
|
||||
|
||||
Example:
|
||||
>>> email_service.send_verification_email(
|
||||
... "user@example.com",
|
||||
... "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
... )
|
||||
"""
|
||||
verification_link = f"{self.base_url}/auth/verify-email?token={token}"
|
||||
html_content = self.verification_template.format(verification_link=verification_link)
|
||||
|
||||
self._send_email(
|
||||
to_email=email,
|
||||
subject="Verify Your Email Address",
|
||||
html_content=html_content
|
||||
)
|
||||
|
||||
def send_password_reset_email(self, email: str, token: str) -> None:
|
||||
"""
|
||||
Send a password reset link to the user.
|
||||
|
||||
Constructs a password reset link using the base_url and token,
|
||||
then sends an email using the configured password reset template.
|
||||
|
||||
Args:
|
||||
email: The recipient's email address.
|
||||
token: The password reset token.
|
||||
|
||||
Raises:
|
||||
smtplib.SMTPException: If email sending fails.
|
||||
|
||||
Example:
|
||||
>>> email_service.send_password_reset_email(
|
||||
... "user@example.com",
|
||||
... "a1b2c3d4e5f6..."
|
||||
... )
|
||||
"""
|
||||
reset_link = f"{self.base_url}/reset-password?token={token}"
|
||||
html_content = self.password_reset_template.format(reset_link=reset_link)
|
||||
|
||||
self._send_email(
|
||||
to_email=email,
|
||||
subject="Reset Your Password",
|
||||
html_content=html_content
|
||||
)
|
||||
Reference in New Issue
Block a user