2019-02-09 19:42:36 +04:00
|
|
|
import logging
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from pathlib import Path
|
2024-02-25 19:39:33 +01:00
|
|
|
from typing import Any
|
2019-02-09 19:42:36 +04:00
|
|
|
|
|
|
|
import emails
|
|
|
|
from emails.template import JinjaTemplate
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
from jose import jwt
|
2019-02-09 19:42:36 +04:00
|
|
|
|
2020-04-16 23:56:10 -06:00
|
|
|
from app.core.config import settings
|
2019-02-09 19:42:36 +04:00
|
|
|
|
|
|
|
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
def send_email(
|
|
|
|
email_to: str,
|
|
|
|
subject_template: str = "",
|
|
|
|
html_template: str = "",
|
2024-02-25 19:39:33 +01:00
|
|
|
environment: dict[str, Any] | None = None,
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
) -> None:
|
2024-02-25 19:39:33 +01:00
|
|
|
current_environment = environment or {}
|
2020-04-16 23:56:10 -06:00
|
|
|
assert settings.EMAILS_ENABLED, "no provided configuration for email variables"
|
2019-02-09 19:42:36 +04:00
|
|
|
message = emails.Message(
|
|
|
|
subject=JinjaTemplate(subject_template),
|
|
|
|
html=JinjaTemplate(html_template),
|
2020-04-16 23:56:10 -06:00
|
|
|
mail_from=(settings.EMAILS_FROM_NAME, settings.EMAILS_FROM_EMAIL),
|
2019-02-09 19:42:36 +04:00
|
|
|
)
|
2020-04-16 23:56:10 -06:00
|
|
|
smtp_options = {"host": settings.SMTP_HOST, "port": settings.SMTP_PORT}
|
|
|
|
if settings.SMTP_TLS:
|
2019-02-09 19:42:36 +04:00
|
|
|
smtp_options["tls"] = True
|
2020-04-16 23:56:10 -06:00
|
|
|
if settings.SMTP_USER:
|
|
|
|
smtp_options["user"] = settings.SMTP_USER
|
|
|
|
if settings.SMTP_PASSWORD:
|
|
|
|
smtp_options["password"] = settings.SMTP_PASSWORD
|
2024-02-25 19:39:33 +01:00
|
|
|
response = message.send(to=email_to, render=current_environment, smtp=smtp_options)
|
2019-02-09 19:42:36 +04:00
|
|
|
logging.info(f"send email result: {response}")
|
|
|
|
|
|
|
|
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
def send_test_email(email_to: str) -> None:
|
2020-04-16 23:56:10 -06:00
|
|
|
project_name = settings.PROJECT_NAME
|
2019-02-23 18:44:29 +04:00
|
|
|
subject = f"{project_name} - Test email"
|
2020-04-16 23:56:10 -06:00
|
|
|
with open(Path(settings.EMAIL_TEMPLATES_DIR) / "test_email.html") as f:
|
2019-02-09 19:42:36 +04:00
|
|
|
template_str = f.read()
|
|
|
|
send_email(
|
|
|
|
email_to=email_to,
|
|
|
|
subject_template=subject,
|
|
|
|
html_template=template_str,
|
2024-02-29 17:30:58 -05:00
|
|
|
environment={"project_name": settings.PROJECT_NAME, "email": email_to},
|
2019-02-09 19:42:36 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
def send_reset_password_email(email_to: str, email: str, token: str) -> None:
|
2020-04-16 23:56:10 -06:00
|
|
|
project_name = settings.PROJECT_NAME
|
2019-02-23 18:44:29 +04:00
|
|
|
subject = f"{project_name} - Password recovery for user {email}"
|
2020-04-16 23:56:10 -06:00
|
|
|
with open(Path(settings.EMAIL_TEMPLATES_DIR) / "reset_password.html") as f:
|
2019-02-09 19:42:36 +04:00
|
|
|
template_str = f.read()
|
2020-04-16 23:56:10 -06:00
|
|
|
server_host = settings.SERVER_HOST
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
link = f"{server_host}/reset-password?token={token}"
|
2019-02-09 19:42:36 +04:00
|
|
|
send_email(
|
|
|
|
email_to=email_to,
|
|
|
|
subject_template=subject,
|
|
|
|
html_template=template_str,
|
2024-02-29 17:30:58 -05:00
|
|
|
environment={
|
2020-04-16 23:56:10 -06:00
|
|
|
"project_name": settings.PROJECT_NAME,
|
2019-02-23 18:44:29 +04:00
|
|
|
"username": email,
|
2019-02-09 19:42:36 +04:00
|
|
|
"email": email_to,
|
2020-04-16 23:56:10 -06:00
|
|
|
"valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS,
|
2019-02-09 19:42:36 +04:00
|
|
|
"link": link,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
def send_new_account_email(email_to: str, username: str, password: str) -> None:
|
2020-04-16 23:56:10 -06:00
|
|
|
project_name = settings.PROJECT_NAME
|
2019-03-11 13:36:42 +04:00
|
|
|
subject = f"{project_name} - New account for user {username}"
|
2020-04-16 23:56:10 -06:00
|
|
|
with open(Path(settings.EMAIL_TEMPLATES_DIR) / "new_account.html") as f:
|
2019-02-09 19:42:36 +04:00
|
|
|
template_str = f.read()
|
2020-04-16 23:56:10 -06:00
|
|
|
link = settings.SERVER_HOST
|
2019-02-09 19:42:36 +04:00
|
|
|
send_email(
|
|
|
|
email_to=email_to,
|
|
|
|
subject_template=subject,
|
|
|
|
html_template=template_str,
|
2024-02-29 17:30:58 -05:00
|
|
|
environment={
|
2020-04-16 23:56:10 -06:00
|
|
|
"project_name": settings.PROJECT_NAME,
|
2019-02-09 19:42:36 +04:00
|
|
|
"username": username,
|
|
|
|
"password": password,
|
|
|
|
"email": email_to,
|
|
|
|
"link": link,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
def generate_password_reset_token(email: str) -> str:
|
2020-04-16 23:56:10 -06:00
|
|
|
delta = timedelta(hours=settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS)
|
2019-02-09 19:42:36 +04:00
|
|
|
now = datetime.utcnow()
|
|
|
|
expires = now + delta
|
|
|
|
exp = expires.timestamp()
|
|
|
|
encoded_jwt = jwt.encode(
|
2024-02-25 19:39:33 +01:00
|
|
|
{"exp": exp, "nbf": now, "sub": email},
|
|
|
|
settings.SECRET_KEY,
|
|
|
|
algorithm="HS256",
|
2019-02-09 19:42:36 +04:00
|
|
|
)
|
|
|
|
return encoded_jwt
|
|
|
|
|
|
|
|
|
2024-02-25 19:39:33 +01:00
|
|
|
def verify_password_reset_token(token: str) -> str | None:
|
2019-02-09 19:42:36 +04:00
|
|
|
try:
|
2020-04-16 23:56:10 -06:00
|
|
|
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
|
2024-02-29 17:30:58 -05:00
|
|
|
return decoded_token["sub"]
|
:recycle: Refactor backend, settings, DB sessions, types, configs, plugins (#158)
* :recycle: Refactor backend, update DB session handling
* :sparkles: Add mypy config and plugins
* :heavy_plus_sign: Use Python-jose instead of PyJWT
as it has some extra functionalities and features
* :sparkles: Add/update scripts for test, lint, format
* :wrench: Update lint and format configs
* :art: Update import format, comments, and types
* :art: Add types to config
* :sparkles: Add types for all the code, and small fixes
* :art: Use global imports to simplify exploring with Jupyter
* :recycle: Import schemas and models, instead of each class
* :truck: Rename db_session to db for simplicity
* :pushpin: Update dependencies installation for testing
2020-04-20 19:03:13 +02:00
|
|
|
except jwt.JWTError:
|
2019-02-23 18:44:29 +04:00
|
|
|
return None
|