mirror of
https://github.com/quay/quay.git
synced 2025-04-18 10:44:06 +03:00
Currently the prometheus and GC workers are not running correctly. Reverting the following commits: -4e1a985e70
-dac183a1ef
-68a0d9eaf0
-af1aacea08
-f334b80098
262 lines
7.8 KiB
Python
262 lines
7.8 KiB
Python
import os
|
|
import json
|
|
import logging
|
|
import features
|
|
from unittest import mock
|
|
|
|
from flask_mail import Message
|
|
|
|
import features
|
|
|
|
from _init import ROOT_DIR
|
|
from app import mail, app, get_app_url
|
|
from util.jinjautil import get_template_env
|
|
from util.html import html2text
|
|
from util.fips import login_fips_safe
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
template_env = get_template_env(os.path.join(ROOT_DIR, "emails"))
|
|
|
|
|
|
class CannotSendEmailException(Exception):
|
|
pass
|
|
|
|
|
|
class GmailAction(object):
|
|
"""
|
|
Represents an action that can be taken in Gmail in response to the email.
|
|
"""
|
|
|
|
def __init__(self, metadata):
|
|
self.metadata = metadata
|
|
|
|
@staticmethod
|
|
def confirm(name, url, description):
|
|
return GmailAction(
|
|
{
|
|
"@context": "http://schema.org",
|
|
"@type": "EmailMessage",
|
|
"action": {
|
|
"@type": "ConfirmAction",
|
|
"name": name,
|
|
"handler": {"@type": "HttpActionHandler", "url": get_app_url() + "/" + url},
|
|
},
|
|
"description": description,
|
|
}
|
|
)
|
|
|
|
@staticmethod
|
|
def view(name, url, description):
|
|
return GmailAction(
|
|
{
|
|
"@context": "http://schema.org",
|
|
"@type": "EmailMessage",
|
|
"action": {"@type": "ViewAction", "name": name, "url": get_app_url() + "/" + url},
|
|
"description": description,
|
|
}
|
|
)
|
|
|
|
|
|
def send_email(recipient, subject, template_file, parameters, action=None):
|
|
app_title = app.config["REGISTRY_TITLE_SHORT"]
|
|
app_url = get_app_url()
|
|
html, plain = render_email(
|
|
app_title, app_url, recipient, subject, template_file, parameters, action=None
|
|
)
|
|
msg = Message("[%s] %s" % (app_title, subject), recipients=[recipient])
|
|
msg.html = html
|
|
msg.body = plain
|
|
|
|
try:
|
|
if features.FIPS:
|
|
assert app.config[
|
|
"MAIL_USE_TLS"
|
|
], "MAIL_USE_TLS must be enabled to use SMTP in FIPS mode."
|
|
with mock.patch("smtplib.SMTP.login", login_fips_safe):
|
|
mail.send(msg)
|
|
else:
|
|
mail.send(msg)
|
|
|
|
if app.config["TESTING"]:
|
|
logger.debug("Quay is configured for testing. Email not sent: '%s'", msg.subject)
|
|
else:
|
|
logger.debug("Sent email: '%s'", msg.subject)
|
|
except Exception as ex:
|
|
logger.exception("Error while trying to send email to %s", recipient)
|
|
raise CannotSendEmailException(str(ex))
|
|
|
|
|
|
def render_email(app_title, app_url, recipient, subject, template_file, parameters, action=None):
|
|
def app_link_handler(url=None):
|
|
return app_url + "/" + url if url else app_url
|
|
|
|
app_logo = app.config.get("ENTERPRISE_LOGO_URL", "https://quay.io/static/img/quay-logo.png")
|
|
|
|
parameters.update(
|
|
{
|
|
"subject": subject,
|
|
"app_logo": app_logo,
|
|
"app_url": app_url,
|
|
"app_title": app_title,
|
|
"hosted": features.BILLING,
|
|
"app_link": app_link_handler,
|
|
"action_metadata": json.dumps(action.metadata) if action else None,
|
|
"with_base_template": True,
|
|
}
|
|
)
|
|
|
|
rendered_html = template_env.get_template(template_file + ".html").render(parameters)
|
|
|
|
parameters.update(
|
|
{
|
|
"with_base_template": False,
|
|
}
|
|
)
|
|
|
|
rendered_for_plain = template_env.get_template(template_file + ".html").render(parameters)
|
|
return rendered_html, html2text(rendered_for_plain)
|
|
|
|
|
|
def send_password_changed(username, email):
|
|
send_email(email, "Account password changed", "passwordchanged", {"username": username})
|
|
|
|
|
|
def send_email_changed(username, old_email, new_email):
|
|
send_email(
|
|
old_email,
|
|
"Account e-mail address changed",
|
|
"emailchanged",
|
|
{"username": username, "new_email": new_email},
|
|
)
|
|
|
|
|
|
def send_change_email(username, email, token):
|
|
send_email(
|
|
email,
|
|
"E-mail address change requested",
|
|
"changeemail",
|
|
{"username": username, "token": token},
|
|
)
|
|
|
|
|
|
def send_confirmation_email(username, email, token):
|
|
action = GmailAction.confirm(
|
|
"Confirm E-mail", "confirm?code=" + token, "Verification of e-mail address"
|
|
)
|
|
|
|
send_email(
|
|
email,
|
|
"Please confirm your e-mail address",
|
|
"confirmemail",
|
|
{"username": username, "token": token},
|
|
action=action,
|
|
)
|
|
|
|
|
|
def send_repo_authorization_email(namespace, repository, email, token):
|
|
action = GmailAction.confirm(
|
|
"Verify E-mail", "authrepoemail?code=" + token, "Verification of e-mail address"
|
|
)
|
|
|
|
subject = "Please verify your e-mail address for repository %s/%s" % (namespace, repository)
|
|
send_email(
|
|
email,
|
|
subject,
|
|
"repoauthorizeemail",
|
|
{"namespace": namespace, "repository": repository, "token": token},
|
|
action=action,
|
|
)
|
|
|
|
|
|
def send_org_recovery_email(org, admin_users):
|
|
subject = "Organization %s recovery" % (org.username)
|
|
send_email(
|
|
org.email,
|
|
subject,
|
|
"orgrecovery",
|
|
{
|
|
"organization": org.username,
|
|
"admin_usernames": [user.username for user in admin_users],
|
|
},
|
|
)
|
|
|
|
|
|
def send_recovery_email(email, token):
|
|
action = GmailAction.view("Recover Account", "recovery?code=" + token, "Recovery of an account")
|
|
|
|
subject = "Account recovery"
|
|
send_email(email, subject, "recovery", {"email": email, "token": token}, action=action)
|
|
|
|
|
|
def send_payment_failed(email, username):
|
|
send_email(email, "Subscription Payment Failure", "paymentfailure", {"username": username})
|
|
|
|
|
|
def send_org_invite_email(member_name, member_email, orgname, team, adder, code):
|
|
action = GmailAction.view(
|
|
"Join %s" % team, "confirminvite?code=" + code, "Invitation to join a team"
|
|
)
|
|
|
|
send_email(
|
|
member_email,
|
|
"Invitation to join team",
|
|
"teaminvite",
|
|
{"inviter": adder, "token": code, "organization": orgname, "teamname": team},
|
|
action=action,
|
|
)
|
|
|
|
|
|
def send_invoice_email(email, contents):
|
|
# Note: This completely generates the contents of the email, so we don't use the
|
|
# normal template here.
|
|
msg = Message("Quay payment received - Thank you!", recipients=[email])
|
|
msg.html = contents
|
|
if features.FIPS:
|
|
assert app.config["MAIL_USE_TLS"], "MAIL_USE_TLS must be enabled to use SMTP in FIPS mode."
|
|
with mock.patch("smtplib.SMTP.login", login_fips_safe):
|
|
mail.send(msg)
|
|
else:
|
|
mail.send(msg)
|
|
|
|
|
|
def send_logs_exported_email(
|
|
email, export_id, status, exported_data_url=None, exported_data_expiration=None
|
|
):
|
|
send_email(
|
|
email,
|
|
"Export Action Logs Complete",
|
|
"logsexported",
|
|
{
|
|
"status": status,
|
|
"export_id": export_id,
|
|
"exported_data_url": exported_data_url,
|
|
"exported_data_expiration": exported_data_expiration,
|
|
},
|
|
)
|
|
|
|
|
|
# INTERNAL EMAILS BELOW
|
|
|
|
|
|
def send_subscription_change(change_description, customer_id, customer_email, quay_username):
|
|
SUBSCRIPTION_CHANGE_TITLE = "Subscription Change - {0} {1}"
|
|
SUBSCRIPTION_CHANGE = """
|
|
Change: {0}<br>
|
|
Customer id: <a href="https://manage.stripe.com/customers/{1}">{1}</a><br>
|
|
Customer email: <a href="mailto:{2}">{2}</a><br>
|
|
Quay user or org name: {3}<br>
|
|
"""
|
|
|
|
title = SUBSCRIPTION_CHANGE_TITLE.format(quay_username, change_description)
|
|
msg = Message(title, recipients=["stripe@quay.io"])
|
|
msg.html = SUBSCRIPTION_CHANGE.format(
|
|
change_description, customer_id, customer_email, quay_username
|
|
)
|
|
if features.FIPS:
|
|
assert app.config["MAIL_USE_TLS"], "MAIL_USE_TLS must be enabled to use SMTP in FIPS mode."
|
|
with mock.patch("smtplib.SMTP.login", login_fips_safe):
|
|
mail.send(msg)
|
|
else:
|
|
mail.send(msg)
|