????
Current Path : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/flask_security/ |
Current File : C:/opt/pgsql/pgAdmin 4/python/Lib/site-packages/flask_security/mail_util.py |
""" flask_security.mail_util ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Utility class providing methods for validating, normalizing and sending emails. :copyright: (c) 2020-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. While this default implementation uses Flask-Mailman - we want to make sure that Flask-Mailman isn't REQUIRED (if this implementation isn't used). """ from __future__ import annotations import typing as t import email_validator from flask import current_app from .utils import config_value, get_message if t.TYPE_CHECKING: # pragma: no cover import flask class EmailValidateException(ValueError): """This is raised for any email validation errors. This can be used by custom MailUtil implementations to provide custom error messages. """ def __init__(self, message: str) -> None: self.msg = message class MailUtil: """ Utility class providing methods for validating, normalizing and sending emails. This default class uses the email_validator package to handle validation and normalization, and the flask_mailman package (if initialized) to send emails. To provide your own implementation, pass in the class as ``mail_util_cls`` at init time. Your class will be instantiated once as part of app initialization. .. versionadded:: 4.0.0 """ def __init__(self, app: flask.Flask): """Instantiate class. :param app: The Flask application being initialized. """ pass def send_mail( self, template: str, subject: str, recipient: str, sender: str | tuple, body: str, html: str | None, **kwargs: t.Any, ) -> None: """Send an email via the Flask-Mailman or Flask-Mail or other mail extension. :param template: the Template name. The message has already been rendered however this might be useful to differentiate why the email is being sent. :param subject: Email subject :param recipient: Email recipient :param sender: who to send email as (see :py:data:`SECURITY_EMAIL_SENDER`) :param body: the rendered body (text) :param html: the rendered body (html) :param kwargs: the entire context It is possible that sender is a lazy_string for localization (unlikely but..) so we cast to str() here to force localization. """ if current_app.extensions.get("mailman", None): from flask_mailman import EmailMultiAlternatives, Mail # Flask-Mailman doesn't appear to take a tuple - a bug has been filed # but not sure they will fix it (parts of Flask-Mailman work - but not # the actual email headers). if isinstance(sender, tuple) and len(sender) == 2: # sender = (str(sender[0]), str(sender[1])) sender = f"{str(sender[0])} <{str(sender[1])}>" else: sender = str(sender) mail: Mail = current_app.extensions.get("mailman") with mail.get_connection() as connection: msg = EmailMultiAlternatives( subject, body=body, from_email=sender, to=[recipient], connection=connection, ) if html: msg.attach_alternative(html, "text/html") msg.send() elif current_app.extensions.get("mail", None): # pragma: no cover from flask_mail import Message # In Flask-Mail, sender can be a two element tuple -- (name, address) if isinstance(sender, tuple) and len(sender) == 2: sender = (str(sender[0]), str(sender[1])) else: sender = str(sender) msg = Message(subject, sender=sender, recipients=[recipient]) msg.body = body msg.html = html mail = current_app.extensions.get("mail") mail.send(msg) # type: ignore else: # pragma: no cover raise ValueError("No email extension configured") def normalize(self, email: str) -> str: """ Given an input email - return a normalized version or raise EmailValidateException if field value isn't syntactically valid. This is called by forms that use email as an identity to be looked up. Must be called in app context and uses :py:data:`SECURITY_EMAIL_VALIDATOR_ARGS` config variable to pass any relevant arguments to email_validator.validate_email() method. This defaults to NOT checking for deliverability (i.e. DNS checks). """ validator_args = config_value("EMAIL_VALIDATOR_ARGS") or {} validator_args["check_deliverability"] = False try: valid = email_validator.validate_email(email, **validator_args) return valid.normalized except ValueError: raise EmailValidateException(get_message("INVALID_EMAIL_ADDRESS")[0]) def validate(self, email: str) -> str: """ Validate the given email. If valid, the normalized version is returned. This is used by forms/views that require an email that likely can have an actual email sent to it. Must be called in app context and uses :py:data:`SECURITY_EMAIL_VALIDATOR_ARGS` config variable to pass any relevant arguments to email_validator.validate_email() method. EmailValidationException is thrown on invalid email. """ validator_args = config_value("EMAIL_VALIDATOR_ARGS") or {} try: valid = email_validator.validate_email(email, **validator_args) return valid.normalized except ValueError: raise EmailValidateException(get_message("INVALID_EMAIL_ADDRESS")[0])