diff --git a/.pylintrc b/.pylintrc index a9800d0..70c22cd 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,3 @@ [MESSAGES CONTROL] max-positional-arguments=10 -disable=R0913,R0911,W0718,W0719,R0902,R0904,R0917,R0801 \ No newline at end of file +disable=R0913,R0911,W0718,W0719,R0902,R0904,R0917,R0801,R0914 \ No newline at end of file diff --git a/README.md b/README.md index 803f1b8..94f9656 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ auth = Auth( "username": "your_email@example.com", "password": "your_email_password" }, # Optional: Include if using email verification + mail_subject="Verification Code", # Optional: Custom subject for verification codes + mail_body="Your verification code is: {verifcode}", # Optional: Custom email body for verification codes. The text verifcode in brackets is replaced with the code. Use HTML or plain text. blocking=True/False, # Optional: True to enable user blocking rate_limiting=0, # Optional: Set to 0 to disable rate limiting, or a positive integer to enable rate limiting with that cooldown period in seconds between user requests. penalty=0, # Optional: Set to 0 to disable rate limiting penalty, or a positive integer to set the penalty in seconds for rate limiting. If rate limiting is enabled, this is the penalty in seconds added to the cooldown period for violating the cooldown. diff --git a/src/easy_mongodb_auth_handler/auth.py b/src/easy_mongodb_auth_handler/auth.py index 94b772b..37f8528 100644 --- a/src/easy_mongodb_auth_handler/auth.py +++ b/src/easy_mongodb_auth_handler/auth.py @@ -34,12 +34,21 @@ def __init__(self, mongo_uri, db_name, mail_info=None, db_name (str): Name of the database. mail_info (dict, optional): Email server configuration with keys: 'server', 'port', 'username', 'password'. + mail_subject (str, optional): Custom email subject template. + Use {verifcode} placeholder for verification code. + Defaults to "Verification Code". + mail_body (str, optional): Custom email body template. + Use {verifcode} placeholder for verification code. + Supports both plain text and HTML. + Defaults to "Your verification code is: {verifcode}". blocking (bool): Enable user blocking. rate_limit (int): Rate limit for user actions in seconds. penalty (int): Penalty time in seconds for rate limiting. readable_errors (bool): Use readable error messages. attempts (int): Number of connection attempts. delay (int): Delay between connection attempts in seconds. + timeout (int): Timeout in milliseconds for MongoDB connection. + certs (str): Path to CA bundle for SSL verification. """ self.db = None self.retry_count = 0 @@ -293,7 +302,8 @@ def register_user(self, email, password, custom_data=None, self.blocked.insert_one({"email": email, "blocked": False}) hashed_password = hash_password(password) verification_code = generate_secure_code() - send_verification_email(self.mail_info, email, verification_code) + send_verification_email(self.mail_info, email, verification_code, + self.mail_subject, self.mail_body) self.users.insert_one( { "email": email, @@ -341,7 +351,8 @@ def register_user_no_pass(self, email, custom_data=None, else: self.blocked.insert_one({"email": email, "blocked": False}) verification_code = generate_secure_code() - send_verification_email(self.mail_info, email, verification_code) + send_verification_email(self.mail_info, email, verification_code, + self.mail_subject, self.mail_body) self.users.insert_one( { "email": email, @@ -413,7 +424,8 @@ def authenticate_user(self, email, password, mfa=False, {"email": email}, {"$set": {"verification_code": verification_code}} ) - send_verification_email(self.mail_info, email, verification_code) + send_verification_email(self.mail_info, email, verification_code, + self.mail_subject, self.mail_body) return {"success": True, "message": self.messages["authentication_success"]} return {"success": False, "message": self.messages["invalid_creds"]} except Exception as error: @@ -547,7 +559,8 @@ def generate_code(self, email, ignore_rate_limit=False): return {"success": False, "message": self.messages["rate_limited"]} reset_code = generate_secure_code() self.users.update_one({"email": email}, {"$set": {"verification_code": reset_code}}) - send_verification_email(self.mail_info, email, reset_code) + send_verification_email(self.mail_info, email, reset_code, + self.mail_subject, self.mail_body) return {"success": True, "message": self.messages["verification_code_sent"]} except Exception as error: return {"success": False, "message": str(error)} diff --git a/src/easy_mongodb_auth_handler/package_functions/func.py b/src/easy_mongodb_auth_handler/package_functions/func.py index 83d90a4..6f688da 100644 --- a/src/easy_mongodb_auth_handler/package_functions/func.py +++ b/src/easy_mongodb_auth_handler/package_functions/func.py @@ -6,6 +6,7 @@ import re import smtplib from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart import bcrypt @@ -79,7 +80,9 @@ def validate_email(email): return re.match(email_regex, email) is not None -def send_verification_email(mail_info, recipient_email, verification_code): +def send_verification_email(mail_info, + recipient_email, verification_code, + subject=None, body=None): """ sends a verification email with a specified code to the recipient @@ -87,6 +90,8 @@ def send_verification_email(mail_info, recipient_email, verification_code): mail_info (dict): The server address, port, email address, and password. recipient_email (str): The recipient's email address. verification_code (str): The verification code to send. + subject (str, optional): Custom email subject. Uses default if None. + body (str, optional): Custom email body. Uses default if None. Raises: ValueError: If mail server settings are incomplete. @@ -99,12 +104,44 @@ def send_verification_email(mail_info, recipient_email, verification_code): if not all([mail_server, mail_port, mail_username, mail_password]): raise ValueError("Mail server settings are incomplete or missing.") - subject = "Your Verification Code" - body = f"Your verification code is: {verification_code}" - msg = MIMEText(body) - msg["Subject"] = subject - msg["From"] = mail_username - msg["To"] = recipient_email + # Use default values if custom ones are not provided + if subject is None: + subject = "Verification Code" + if body is None: + body = "Your verification code is: {verifcode}" + + # Replace verification code placeholder in both subject and body + final_subject = subject.replace("{verifcode}", verification_code) + final_body = body.replace("{verifcode}", verification_code) + + # Check if body contains HTML tags to determine content type + is_html = any(tag in final_body.lower() for + tag in ['', '', '

', '
', + '

', '']) + if is_html: + # Create multipart message for HTML content + msg = MIMEMultipart('alternative') + msg["Subject"] = final_subject + msg["From"] = mail_username + msg["To"] = recipient_email + + # Create plain text version by removing HTML tags (simple approach) + plain_text = re.sub(r'<[^>]+>', '', final_body) + plain_text = re.sub(r'\s+', ' ', plain_text).strip() + + # Create the text and HTML parts + text_part = MIMEText(plain_text, 'plain') + html_part = MIMEText(final_body, 'html') + + # Add parts to message + msg.attach(text_part) + msg.attach(html_part) + else: + # Simple text message + msg = MIMEText(final_body) + msg["Subject"] = final_subject + msg["From"] = mail_username + msg["To"] = recipient_email try: with smtplib.SMTP(mail_server, mail_port) as server: