Source code for kotti.message

import hashlib
import time
from typing import Dict
from typing import List
from typing import Optional
from urllib.parse import urlencode

from html2text import HTML2Text
from pyramid.renderers import render
from pyramid_mailer.mailer import Mailer
from pyramid_mailer.message import Message

from kotti import get_settings
from kotti.request import Request
from kotti.security import Principal

_inject_mailer = []


def get_mailer():
    # Consider that we may have persistent settings
    if _inject_mailer:
        return _inject_mailer[0]
    return Mailer.from_settings(get_settings())  # pragma: no cover


def make_token(user: Principal, seconds: Optional[float] = None) -> str:
    secret = get_settings()["kotti.secret2"]
    if seconds is None:
        seconds = time.time()
    token = f"{user.name}:{secret}:{seconds}"
    return "{}:{}".format(hashlib.sha224(token.encode("utf8")).hexdigest(), seconds)


[docs]def validate_token(user: Principal, token: str, valid_hrs: int = 24) -> bool: """ >>> from kotti.testing import setUp, tearDown >>> ignore = setUp() >>> class User(object): ... pass >>> daniel = User() >>> daniel.name = u'daniel' >>> alice = User() >>> alice.name = u'alice' >>> token = make_token(daniel) >>> validate_token(daniel, token) True >>> validate_token(alice, token) False >>> validate_token(daniel, 'foo') False >>> token = make_token(daniel, seconds=time.time() - 100000) >>> validate_token(daniel, token) False >>> validate_token(daniel, token, valid_hrs=48) True >>> tearDown() """ try: seconds = float(token.split(":")[1]) except (IndexError, ValueError): return False if ( token == make_token(user, seconds) and time.time() - seconds < 60 * 60 * valid_hrs ): return True return False
[docs]def send_email( request: Request, recipients: List[str], template_name: str, template_vars: Optional[Dict[str, str]] = None, ) -> None: """ General email sender. :param request: current request. :type request: :class:`kotti.request.Request` :param recipients: list of email addresses. Each email should be a string like: u'"John Doe" <joedoe@foo.com>'. :type recipients: list :param template_name: asset specification (e.g. 'mypackage:templates/email.pt') :type template_name: string :param template_vars: set of variables present on template. :type template_vars: dict """ if template_vars is None: template_vars = {} text = render(template_name, template_vars, request) subject, htmlbody = text.strip().split("\n", 1) subject = subject.replace("Subject:", "", 1).strip() html2text = HTML2Text() html2text.body_width = 0 textbody = html2text.handle(htmlbody).strip() message = Message( recipients=recipients, subject=subject, body=textbody, html=htmlbody ) mailer = get_mailer() mailer.send(message)
def email_set_password( user: Principal, request: Request, template_name: Optional[str] = "kotti:templates/email-set-password.pt", # noqa add_query: Optional[Dict[str, str]] = None, ) -> None: site_title = get_settings()["kotti.site_title"] token = make_token(user) user.confirm_token = token set_password_query = {"token": token, "email": user.email} if add_query: set_password_query.update(add_query) url = "{}/@@set-password?{}".format( request.application_url, urlencode(set_password_query) ) variables = dict(user_title=user.title, site_title=site_title, url=url) recipients = [f'"{user.title}" <{user.email}>'] # XXX naive? send_email(request, recipients, template_name, variables)