Black lint all the things
This commit is contained in:
@ -14,7 +14,14 @@ from lemur.notifications.messaging import send_expiration_notifications
|
||||
manager = Manager(usage="Handles notification related tasks.")
|
||||
|
||||
|
||||
@manager.option('-e', '--exclude', dest='exclude', action='append', default=[], help='Common name matching of certificates that should be excluded from notification')
|
||||
@manager.option(
|
||||
"-e",
|
||||
"--exclude",
|
||||
dest="exclude",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Common name matching of certificates that should be excluded from notification",
|
||||
)
|
||||
def expirations(exclude):
|
||||
"""
|
||||
Runs Lemur's notification engine, that looks for expired certificates and sends
|
||||
@ -33,12 +40,13 @@ def expirations(exclude):
|
||||
success, failed = send_expiration_notifications(exclude)
|
||||
print(
|
||||
"Finished notifying subscribers about expiring certificates! Sent: {success} Failed: {failed}".format(
|
||||
success=success,
|
||||
failed=failed
|
||||
success=success, failed=failed
|
||||
)
|
||||
)
|
||||
status = SUCCESS_METRIC_STATUS
|
||||
except Exception as e:
|
||||
sentry.captureException()
|
||||
|
||||
metrics.send('expiration_notification_job', 'counter', 1, metric_tags={'status': status})
|
||||
metrics.send(
|
||||
"expiration_notification_job", "counter", 1, metric_tags={"status": status}
|
||||
)
|
||||
|
@ -36,15 +36,17 @@ def get_certificates(exclude=None):
|
||||
now = arrow.utcnow()
|
||||
max = now + timedelta(days=90)
|
||||
|
||||
q = database.db.session.query(Certificate) \
|
||||
.filter(Certificate.not_after <= max) \
|
||||
.filter(Certificate.notify == True) \
|
||||
.filter(Certificate.expired == False) # noqa
|
||||
q = (
|
||||
database.db.session.query(Certificate)
|
||||
.filter(Certificate.not_after <= max)
|
||||
.filter(Certificate.notify == True)
|
||||
.filter(Certificate.expired == False)
|
||||
) # noqa
|
||||
|
||||
exclude_conditions = []
|
||||
if exclude:
|
||||
for e in exclude:
|
||||
exclude_conditions.append(~Certificate.name.ilike('%{}%'.format(e)))
|
||||
exclude_conditions.append(~Certificate.name.ilike("%{}%".format(e)))
|
||||
|
||||
q = q.filter(and_(*exclude_conditions))
|
||||
|
||||
@ -101,7 +103,12 @@ def send_notification(event_type, data, targets, notification):
|
||||
except Exception as e:
|
||||
sentry.captureException()
|
||||
|
||||
metrics.send('notification', 'counter', 1, metric_tags={'status': status, 'event_type': event_type})
|
||||
metrics.send(
|
||||
"notification",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"status": status, "event_type": event_type},
|
||||
)
|
||||
|
||||
if status == SUCCESS_METRIC_STATUS:
|
||||
return True
|
||||
@ -115,7 +122,7 @@ def send_expiration_notifications(exclude):
|
||||
success = failure = 0
|
||||
|
||||
# security team gets all
|
||||
security_email = current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL')
|
||||
security_email = current_app.config.get("LEMUR_SECURITY_TEAM_EMAIL")
|
||||
|
||||
security_data = []
|
||||
for owner, notification_group in get_eligible_certificates(exclude=exclude).items():
|
||||
@ -127,26 +134,43 @@ def send_expiration_notifications(exclude):
|
||||
|
||||
for data in certificates:
|
||||
n, certificate = data
|
||||
cert_data = certificate_notification_output_schema.dump(certificate).data
|
||||
cert_data = certificate_notification_output_schema.dump(
|
||||
certificate
|
||||
).data
|
||||
notification_data.append(cert_data)
|
||||
security_data.append(cert_data)
|
||||
|
||||
notification_recipient = get_plugin_option('recipients', notification.options)
|
||||
notification_recipient = get_plugin_option(
|
||||
"recipients", notification.options
|
||||
)
|
||||
if notification_recipient:
|
||||
notification_recipient = notification_recipient.split(",")
|
||||
|
||||
if send_notification('expiration', notification_data, [owner], notification):
|
||||
if send_notification(
|
||||
"expiration", notification_data, [owner], notification
|
||||
):
|
||||
success += 1
|
||||
else:
|
||||
failure += 1
|
||||
|
||||
if notification_recipient and owner != notification_recipient and security_email != notification_recipient:
|
||||
if send_notification('expiration', notification_data, notification_recipient, notification):
|
||||
if (
|
||||
notification_recipient
|
||||
and owner != notification_recipient
|
||||
and security_email != notification_recipient
|
||||
):
|
||||
if send_notification(
|
||||
"expiration",
|
||||
notification_data,
|
||||
notification_recipient,
|
||||
notification,
|
||||
):
|
||||
success += 1
|
||||
else:
|
||||
failure += 1
|
||||
|
||||
if send_notification('expiration', security_data, security_email, notification):
|
||||
if send_notification(
|
||||
"expiration", security_data, security_email, notification
|
||||
):
|
||||
success += 1
|
||||
else:
|
||||
failure += 1
|
||||
@ -165,24 +189,35 @@ def send_rotation_notification(certificate, notification_plugin=None):
|
||||
"""
|
||||
status = FAILURE_METRIC_STATUS
|
||||
if not notification_plugin:
|
||||
notification_plugin = plugins.get(current_app.config.get('LEMUR_DEFAULT_NOTIFICATION_PLUGIN'))
|
||||
notification_plugin = plugins.get(
|
||||
current_app.config.get("LEMUR_DEFAULT_NOTIFICATION_PLUGIN")
|
||||
)
|
||||
|
||||
data = certificate_notification_output_schema.dump(certificate).data
|
||||
|
||||
try:
|
||||
notification_plugin.send('rotation', data, [data['owner']])
|
||||
notification_plugin.send("rotation", data, [data["owner"]])
|
||||
status = SUCCESS_METRIC_STATUS
|
||||
except Exception as e:
|
||||
current_app.logger.error('Unable to send notification to {}.'.format(data['owner']), exc_info=True)
|
||||
current_app.logger.error(
|
||||
"Unable to send notification to {}.".format(data["owner"]), exc_info=True
|
||||
)
|
||||
sentry.captureException()
|
||||
|
||||
metrics.send('notification', 'counter', 1, metric_tags={'status': status, 'event_type': 'rotation'})
|
||||
metrics.send(
|
||||
"notification",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"status": status, "event_type": "rotation"},
|
||||
)
|
||||
|
||||
if status == SUCCESS_METRIC_STATUS:
|
||||
return True
|
||||
|
||||
|
||||
def send_pending_failure_notification(pending_cert, notify_owner=True, notify_security=True, notification_plugin=None):
|
||||
def send_pending_failure_notification(
|
||||
pending_cert, notify_owner=True, notify_security=True, notification_plugin=None
|
||||
):
|
||||
"""
|
||||
Sends a report to certificate owners when their pending certificate failed to be created.
|
||||
|
||||
@ -194,32 +229,47 @@ def send_pending_failure_notification(pending_cert, notify_owner=True, notify_se
|
||||
|
||||
if not notification_plugin:
|
||||
notification_plugin = plugins.get(
|
||||
current_app.config.get('LEMUR_DEFAULT_NOTIFICATION_PLUGIN', 'email-notification')
|
||||
current_app.config.get(
|
||||
"LEMUR_DEFAULT_NOTIFICATION_PLUGIN", "email-notification"
|
||||
)
|
||||
)
|
||||
|
||||
data = pending_certificate_output_schema.dump(pending_cert).data
|
||||
data["security_email"] = current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL')
|
||||
data["security_email"] = current_app.config.get("LEMUR_SECURITY_TEAM_EMAIL")
|
||||
|
||||
if notify_owner:
|
||||
try:
|
||||
notification_plugin.send('failed', data, [data['owner']], pending_cert)
|
||||
notification_plugin.send("failed", data, [data["owner"]], pending_cert)
|
||||
status = SUCCESS_METRIC_STATUS
|
||||
except Exception as e:
|
||||
current_app.logger.error('Unable to send pending failure notification to {}.'.format(data['owner']),
|
||||
exc_info=True)
|
||||
current_app.logger.error(
|
||||
"Unable to send pending failure notification to {}.".format(
|
||||
data["owner"]
|
||||
),
|
||||
exc_info=True,
|
||||
)
|
||||
sentry.captureException()
|
||||
|
||||
if notify_security:
|
||||
try:
|
||||
notification_plugin.send('failed', data, data["security_email"], pending_cert)
|
||||
notification_plugin.send(
|
||||
"failed", data, data["security_email"], pending_cert
|
||||
)
|
||||
status = SUCCESS_METRIC_STATUS
|
||||
except Exception as e:
|
||||
current_app.logger.error('Unable to send pending failure notification to '
|
||||
'{}.'.format(data['security_email']),
|
||||
exc_info=True)
|
||||
current_app.logger.error(
|
||||
"Unable to send pending failure notification to "
|
||||
"{}.".format(data["security_email"]),
|
||||
exc_info=True,
|
||||
)
|
||||
sentry.captureException()
|
||||
|
||||
metrics.send('notification', 'counter', 1, metric_tags={'status': status, 'event_type': 'rotation'})
|
||||
metrics.send(
|
||||
"notification",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"status": status, "event_type": "rotation"},
|
||||
)
|
||||
|
||||
if status == SUCCESS_METRIC_STATUS:
|
||||
return True
|
||||
@ -242,20 +292,22 @@ def needs_notification(certificate):
|
||||
if not notification.active or not notification.options:
|
||||
return
|
||||
|
||||
interval = get_plugin_option('interval', notification.options)
|
||||
unit = get_plugin_option('unit', notification.options)
|
||||
interval = get_plugin_option("interval", notification.options)
|
||||
unit = get_plugin_option("unit", notification.options)
|
||||
|
||||
if unit == 'weeks':
|
||||
if unit == "weeks":
|
||||
interval *= 7
|
||||
|
||||
elif unit == 'months':
|
||||
elif unit == "months":
|
||||
interval *= 30
|
||||
|
||||
elif unit == 'days': # it's nice to be explicit about the base unit
|
||||
elif unit == "days": # it's nice to be explicit about the base unit
|
||||
pass
|
||||
|
||||
else:
|
||||
raise Exception("Invalid base unit for expiration interval: {0}".format(unit))
|
||||
raise Exception(
|
||||
"Invalid base unit for expiration interval: {0}".format(unit)
|
||||
)
|
||||
|
||||
if days == interval:
|
||||
notifications.append(notification)
|
||||
|
@ -11,12 +11,14 @@ from sqlalchemy_utils import JSONType
|
||||
|
||||
from lemur.database import db
|
||||
from lemur.plugins.base import plugins
|
||||
from lemur.models import certificate_notification_associations, \
|
||||
pending_cert_notification_associations
|
||||
from lemur.models import (
|
||||
certificate_notification_associations,
|
||||
pending_cert_notification_associations,
|
||||
)
|
||||
|
||||
|
||||
class Notification(db.Model):
|
||||
__tablename__ = 'notifications'
|
||||
__tablename__ = "notifications"
|
||||
id = Column(Integer, primary_key=True)
|
||||
label = Column(String(128), unique=True)
|
||||
description = Column(Text())
|
||||
@ -28,14 +30,14 @@ class Notification(db.Model):
|
||||
secondary=certificate_notification_associations,
|
||||
passive_deletes=True,
|
||||
backref="notification",
|
||||
cascade='all,delete'
|
||||
cascade="all,delete",
|
||||
)
|
||||
pending_certificates = relationship(
|
||||
"PendingCertificate",
|
||||
secondary=pending_cert_notification_associations,
|
||||
passive_deletes=True,
|
||||
backref="notification",
|
||||
cascade='all,delete'
|
||||
cascade="all,delete",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -7,7 +7,11 @@
|
||||
"""
|
||||
from marshmallow import fields, post_dump
|
||||
from lemur.common.schema import LemurInputSchema, LemurOutputSchema
|
||||
from lemur.schemas import PluginInputSchema, PluginOutputSchema, AssociatedCertificateSchema
|
||||
from lemur.schemas import (
|
||||
PluginInputSchema,
|
||||
PluginOutputSchema,
|
||||
AssociatedCertificateSchema,
|
||||
)
|
||||
|
||||
|
||||
class NotificationInputSchema(LemurInputSchema):
|
||||
@ -30,7 +34,7 @@ class NotificationOutputSchema(LemurOutputSchema):
|
||||
@post_dump
|
||||
def fill_object(self, data):
|
||||
if data:
|
||||
data['plugin']['pluginOptions'] = data['options']
|
||||
data["plugin"]["pluginOptions"] = data["options"]
|
||||
return data
|
||||
|
||||
|
||||
|
@ -31,26 +31,28 @@ def create_default_expiration_notifications(name, recipients, intervals=None):
|
||||
|
||||
options = [
|
||||
{
|
||||
'name': 'unit',
|
||||
'type': 'select',
|
||||
'required': True,
|
||||
'validation': '',
|
||||
'available': ['days', 'weeks', 'months'],
|
||||
'helpMessage': 'Interval unit',
|
||||
'value': 'days',
|
||||
"name": "unit",
|
||||
"type": "select",
|
||||
"required": True,
|
||||
"validation": "",
|
||||
"available": ["days", "weeks", "months"],
|
||||
"helpMessage": "Interval unit",
|
||||
"value": "days",
|
||||
},
|
||||
{
|
||||
'name': 'recipients',
|
||||
'type': 'str',
|
||||
'required': True,
|
||||
'validation': '^([\w+-.%]+@[\w-.]+\.[A-Za-z]{2,4},?)+$',
|
||||
'helpMessage': 'Comma delimited list of email addresses',
|
||||
'value': ','.join(recipients)
|
||||
"name": "recipients",
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"validation": "^([\w+-.%]+@[\w-.]+\.[A-Za-z]{2,4},?)+$",
|
||||
"helpMessage": "Comma delimited list of email addresses",
|
||||
"value": ",".join(recipients),
|
||||
},
|
||||
]
|
||||
|
||||
if intervals is None:
|
||||
intervals = current_app.config.get("LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS", [30, 15, 2])
|
||||
intervals = current_app.config.get(
|
||||
"LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS", [30, 15, 2]
|
||||
)
|
||||
|
||||
notifications = []
|
||||
for i in intervals:
|
||||
@ -58,21 +60,25 @@ def create_default_expiration_notifications(name, recipients, intervals=None):
|
||||
if not n:
|
||||
inter = [
|
||||
{
|
||||
'name': 'interval',
|
||||
'type': 'int',
|
||||
'required': True,
|
||||
'validation': '^\d+$',
|
||||
'helpMessage': 'Number of days to be alert before expiration.',
|
||||
'value': i,
|
||||
"name": "interval",
|
||||
"type": "int",
|
||||
"required": True,
|
||||
"validation": "^\d+$",
|
||||
"helpMessage": "Number of days to be alert before expiration.",
|
||||
"value": i,
|
||||
}
|
||||
]
|
||||
inter.extend(options)
|
||||
n = create(
|
||||
label="{name}_{interval}_DAY".format(name=name, interval=i),
|
||||
plugin_name=current_app.config.get("LEMUR_DEFAULT_NOTIFICATION_PLUGIN", "email-notification"),
|
||||
plugin_name=current_app.config.get(
|
||||
"LEMUR_DEFAULT_NOTIFICATION_PLUGIN", "email-notification"
|
||||
),
|
||||
options=list(inter),
|
||||
description="Default {interval} day expiration notification".format(interval=i),
|
||||
certificates=[]
|
||||
description="Default {interval} day expiration notification".format(
|
||||
interval=i
|
||||
),
|
||||
certificates=[],
|
||||
)
|
||||
notifications.append(n)
|
||||
|
||||
@ -91,7 +97,9 @@ def create(label, plugin_name, options, description, certificates):
|
||||
:rtype : Notification
|
||||
:return:
|
||||
"""
|
||||
notification = Notification(label=label, options=options, plugin_name=plugin_name, description=description)
|
||||
notification = Notification(
|
||||
label=label, options=options, plugin_name=plugin_name, description=description
|
||||
)
|
||||
notification.certificates = certificates
|
||||
return database.create(notification)
|
||||
|
||||
@ -147,7 +155,7 @@ def get_by_label(label):
|
||||
:param label:
|
||||
:return:
|
||||
"""
|
||||
return database.get(Notification, label, field='label')
|
||||
return database.get(Notification, label, field="label")
|
||||
|
||||
|
||||
def get_all():
|
||||
@ -161,18 +169,20 @@ def get_all():
|
||||
|
||||
|
||||
def render(args):
|
||||
filt = args.pop('filter')
|
||||
certificate_id = args.pop('certificate_id', None)
|
||||
filt = args.pop("filter")
|
||||
certificate_id = args.pop("certificate_id", None)
|
||||
|
||||
if certificate_id:
|
||||
query = database.session_query(Notification).join(Certificate, Notification.certificate)
|
||||
query = database.session_query(Notification).join(
|
||||
Certificate, Notification.certificate
|
||||
)
|
||||
query = query.filter(Certificate.id == certificate_id)
|
||||
else:
|
||||
query = database.session_query(Notification)
|
||||
|
||||
if filt:
|
||||
terms = filt.split(';')
|
||||
if terms[0] == 'active':
|
||||
terms = filt.split(";")
|
||||
if terms[0] == "active":
|
||||
query = query.filter(Notification.active == truthiness(terms[1]))
|
||||
else:
|
||||
query = database.filter(query, Notification, terms)
|
||||
|
@ -9,7 +9,11 @@
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api, reqparse, inputs
|
||||
from lemur.notifications import service
|
||||
from lemur.notifications.schemas import notification_input_schema, notification_output_schema, notifications_output_schema
|
||||
from lemur.notifications.schemas import (
|
||||
notification_input_schema,
|
||||
notification_output_schema,
|
||||
notifications_output_schema,
|
||||
)
|
||||
|
||||
from lemur.auth.service import AuthenticatedResource
|
||||
from lemur.common.utils import paginated_parser
|
||||
@ -17,12 +21,13 @@ from lemur.common.utils import paginated_parser
|
||||
from lemur.common.schema import validate_schema
|
||||
|
||||
|
||||
mod = Blueprint('notifications', __name__)
|
||||
mod = Blueprint("notifications", __name__)
|
||||
api = Api(mod)
|
||||
|
||||
|
||||
class NotificationsList(AuthenticatedResource):
|
||||
""" Defines the 'notifications' endpoint """
|
||||
|
||||
def __init__(self):
|
||||
self.reqparse = reqparse.RequestParser()
|
||||
super(NotificationsList, self).__init__()
|
||||
@ -103,7 +108,7 @@ class NotificationsList(AuthenticatedResource):
|
||||
:statuscode 200: no error
|
||||
"""
|
||||
parser = paginated_parser.copy()
|
||||
parser.add_argument('active', type=inputs.boolean, location='args')
|
||||
parser.add_argument("active", type=inputs.boolean, location="args")
|
||||
args = parser.parse_args()
|
||||
return service.render(args)
|
||||
|
||||
@ -215,11 +220,11 @@ class NotificationsList(AuthenticatedResource):
|
||||
:statuscode 200: no error
|
||||
"""
|
||||
return service.create(
|
||||
data['label'],
|
||||
data['plugin']['slug'],
|
||||
data['plugin']['plugin_options'],
|
||||
data['description'],
|
||||
data['certificates']
|
||||
data["label"],
|
||||
data["plugin"]["slug"],
|
||||
data["plugin"]["plugin_options"],
|
||||
data["description"],
|
||||
data["certificates"],
|
||||
)
|
||||
|
||||
|
||||
@ -334,20 +339,21 @@ class Notifications(AuthenticatedResource):
|
||||
"""
|
||||
return service.update(
|
||||
notification_id,
|
||||
data['label'],
|
||||
data['plugin']['plugin_options'],
|
||||
data['description'],
|
||||
data['active'],
|
||||
data['certificates']
|
||||
data["label"],
|
||||
data["plugin"]["plugin_options"],
|
||||
data["description"],
|
||||
data["active"],
|
||||
data["certificates"],
|
||||
)
|
||||
|
||||
def delete(self, notification_id):
|
||||
service.delete(notification_id)
|
||||
return {'result': True}
|
||||
return {"result": True}
|
||||
|
||||
|
||||
class CertificateNotifications(AuthenticatedResource):
|
||||
""" Defines the 'certificate/<int:certificate_id/notifications'' endpoint """
|
||||
|
||||
def __init__(self):
|
||||
super(CertificateNotifications, self).__init__()
|
||||
|
||||
@ -426,10 +432,15 @@ class CertificateNotifications(AuthenticatedResource):
|
||||
:reqheader Authorization: OAuth token to authenticate
|
||||
:statuscode 200: no error
|
||||
"""
|
||||
return service.render({'certificate_id': certificate_id})
|
||||
return service.render({"certificate_id": certificate_id})
|
||||
|
||||
|
||||
api.add_resource(NotificationsList, '/notifications', endpoint='notifications')
|
||||
api.add_resource(Notifications, '/notifications/<int:notification_id>', endpoint='notification')
|
||||
api.add_resource(CertificateNotifications, '/certificates/<int:certificate_id>/notifications',
|
||||
endpoint='certificateNotifications')
|
||||
api.add_resource(NotificationsList, "/notifications", endpoint="notifications")
|
||||
api.add_resource(
|
||||
Notifications, "/notifications/<int:notification_id>", endpoint="notification"
|
||||
)
|
||||
api.add_resource(
|
||||
CertificateNotifications,
|
||||
"/certificates/<int:certificate_id>/notifications",
|
||||
endpoint="certificateNotifications",
|
||||
)
|
||||
|
Reference in New Issue
Block a user