From 60bb0037f0b3bd774b935dbc7991e32f2f6386fc Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Fri, 16 Oct 2020 15:13:12 -0700 Subject: [PATCH 01/11] Miscellaneous notification fixes and tests --- lemur/notifications/messaging.py | 9 +- lemur/plugins/lemur_email/plugin.py | 9 +- .../lemur_email/templates/rotation.html | 18 ++-- lemur/plugins/lemur_email/tests/test_email.py | 89 ++++++++++++++----- lemur/plugins/lemur_slack/plugin.py | 31 +++---- lemur/plugins/lemur_slack/tests/test_slack.py | 45 ++++++++++ lemur/tests/conf.py | 2 +- lemur/tests/test_messaging.py | 15 +++- 8 files changed, 157 insertions(+), 61 deletions(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index 82db7b6e..82a1ff1e 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -101,6 +101,9 @@ def send_notification(event_type, data, targets, notification): notification.plugin.send(event_type, data, targets, notification.options) status = SUCCESS_METRIC_STATUS except Exception as e: + current_app.logger.error( + "Unable to send notification to {}.".format(targets), exc_info=True + ) sentry.captureException() metrics.send( @@ -190,13 +193,13 @@ 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") + current_app.config.get("LEMUR_DEFAULT_NOTIFICATION_PLUGIN", "email-notification") ) 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( @@ -290,7 +293,7 @@ def needs_notification(certificate): for notification in certificate.notifications: if not notification.active or not notification.options: - return + continue interval = get_plugin_option("interval", notification.options) unit = get_plugin_option("unit", notification.options) diff --git a/lemur/plugins/lemur_email/plugin.py b/lemur/plugins/lemur_email/plugin.py index 241aa1b0..08332ef1 100644 --- a/lemur/plugins/lemur_email/plugin.py +++ b/lemur/plugins/lemur_email/plugin.py @@ -19,14 +19,16 @@ from lemur.plugins import lemur_email as email from lemur.plugins.lemur_email.templates.config import env -def render_html(template_name, message): +def render_html(template_name, options, certificates): """ Renders the html for our email notification. :param template_name: - :param message: + :param options: + :param certificates: :return: """ + message = {"options": options, "certificates": certificates} template = env.get_template("{}.html".format(template_name)) return template.render( dict(message=message, hostname=current_app.config.get("LEMUR_HOSTNAME")) @@ -100,8 +102,7 @@ class EmailNotificationPlugin(ExpirationNotificationPlugin): subject = "Lemur: {0} Notification".format(notification_type.capitalize()) - data = {"options": options, "certificates": message} - body = render_html(notification_type, data) + body = render_html(notification_type, options, message) s_type = current_app.config.get("LEMUR_EMAIL_SENDER", "ses").lower() diff --git a/lemur/plugins/lemur_email/templates/rotation.html b/lemur/plugins/lemur_email/templates/rotation.html index 521eb327..9ce7ff33 100644 --- a/lemur/plugins/lemur_email/templates/rotation.html +++ b/lemur/plugins/lemur_email/templates/rotation.html @@ -83,12 +83,12 @@ - {{ certificate.name }} + {{ message.certificates.name }}
-
{{ certificate.owner }} -
{{ certificate.validityEnd | time }} - Details +
{{ message.certificates.owner }} +
{{ message.certificates.validityEnd | time }} + Details
@@ -110,12 +110,12 @@ - {{ certificate.replacedBy[0].name }} + {{ message.certificates.name }}
-
{{ certificate.replacedBy[0].owner }} -
{{ certificate.replacedBy[0].validityEnd | time }} - Details +
{{ message.certificates.owner }} +
{{ message.certificates.validityEnd | time }} + Details
@@ -133,7 +133,7 @@ - {% for endpoint in certificate.endpoints %} + {% for endpoint in message.certificates.endpoints %} diff --git a/lemur/plugins/lemur_email/tests/test_email.py b/lemur/plugins/lemur_email/tests/test_email.py index 43168cab..4f1ea187 100644 --- a/lemur/plugins/lemur_email/tests/test_email.py +++ b/lemur/plugins/lemur_email/tests/test_email.py @@ -1,36 +1,83 @@ import os -from lemur.plugins.lemur_email.templates.config import env +from datetime import timedelta +import arrow +import boto3 +from moto import mock_ses + +from lemur.certificates.schemas import certificate_notification_output_schema +from lemur.plugins.lemur_email.plugin import render_html from lemur.tests.factories import CertificateFactory dir_path = os.path.dirname(os.path.realpath(__file__)) -def test_render(certificate, endpoint): - from lemur.certificates.schemas import certificate_notification_output_schema +@mock_ses +def verify_sender_email(): + ses_client = boto3.client("ses", region_name="us-east-1") + ses_client.verify_email_identity(EmailAddress="lemur@example.com") + + +def get_options(): + return [ + {"name": "interval", "value": 10}, + {"name": "unit", "value": "days"}, + ] + + +def test_render_expiration(certificate, endpoint): new_cert = CertificateFactory() new_cert.replaces.append(certificate) - data = { - "certificates": [certificate_notification_output_schema.dump(certificate).data], - "options": [ - {"name": "interval", "value": 10}, - {"name": "unit", "value": "days"}, - ], - } + assert render_html("expiration", get_options(), [certificate_notification_output_schema.dump(certificate).data]) - template = env.get_template("{}.html".format("expiration")) - - body = template.render(dict(message=data, hostname="lemur.test.example.com")) - - template = env.get_template("{}.html".format("rotation")) +def test_render_rotation(certificate, endpoint): certificate.endpoints.append(endpoint) - body = template.render( - dict( - certificate=certificate_notification_output_schema.dump(certificate).data, - hostname="lemur.test.example.com", - ) - ) + assert render_html("rotation", get_options(), certificate_notification_output_schema.dump(certificate).data) + + +def test_render_rotation_failure(pending_certificate): + assert render_html("failed", get_options(), certificate_notification_output_schema.dump(pending_certificate).data) + + +@mock_ses +def test_send_expiration_notification(): + from lemur.notifications.messaging import send_expiration_notifications + from lemur.tests.factories import CertificateFactory + from lemur.tests.factories import NotificationFactory + + now = arrow.utcnow() + in_ten_days = now + timedelta(days=10, hours=1) # a bit more than 10 days since we'll check in the future + certificate = CertificateFactory() + notification = NotificationFactory(plugin_name="email-notification") + + certificate.not_after = in_ten_days + certificate.notifications.append(notification) + certificate.notifications[0].options = get_options() + + verify_sender_email() + assert send_expiration_notifications([]) == (2, 0) + + +@mock_ses +def test_send_rotation_notification(endpoint, source_plugin): + from lemur.notifications.messaging import send_rotation_notification + from lemur.deployment.service import rotate_certificate + + new_certificate = CertificateFactory() + rotate_certificate(endpoint, new_certificate) + assert endpoint.certificate == new_certificate + + verify_sender_email() + assert send_rotation_notification(new_certificate) + + +@mock_ses +def test_send_pending_failure_notification(user, pending_certificate, async_issuer_plugin): + from lemur.notifications.messaging import send_pending_failure_notification + + verify_sender_email() + assert send_pending_failure_notification(pending_certificate) diff --git a/lemur/plugins/lemur_slack/plugin.py b/lemur/plugins/lemur_slack/plugin.py index 7569d295..67c3fd84 100644 --- a/lemur/plugins/lemur_slack/plugin.py +++ b/lemur/plugins/lemur_slack/plugin.py @@ -58,26 +58,19 @@ def create_rotation_attachments(certificate): "title": certificate["name"], "title_link": create_certificate_url(certificate["name"]), "fields": [ + {"title": "Owner", "value": certificate["owner"], "short": True}, { - {"title": "Owner", "value": certificate["owner"], "short": True}, - { - "title": "Expires", - "value": arrow.get(certificate["validityEnd"]).format( - "dddd, MMMM D, YYYY" - ), - "short": True, - }, - { - "title": "Replaced By", - "value": len(certificate["replaced"][0]["name"]), - "short": True, - }, - { - "title": "Endpoints Rotated", - "value": len(certificate["endpoints"]), - "short": True, - }, - } + "title": "Expires", + "value": arrow.get(certificate["validityEnd"]).format( + "dddd, MMMM D, YYYY" + ), + "short": True, + }, + { + "title": "Endpoints Rotated", + "value": len(certificate["endpoints"]), + "short": True, + }, ], } diff --git a/lemur/plugins/lemur_slack/tests/test_slack.py b/lemur/plugins/lemur_slack/tests/test_slack.py index 86add25f..77abd542 100644 --- a/lemur/plugins/lemur_slack/tests/test_slack.py +++ b/lemur/plugins/lemur_slack/tests/test_slack.py @@ -21,3 +21,48 @@ def test_formatting(certificate): } assert attachment == create_expiration_attachments(data)[0] + + +def get_options(): + return [ + {"name": "interval", "value": 10}, + {"name": "unit", "value": "days"}, + ] + + +# Currently disabled as we have no good way to mock Slack webhooks +# def test_send_expiration_notification(): +# from lemur.notifications.messaging import send_expiration_notifications +# from lemur.tests.factories import CertificateFactory +# +# now = arrow.utcnow() +# in_ten_days = now + timedelta(days=10, hours=1) # a bit more than 10 days since we'll check in the future +# certificate = CertificateFactory() +# notification = NotificationFactory(plugin_name="slack-notification") +# +# certificate.not_after = in_ten_days +# certificate.notifications.append(notification) +# certificate.notifications[0].options = get_options() +# +# assert send_expiration_notifications([]) == (2, 0) + + +# Currently disabled as we have no good way to mock Slack webhooks +# def test_send_rotation_notification(endpoint, source_plugin): +# from lemur.notifications.messaging import send_rotation_notification +# from lemur.deployment.service import rotate_certificate +# +# notification = NotificationFactory(plugin_name="slack-notification") +# +# new_certificate = CertificateFactory() +# rotate_certificate(endpoint, new_certificate) +# assert endpoint.certificate == new_certificate +# +# assert send_rotation_notification(new_certificate, notification_plugin=notification.plugin) + + +# Currently disabled as the Slack plugin doesn't support this type of notification +# def test_send_pending_failure_notification(user, pending_certificate, async_issuer_plugin): +# from lemur.notifications.messaging import send_pending_failure_notification +# +# assert send_pending_failure_notification(pending_certificate, notification_plugin=plugins.get("slack-notification")) diff --git a/lemur/tests/conf.py b/lemur/tests/conf.py index 38b8bade..3dfb5621 100644 --- a/lemur/tests/conf.py +++ b/lemur/tests/conf.py @@ -46,7 +46,7 @@ LEMUR_ALLOWED_DOMAINS = [ # Lemur currently only supports SES for sending email, this address # needs to be verified -LEMUR_EMAIL = "" +LEMUR_EMAIL = "lemur@example.com" LEMUR_SECURITY_TEAM_EMAIL = ["security@example.com"] LEMUR_HOSTNAME = "lemur.example.com" diff --git a/lemur/tests/test_messaging.py b/lemur/tests/test_messaging.py index 98e9ebf3..dd8f339f 100644 --- a/lemur/tests/test_messaging.py +++ b/lemur/tests/test_messaging.py @@ -1,8 +1,8 @@ +from datetime import timedelta + +import arrow import pytest from freezegun import freeze_time - -from datetime import timedelta -import arrow from moto import mock_ses @@ -105,4 +105,11 @@ def test_send_expiration_notification_with_no_notifications( def test_send_rotation_notification(notification_plugin, certificate): from lemur.notifications.messaging import send_rotation_notification - send_rotation_notification(certificate, notification_plugin=notification_plugin) + assert send_rotation_notification(certificate, notification_plugin=notification_plugin) + + +@mock_ses +def test_send_pending_failure_notification(notification_plugin, async_issuer_plugin, pending_certificate): + from lemur.notifications.messaging import send_pending_failure_notification + + assert send_pending_failure_notification(pending_certificate, notification_plugin=notification_plugin) From 072b337f37487a5750e339ffdf05100aec24162c Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Fri, 16 Oct 2020 16:21:43 -0700 Subject: [PATCH 02/11] Restructure log messages --- lemur/notifications/messaging.py | 54 ++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index 82a1ff1e..78809dbc 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -8,6 +8,7 @@ .. moduleauthor:: Kevin Glisson """ +import sys from collections import defaultdict from datetime import timedelta from itertools import groupby @@ -96,14 +97,20 @@ def send_notification(event_type, data, targets, notification): :param notification: :return: """ + function = f"{__name__}.{sys._getframe().f_code.co_name}" + log_data = { + "function": function, + "message": "Sending expiration notification for to targets {}".format(targets), + "notification_type": "rotation", + "targets": targets, + } status = FAILURE_METRIC_STATUS try: notification.plugin.send(event_type, data, targets, notification.options) status = SUCCESS_METRIC_STATUS except Exception as e: - current_app.logger.error( - "Unable to send notification to {}.".format(targets), exc_info=True - ) + log_data["message"] = "Unable to send expiration notification to targets {}".format(targets) + current_app.logger.error(log_data, exc_info=True) sentry.captureException() metrics.send( @@ -190,6 +197,14 @@ def send_rotation_notification(certificate, notification_plugin=None): :param notification_plugin: :return: """ + function = f"{__name__}.{sys._getframe().f_code.co_name}" + log_data = { + "function": function, + "message": "Sending rotation notification for certificate {}".format(certificate.name), + "notification_type": "rotation", + "name": certificate.name, + "owner": certificate.owner, + } status = FAILURE_METRIC_STATUS if not notification_plugin: notification_plugin = plugins.get( @@ -202,9 +217,9 @@ def send_rotation_notification(certificate, notification_plugin=None): 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 - ) + log_data["message"] = "Unable to send rotation notification for certificate {0} to ownner {1}" \ + .format(certificate.name, data["owner"]) + current_app.logger.error(log_data) sentry.captureException() metrics.send( @@ -228,6 +243,14 @@ def send_pending_failure_notification( :param notification_plugin: :return: """ + function = f"{__name__}.{sys._getframe().f_code.co_name}" + log_data = { + "function": function, + "message": "Sending pending failure notification for pending certificate {}".format(pending_cert.name), + "notification_type": "rotation", + "name": pending_cert.name, + "owner": pending_cert.owner, + } status = FAILURE_METRIC_STATUS if not notification_plugin: @@ -245,12 +268,10 @@ def send_pending_failure_notification( 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, - ) + log_data["recipient"] = data["owner"] + log_data["message"] = "Unable to send pending failure notification for certificate {0} to owner {1}" \ + .format(pending_cert.name, pending_cert.owner) + current_app.logger.error(log_data, exc_info=True) sentry.captureException() if notify_security: @@ -260,11 +281,10 @@ def send_pending_failure_notification( ) 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, - ) + log_data["recipient"] = data["security_email"] + log_data["message"] = "Unable to send pending failure notification for certificate {0} to security email " \ + "{1}".format(pending_cert.name, pending_cert.owner) + current_app.logger.error(log_data, exc_info=True) sentry.captureException() metrics.send( From 6a1889787dc2d5df5836a9f19ca2b503c80515b5 Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Fri, 16 Oct 2020 16:30:21 -0700 Subject: [PATCH 03/11] Correct log attributes --- lemur/notifications/messaging.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index 78809dbc..5452f4fc 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -101,8 +101,8 @@ def send_notification(event_type, data, targets, notification): log_data = { "function": function, "message": "Sending expiration notification for to targets {}".format(targets), - "notification_type": "rotation", - "targets": targets, + "notification_type": "expiration", + "certificate_targets": targets, } status = FAILURE_METRIC_STATUS try: @@ -202,8 +202,8 @@ def send_rotation_notification(certificate, notification_plugin=None): "function": function, "message": "Sending rotation notification for certificate {}".format(certificate.name), "notification_type": "rotation", - "name": certificate.name, - "owner": certificate.owner, + "certificate_name": certificate.name, + "certificate_owner": certificate.owner, } status = FAILURE_METRIC_STATUS if not notification_plugin: @@ -247,9 +247,9 @@ def send_pending_failure_notification( log_data = { "function": function, "message": "Sending pending failure notification for pending certificate {}".format(pending_cert.name), - "notification_type": "rotation", - "name": pending_cert.name, - "owner": pending_cert.owner, + "notification_type": "failed", + "certificate_name": pending_cert.name, + "certificate_owner": pending_cert.owner, } status = FAILURE_METRIC_STATUS From e90b08b3633c7a277da84ad19cde45324ca9bf5a Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Fri, 16 Oct 2020 17:08:44 -0700 Subject: [PATCH 04/11] Correct typo and enable Slack notification test --- lemur/notifications/messaging.py | 4 +- lemur/plugins/lemur_slack/tests/test_slack.py | 40 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index 5452f4fc..aa85123d 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -217,9 +217,9 @@ def send_rotation_notification(certificate, notification_plugin=None): notification_plugin.send("rotation", data, [data["owner"]], []) status = SUCCESS_METRIC_STATUS except Exception as e: - log_data["message"] = "Unable to send rotation notification for certificate {0} to ownner {1}" \ + log_data["message"] = "Unable to send rotation notification for certificate {0} to owner {1}" \ .format(certificate.name, data["owner"]) - current_app.logger.error(log_data) + current_app.logger.error(log_data, exc_info=True) sentry.captureException() metrics.send( diff --git a/lemur/plugins/lemur_slack/tests/test_slack.py b/lemur/plugins/lemur_slack/tests/test_slack.py index 77abd542..da232d61 100644 --- a/lemur/plugins/lemur_slack/tests/test_slack.py +++ b/lemur/plugins/lemur_slack/tests/test_slack.py @@ -1,3 +1,10 @@ +from datetime import timedelta + +import arrow + +from lemur.tests.factories import NotificationFactory, CertificateFactory + + def test_formatting(certificate): from lemur.plugins.lemur_slack.plugin import create_expiration_attachments from lemur.certificates.schemas import certificate_notification_output_schema @@ -27,32 +34,33 @@ def get_options(): return [ {"name": "interval", "value": 10}, {"name": "unit", "value": "days"}, + {"name": "webhook", "value": "https://slack.com/api/api.test"}, ] -# Currently disabled as we have no good way to mock Slack webhooks -# def test_send_expiration_notification(): -# from lemur.notifications.messaging import send_expiration_notifications -# from lemur.tests.factories import CertificateFactory -# -# now = arrow.utcnow() -# in_ten_days = now + timedelta(days=10, hours=1) # a bit more than 10 days since we'll check in the future -# certificate = CertificateFactory() -# notification = NotificationFactory(plugin_name="slack-notification") -# -# certificate.not_after = in_ten_days -# certificate.notifications.append(notification) -# certificate.notifications[0].options = get_options() -# -# assert send_expiration_notifications([]) == (2, 0) +def test_send_expiration_notification(): + from lemur.notifications.messaging import send_expiration_notifications + + notification = NotificationFactory(plugin_name="slack-notification") + notification.options = get_options() + + now = arrow.utcnow() + in_ten_days = now + timedelta(days=10, hours=1) # a bit more than 10 days since we'll check in the future + + certificate = CertificateFactory() + certificate.not_after = in_ten_days + certificate.notifications.append(notification) + + assert send_expiration_notifications([]) == (2, 0) -# Currently disabled as we have no good way to mock Slack webhooks +# Currently disabled as the Slack plugin doesn't support this type of notification # def test_send_rotation_notification(endpoint, source_plugin): # from lemur.notifications.messaging import send_rotation_notification # from lemur.deployment.service import rotate_certificate # # notification = NotificationFactory(plugin_name="slack-notification") +# notification.options = get_options() # # new_certificate = CertificateFactory() # rotate_certificate(endpoint, new_certificate) From ecd4d6ebe3489cb9f2ad7988eb3eb373fb053714 Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Mon, 19 Oct 2020 15:12:48 -0700 Subject: [PATCH 05/11] Change string formatting pattern --- lemur/notifications/messaging.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index aa85123d..6c8599aa 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -100,7 +100,7 @@ def send_notification(event_type, data, targets, notification): function = f"{__name__}.{sys._getframe().f_code.co_name}" log_data = { "function": function, - "message": "Sending expiration notification for to targets {}".format(targets), + "message": f"Sending expiration notification for to targets {targets}", "notification_type": "expiration", "certificate_targets": targets, } @@ -109,7 +109,7 @@ def send_notification(event_type, data, targets, notification): notification.plugin.send(event_type, data, targets, notification.options) status = SUCCESS_METRIC_STATUS except Exception as e: - log_data["message"] = "Unable to send expiration notification to targets {}".format(targets) + log_data["message"] = f"Unable to send expiration notification to targets {targets}" current_app.logger.error(log_data, exc_info=True) sentry.captureException() @@ -200,7 +200,7 @@ def send_rotation_notification(certificate, notification_plugin=None): function = f"{__name__}.{sys._getframe().f_code.co_name}" log_data = { "function": function, - "message": "Sending rotation notification for certificate {}".format(certificate.name), + "message": f"Sending rotation notification for certificate {certificate.name}", "notification_type": "rotation", "certificate_name": certificate.name, "certificate_owner": certificate.owner, @@ -217,8 +217,8 @@ def send_rotation_notification(certificate, notification_plugin=None): notification_plugin.send("rotation", data, [data["owner"]], []) status = SUCCESS_METRIC_STATUS except Exception as e: - log_data["message"] = "Unable to send rotation notification for certificate {0} to owner {1}" \ - .format(certificate.name, data["owner"]) + log_data["message"] = f"Unable to send rotation notification for certificate {certificate.name} " \ + f"to owner {data['owner']}" current_app.logger.error(log_data, exc_info=True) sentry.captureException() @@ -246,7 +246,7 @@ def send_pending_failure_notification( function = f"{__name__}.{sys._getframe().f_code.co_name}" log_data = { "function": function, - "message": "Sending pending failure notification for pending certificate {}".format(pending_cert.name), + "message": f"Sending pending failure notification for pending certificate {pending_cert}" "notification_type": "failed", "certificate_name": pending_cert.name, "certificate_owner": pending_cert.owner, @@ -269,8 +269,8 @@ def send_pending_failure_notification( status = SUCCESS_METRIC_STATUS except Exception as e: log_data["recipient"] = data["owner"] - log_data["message"] = "Unable to send pending failure notification for certificate {0} to owner {1}" \ - .format(pending_cert.name, pending_cert.owner) + log_data["message"] = f"Unable to send pending failure notification for certificate {pending_cert.name} " \ + f"to owner {pending_cert.owner}" current_app.logger.error(log_data, exc_info=True) sentry.captureException() @@ -282,8 +282,8 @@ def send_pending_failure_notification( status = SUCCESS_METRIC_STATUS except Exception as e: log_data["recipient"] = data["security_email"] - log_data["message"] = "Unable to send pending failure notification for certificate {0} to security email " \ - "{1}".format(pending_cert.name, pending_cert.owner) + log_data["message"] = f"Unable to send pending failure notification for certificate {pending_cert.name} " \ + f"to security email {pending_cert.owner}" current_app.logger.error(log_data, exc_info=True) sentry.captureException() @@ -291,7 +291,7 @@ def send_pending_failure_notification( "notification", "counter", 1, - metric_tags={"status": status, "event_type": "rotation"}, + metric_tags={"status": status, "event_type": "failed"}, ) if status == SUCCESS_METRIC_STATUS: @@ -329,7 +329,7 @@ def needs_notification(certificate): else: raise Exception( - "Invalid base unit for expiration interval: {0}".format(unit) + f"Invalid base unit for expiration interval: {unit}" ) if days == interval: From b5f0fc5a195f464885a3f808edcfe4f95b054f4d Mon Sep 17 00:00:00 2001 From: Jasmine Schladen Date: Mon, 19 Oct 2020 15:21:34 -0700 Subject: [PATCH 06/11] Fix syntax error --- lemur/notifications/messaging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/notifications/messaging.py b/lemur/notifications/messaging.py index 6c8599aa..ca955b69 100644 --- a/lemur/notifications/messaging.py +++ b/lemur/notifications/messaging.py @@ -246,7 +246,7 @@ def send_pending_failure_notification( function = f"{__name__}.{sys._getframe().f_code.co_name}" log_data = { "function": function, - "message": f"Sending pending failure notification for pending certificate {pending_cert}" + "message": f"Sending pending failure notification for pending certificate {pending_cert}", "notification_type": "failed", "certificate_name": pending_cert.name, "certificate_owner": pending_cert.owner, From 855baadfee15cf40610603c79fc6c8ead0164999 Mon Sep 17 00:00:00 2001 From: sayali Date: Fri, 16 Oct 2020 17:36:32 -0700 Subject: [PATCH 07/11] Show only few supported ECC algorithms on UI --- .../angular/authorities/authority/options.tpl.html | 13 +++++++++++-- .../certificates/certificate/options.tpl.html | 12 ++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lemur/static/app/angular/authorities/authority/options.tpl.html b/lemur/static/app/angular/authorities/authority/options.tpl.html index bf1ad70c..01928fc3 100644 --- a/lemur/static/app/angular/authorities/authority/options.tpl.html +++ b/lemur/static/app/angular/authorities/authority/options.tpl.html @@ -20,8 +20,17 @@ Key Type
- +
diff --git a/lemur/static/app/angular/certificates/certificate/options.tpl.html b/lemur/static/app/angular/certificates/certificate/options.tpl.html index 7e6ad428..2c02c693 100644 --- a/lemur/static/app/angular/certificates/certificate/options.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/options.tpl.html @@ -32,10 +32,14 @@
From 788703ce12b9e3783fe6cd3ce00d3ebf98caf9e3 Mon Sep 17 00:00:00 2001 From: sayali Date: Tue, 20 Oct 2020 16:43:57 -0700 Subject: [PATCH 08/11] Fix cert reissue when L/OU is not set get_certificate_primitives complains with None L/OU --- lemur/certificates/schemas.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lemur/certificates/schemas.py b/lemur/certificates/schemas.py index cc0a607e..77f49c9b 100644 --- a/lemur/certificates/schemas.py +++ b/lemur/certificates/schemas.py @@ -353,6 +353,12 @@ class CertificateOutputSchema(LemurOutputSchema): data.pop("organization", None) data.pop("organizational_unit", None) + # Removing optional fields if None, else it complains in de-serialization + if "location" in data and data["location"] is None: + data.pop("location") + if "organizational_unit" in data and data["organizational_unit"] is None: + data.pop("organizational_unit") + class CertificateShortOutputSchema(LemurOutputSchema): id = fields.Integer() From 01dddd2a557286cbf8ecf1229ec5ed51518fd65f Mon Sep 17 00:00:00 2001 From: sayali Date: Tue, 20 Oct 2020 17:17:28 -0700 Subject: [PATCH 09/11] iterate over subject details --- lemur/certificates/schemas.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lemur/certificates/schemas.py b/lemur/certificates/schemas.py index 77f49c9b..3dc864e7 100644 --- a/lemur/certificates/schemas.py +++ b/lemur/certificates/schemas.py @@ -340,6 +340,8 @@ class CertificateOutputSchema(LemurOutputSchema): @post_dump def handle_subject_details(self, data): + subject_details = ["country", "state", "location", "organization", "organizational_unit"] + # Remove subject details if authority is CA/Browser Forum compliant. The code will use default set of values in that case. # If CA/Browser Forum compliance of an authority is unknown (None), it is safe to fallback to default values. Thus below # condition checks for 'not False' ==> 'True or None' @@ -347,17 +349,13 @@ class CertificateOutputSchema(LemurOutputSchema): is_cab_compliant = data.get("authority").get("isCabCompliant") if is_cab_compliant is not False: - data.pop("country", None) - data.pop("state", None) - data.pop("location", None) - data.pop("organization", None) - data.pop("organizational_unit", None) + for field in subject_details: + data.pop(field, None) - # Removing optional fields if None, else it complains in de-serialization - if "location" in data and data["location"] is None: - data.pop("location") - if "organizational_unit" in data and data["organizational_unit"] is None: - data.pop("organizational_unit") + # Removing subject fields if None, else it complains in de-serialization + for field in subject_details: + if field in data and data[field] is None: + data.pop(field) class CertificateShortOutputSchema(LemurOutputSchema): From 49971652351ec487961234c353cad0ed62158984 Mon Sep 17 00:00:00 2001 From: sayali Date: Tue, 20 Oct 2020 17:59:50 -0700 Subject: [PATCH 10/11] Removing ECC 192 and 521 from UI not CAB supported. Keeping 521 for authority --- .../static/app/angular/authorities/authority/options.tpl.html | 1 - .../app/angular/certificates/certificate/options.tpl.html | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lemur/static/app/angular/authorities/authority/options.tpl.html b/lemur/static/app/angular/authorities/authority/options.tpl.html index 01928fc3..adf8eacc 100644 --- a/lemur/static/app/angular/authorities/authority/options.tpl.html +++ b/lemur/static/app/angular/authorities/authority/options.tpl.html @@ -24,7 +24,6 @@ ng-options="option.value as option.name for option in [ {'name': 'RSA-2048', 'value': 'RSA2048'}, {'name': 'RSA-4096', 'value': 'RSA4096'}, - {'name': 'ECC-PRIME192V1', 'value': 'ECCPRIME192V1'}, {'name': 'ECC-PRIME256V1', 'value': 'ECCPRIME256V1'}, {'name': 'ECC-SECP384R1', 'value': 'ECCSECP384R1'}, {'name': 'ECC-SECP521R1', 'value': 'ECCSECP521R1'}]" diff --git a/lemur/static/app/angular/certificates/certificate/options.tpl.html b/lemur/static/app/angular/certificates/certificate/options.tpl.html index 2c02c693..11b8fe68 100644 --- a/lemur/static/app/angular/certificates/certificate/options.tpl.html +++ b/lemur/static/app/angular/certificates/certificate/options.tpl.html @@ -35,10 +35,8 @@ ng-options="option.value as option.name for option in [ {'name': 'RSA-2048', 'value': 'RSA2048'}, {'name': 'RSA-4096', 'value': 'RSA4096'}, - {'name': 'ECC-PRIME192V1', 'value': 'ECCPRIME192V1'}, {'name': 'ECC-PRIME256V1', 'value': 'ECCPRIME256V1'}, - {'name': 'ECC-SECP384R1', 'value': 'ECCSECP384R1'}, - {'name': 'ECC-SECP521R1', 'value': 'ECCSECP521R1'}]" + {'name': 'ECC-SECP384R1', 'value': 'ECCSECP384R1'}]" ng-init="certificate.keyType = 'RSA2048'"> From 757e190b6094966ff16113d2e82b5677ca8bb025 Mon Sep 17 00:00:00 2001 From: sayali Date: Wed, 21 Oct 2020 12:11:41 -0700 Subject: [PATCH 11/11] Check if OU and L is present in subject fixing index out of range --- lemur/common/defaults.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lemur/common/defaults.py b/lemur/common/defaults.py index b9c88e49..d94c3563 100644 --- a/lemur/common/defaults.py +++ b/lemur/common/defaults.py @@ -110,9 +110,11 @@ def organizational_unit(cert): :return: """ try: - return cert.subject.get_attributes_for_oid(x509.OID_ORGANIZATIONAL_UNIT_NAME)[ - 0 - ].value.strip() + ou = cert.subject.get_attributes_for_oid(x509.OID_ORGANIZATIONAL_UNIT_NAME) + if not ou: + return None + + return ou[0].value.strip() except Exception as e: sentry.captureException() current_app.logger.error("Unable to get organizational unit! {0}".format(e)) @@ -155,9 +157,11 @@ def location(cert): :return: """ try: - return cert.subject.get_attributes_for_oid(x509.OID_LOCALITY_NAME)[ - 0 - ].value.strip() + loc = cert.subject.get_attributes_for_oid(x509.OID_LOCALITY_NAME) + if not loc: + return None + + return loc[0].value.strip() except Exception as e: sentry.captureException() current_app.logger.error("Unable to get location! {0}".format(e))