From 7dac0e1dd8978c055a6201d80e67a5cb30525362 Mon Sep 17 00:00:00 2001 From: csine-nflx <59457265+csine-nflx@users.noreply.github.com> Date: Fri, 31 Jan 2020 16:54:25 -0800 Subject: [PATCH 01/17] Update administration.rst --- docs/administration.rst | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/administration.rst b/docs/administration.rst index e292ae03..252a7dec 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -973,6 +973,41 @@ Will be the sender of all notifications, so ensure that it is verified with AWS. SES if the default notification gateway and will be used unless SMTP settings are configured in the application configuration settings. +PowerDNS ACME Plugin +~~~~~~~~~~~~~~~~~~~~~~ + +The following configuration properties are required to use the PowerDNS ACME Plugin for domain validation. + + +.. data:: ACME_POWERDNS_DOMAIN + :noindex: + + This is the FQDN for the PowerDNS API (without path) + + +.. data:: ACME_POWERDNS_SERVERID + :noindex: + + This is the ServerID attribute of the PowerDNS API Server (i.e. "localhost" + + +.. data:: ACME_POWERDNS_APIKEYNAME + :noindex: + + This is the Key name to use for authentication (i.e. "X-API-Key") + + +.. data:: ACME_POWERDNS_APIKEY + :noindex: + + This is the API Key to use for authentication (i.e. "Password") + + +.. data:: ACME_POWERDNS_RETRIES + :noindex: + + This is the number of times DNS Verification should be attempted (i.e. 20) + .. _CommandLineInterface: Command Line Interface @@ -1172,11 +1207,12 @@ Acme Kevin Glisson , Curtis Castrapel , Hossein Shafagh , - Mikhail Khodorovskiy + Mikhail Khodorovskiy , + Chad Sine :Type: Issuer :Description: - Adds support for the ACME protocol (including LetsEncrypt) with domain validation being handled Route53. + Adds support for the ACME protocol (including LetsEncrypt) with domain validation using several providers. Atlas From ac0282529ef9ec60050be4681c1ce278cb0eab04 Mon Sep 17 00:00:00 2001 From: csine-nflx Date: Mon, 3 Feb 2020 11:05:20 -0800 Subject: [PATCH 02/17] adding basic logging on success --- lemur/plugins/lemur_acme/plugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lemur/plugins/lemur_acme/plugin.py b/lemur/plugins/lemur_acme/plugin.py index e38870d8..54493f97 100644 --- a/lemur/plugins/lemur_acme/plugin.py +++ b/lemur/plugins/lemur_acme/plugin.py @@ -183,6 +183,10 @@ class AcmeHandler(object): else: raise + current_app.logger.debug( + f"Successfully resolved Acme order: {order.uri}", exc_info=True + ) + pem_certificate = OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.load_certificate( From 5324290234fdb865a89fcdabe6c4811f0889060b Mon Sep 17 00:00:00 2001 From: csine-nflx Date: Tue, 4 Feb 2020 16:23:53 -0800 Subject: [PATCH 03/17] updating documentation based on feedback --- docs/administration.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/administration.rst b/docs/administration.rst index 252a7dec..8f055147 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -988,7 +988,7 @@ The following configuration properties are required to use the PowerDNS ACME Plu .. data:: ACME_POWERDNS_SERVERID :noindex: - This is the ServerID attribute of the PowerDNS API Server (i.e. "localhost" + This is the ServerID attribute of the PowerDNS API Server (i.e. "localhost") .. data:: ACME_POWERDNS_APIKEYNAME @@ -1106,6 +1106,15 @@ All commands default to `~/.lemur/lemur.conf.py` if a configuration is not speci lemur notify +.. data:: acme + + Handles all ACME related tasks, like ACME plugin testing. + + :: + + lemur acme + + Sub-commands ------------ From ca8e73286f6f0ade5ffb130e276397e9d6b96273 Mon Sep 17 00:00:00 2001 From: csine-nflx Date: Wed, 12 Feb 2020 15:10:24 -0800 Subject: [PATCH 04/17] fixed get_domains() to remove duplicate entries, updated usage and tests --- lemur/plugins/lemur_acme/plugin.py | 14 ++++---------- lemur/plugins/lemur_acme/tests/test_acme.py | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lemur/plugins/lemur_acme/plugin.py b/lemur/plugins/lemur_acme/plugin.py index 8991efdf..95689a13 100644 --- a/lemur/plugins/lemur_acme/plugin.py +++ b/lemur/plugins/lemur_acme/plugin.py @@ -254,8 +254,9 @@ class AcmeHandler(object): domains = [options["common_name"]] if options.get("extensions"): - for name in options["extensions"]["sub_alt_names"]["names"]: - domains.append(name) + for dns_name in options["extensions"]["sub_alt_names"]["names"]: + if dns_name.value not in domains: + domains.append(dns_name.value) current_app.logger.debug("Got these domains: {0}".format(domains)) return domains @@ -640,15 +641,8 @@ class ACMEIssuerPlugin(IssuerPlugin): domains = self.acme.get_domains(issuer_options) if not create_immediately: # Create pending authorizations that we'll need to do the creation - authz_domains = [] - for d in domains: - if type(d) == str: - authz_domains.append(d) - else: - authz_domains.append(d.value) - dns_authorization = authorization_service.create( - account_number, authz_domains, provider_type + account_number, domains, provider_type ) # Return id of the DNS Authorization return None, None, dns_authorization.id diff --git a/lemur/plugins/lemur_acme/tests/test_acme.py b/lemur/plugins/lemur_acme/tests/test_acme.py index 04997ace..990a556e 100644 --- a/lemur/plugins/lemur_acme/tests/test_acme.py +++ b/lemur/plugins/lemur_acme/tests/test_acme.py @@ -1,4 +1,6 @@ import unittest + +from cryptography.x509 import DNSName from requests.models import Response from mock import MagicMock, Mock, patch @@ -74,12 +76,14 @@ class TestAcme(unittest.TestCase): @patch("acme.client.Client") @patch("lemur.plugins.lemur_acme.plugin.current_app") @patch("lemur.plugins.lemur_acme.cloudflare.wait_for_dns_change") + @patch("time.sleep") def test_complete_dns_challenge_success( - self, mock_wait_for_dns_change, mock_current_app, mock_acme + self, mock_sleep, mock_wait_for_dns_change, mock_current_app, mock_acme ): mock_dns_provider = Mock() mock_dns_provider.wait_for_dns_change = Mock(return_value=True) mock_authz = Mock() + mock_sleep.return_value = False mock_authz.dns_challenge.response = Mock() mock_authz.dns_challenge.response.simple_verify = Mock(return_value=True) mock_authz.authz = [] @@ -179,7 +183,7 @@ class TestAcme(unittest.TestCase): options = { "common_name": "test.netflix.net", "extensions": { - "sub_alt_names": {"names": ["test2.netflix.net", "test3.netflix.net"]} + "sub_alt_names": {"names": [DNSName("test2.netflix.net"), DNSName("test3.netflix.net")]} }, } result = self.acme.get_domains(options) @@ -187,6 +191,19 @@ class TestAcme(unittest.TestCase): result, [options["common_name"], "test2.netflix.net", "test3.netflix.net"] ) + @patch("lemur.plugins.lemur_acme.plugin.current_app") + def test_get_domains_san(self, mock_current_app): + options = { + "common_name": "test.netflix.net", + "extensions": { + "sub_alt_names": {"names": [DNSName("test.netflix.net"), DNSName("test2.netflix.net")]} + }, + } + result = self.acme.get_domains(options) + self.assertEqual( + result, [options["common_name"], "test2.netflix.net"] + ) + @patch( "lemur.plugins.lemur_acme.plugin.AcmeHandler.start_dns_challenge", return_value="test", From 6c7bb5f9b73fe9bca34bfadb2f8a1eae01a5bcd4 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Thu, 13 Feb 2020 07:35:35 +0100 Subject: [PATCH 05/17] Fixed TLS secret format ( #2913 ) The Plugin handled the TLS secret format wrong: it sent chain certificate instead of requested public certificate #2913 --- lemur/plugins/lemur_kubernetes/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_kubernetes/plugin.py b/lemur/plugins/lemur_kubernetes/plugin.py index 62ffffda..f7ff00f7 100644 --- a/lemur/plugins/lemur_kubernetes/plugin.py +++ b/lemur/plugins/lemur_kubernetes/plugin.py @@ -96,7 +96,7 @@ def build_secret(secret_format, secret_name, body, private_key, cert_chain): if secret_format == "TLS": secret["type"] = "kubernetes.io/tls" secret["data"] = { - "tls.crt": base64encode(cert_chain), + "tls.crt": base64encode(body), "tls.key": base64encode(private_key), } if secret_format == "Certificate": From 571c8bf42d83f32ee808840edd2287a665a2f6fc Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Thu, 13 Feb 2020 07:38:04 +0100 Subject: [PATCH 06/17] Error when validity_end date is empty #2905 this lines of code (114ff) in threw an error, when the validity_end date was empty: if options.get("validity_end") > arrow.utcnow().shift(years=2): raise Exception( "Verisign issued certificates cannot exceed two years in validity" ) Actually, they are not needed, because immidiately following is a check for an empty validity_end and for the length of the entered period. When I commented it out for testing, the error was gone and everything worked as expected. --- lemur/plugins/lemur_verisign/plugin.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 7bf517b7..6a49364f 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -111,11 +111,7 @@ def process_options(options): data["subject_alt_names"] = ",".join(get_additional_names(options)) - if options.get("validity_end") > arrow.utcnow().shift(years=2): - raise Exception( - "Verisign issued certificates cannot exceed two years in validity" - ) - + if options.get("validity_end"): # VeriSign (Symantec) only accepts strictly smaller than 2 year end date if options.get("validity_end") < arrow.utcnow().shift(years=2, days=-1): From 2b849a65205a00ea129247263c4ac300f8ebd25d Mon Sep 17 00:00:00 2001 From: Hossein Shafagh Date: Thu, 13 Feb 2020 15:58:07 -0800 Subject: [PATCH 07/17] Update plugin.py making lint happy --- lemur/plugins/lemur_verisign/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 6a49364f..3e7c383f 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -110,7 +110,6 @@ def process_options(options): } data["subject_alt_names"] = ",".join(get_additional_names(options)) - if options.get("validity_end"): # VeriSign (Symantec) only accepts strictly smaller than 2 year end date From af21225918929e936ed5781246c3cca05fe44d4d Mon Sep 17 00:00:00 2001 From: csine-nflx Date: Thu, 13 Feb 2020 16:38:33 -0800 Subject: [PATCH 08/17] adding logging on sucess and metric submission of URL for certificate issuance --- lemur/plugins/lemur_acme/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_acme/plugin.py b/lemur/plugins/lemur_acme/plugin.py index 54493f97..179285b3 100644 --- a/lemur/plugins/lemur_acme/plugin.py +++ b/lemur/plugins/lemur_acme/plugin.py @@ -172,7 +172,7 @@ class AcmeHandler(object): except (AcmeError, TimeoutError): sentry.captureException(extra={"order_url": str(order.uri)}) - metrics.send("request_certificate_error", "counter", 1) + metrics.send("request_certificate_error", "counter", 1, metric_tags={"uri": order.uri}) current_app.logger.error( f"Unable to resolve Acme order: {order.uri}", exc_info=True ) @@ -183,7 +183,8 @@ class AcmeHandler(object): else: raise - current_app.logger.debug( + metrics.send("request_certificate_success", "counter", 1, metric_tags={"uri": order.uri}) + current_app.logger.info( f"Successfully resolved Acme order: {order.uri}", exc_info=True ) From 8e3cc93d6ae64bd6b75b1d54a7903d87052bb96f Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Fri, 14 Feb 2020 07:50:18 +0100 Subject: [PATCH 09/17] Whitespaces in empty line 113 removed --- lemur/plugins/lemur_verisign/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 3e7c383f..a0e2d1cb 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -110,7 +110,7 @@ def process_options(options): } data["subject_alt_names"] = ",".join(get_additional_names(options)) - + if options.get("validity_end"): # VeriSign (Symantec) only accepts strictly smaller than 2 year end date if options.get("validity_end") < arrow.utcnow().shift(years=2, days=-1): From fabcad1e46c44d86c7dfe69204fdf45cbf1c189c Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Sat, 15 Feb 2020 15:52:24 +0100 Subject: [PATCH 10/17] New variable VERISIGN_PRODUCT_(authority.name) If there is a config variable with VERISIGN_PRODUCT_ take the value as Cert product-type else default to "Server", to be compatoible with former versions. This enables the use of different Verisign authorities for differnt cert-products eg. EV or Standard Certs --- lemur/plugins/lemur_verisign/plugin.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index a0e2d1cb..c74a71f1 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -98,10 +98,18 @@ def process_options(options): :param options: :return: dict or valid verisign options """ + + # if there is a config variable with VERISIGN_PRODUCT_ take the value as Cert product-type + # else default to "Server", to be compatoible with former versions + authority = options.get("authority").name.upper() + product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority)) + if product_type is None: + product_type ="Server" + data = { "challenge": get_psuedo_random_string(), "serverType": "Apache", - "certProductType": "Server", + "certProductType": product_type, "firstName": current_app.config.get("VERISIGN_FIRST_NAME"), "lastName": current_app.config.get("VERISIGN_LAST_NAME"), "signatureAlgorithm": "sha256WithRSAEncryption", From bfa953270d3840b9d96807b69c30466138f89b39 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:04:44 +0100 Subject: [PATCH 11/17] Fixed whitespace error --- lemur/plugins/lemur_verisign/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index c74a71f1..cd716e84 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -104,7 +104,7 @@ def process_options(options): authority = options.get("authority").name.upper() product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority)) if product_type is None: - product_type ="Server" + product_type = "Server" data = { "challenge": get_psuedo_random_string(), From 3693bc2d8be12cafce118c196fadf72e8606d672 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:09:25 +0100 Subject: [PATCH 12/17] removed whitespaces inserted by online editor --- lemur/plugins/lemur_verisign/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index cd716e84..ca606baf 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -98,14 +98,14 @@ def process_options(options): :param options: :return: dict or valid verisign options """ - + # if there is a config variable with VERISIGN_PRODUCT_ take the value as Cert product-type # else default to "Server", to be compatoible with former versions authority = options.get("authority").name.upper() product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority)) if product_type is None: product_type = "Server" - + data = { "challenge": get_psuedo_random_string(), "serverType": "Apache", From a70a49e4e9cffa757a608022e51e14646d766513 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Sat, 15 Feb 2020 16:11:58 +0100 Subject: [PATCH 13/17] Update plugin.py --- lemur/plugins/lemur_verisign/plugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index ca606baf..6d9182df 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -98,14 +98,12 @@ def process_options(options): :param options: :return: dict or valid verisign options """ - # if there is a config variable with VERISIGN_PRODUCT_ take the value as Cert product-type # else default to "Server", to be compatoible with former versions authority = options.get("authority").name.upper() product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority)) if product_type is None: product_type = "Server" - data = { "challenge": get_psuedo_random_string(), "serverType": "Apache", From 1815c8997064130e903e172a7afc4a13df23f714 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Sun, 16 Feb 2020 09:28:52 +0100 Subject: [PATCH 14/17] Made the change more elegant As suggested by @hosseinsh. This is of course more elegant. --- lemur/plugins/lemur_verisign/plugin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 6d9182df..0864657a 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -101,9 +101,7 @@ def process_options(options): # if there is a config variable with VERISIGN_PRODUCT_ take the value as Cert product-type # else default to "Server", to be compatoible with former versions authority = options.get("authority").name.upper() - product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority)) - if product_type is None: - product_type = "Server" + product_type = current_app.config.get("VERISIGN_PRODUCT_{0}".format(authority), "Server") data = { "challenge": get_psuedo_random_string(), "serverType": "Apache", From 3fd0d3e1416fcb79aedb122c83c8c23213967e11 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Mon, 17 Feb 2020 12:40:36 +0100 Subject: [PATCH 15/17] Added VERISIGN_INTERMEDIATE_ parameter When using the VERISIGN_PRODUCT_ Parameter one also has to add this parameter: VERISIGN_INTERMEDIATE_ = """ """ While doing this, I also added code, so the external_id field is filled with data from CA-Answer --- lemur/plugins/lemur_verisign/plugin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 0864657a..2d108071 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -209,7 +209,7 @@ class VerisignIssuerPlugin(IssuerPlugin): response = self.session.post(url, data=data) try: - cert = handle_response(response.content)["Response"]["Certificate"] + response_dict = handle_response(response.content) except KeyError: metrics.send( "verisign_create_certificate_error", @@ -221,8 +221,12 @@ class VerisignIssuerPlugin(IssuerPlugin): extra={"common_name": issuer_options.get("common_name", "")} ) raise Exception(f"Error with Verisign: {response.content}") - # TODO add external id - return cert, current_app.config.get("VERISIGN_INTERMEDIATE"), None + # DONE: TODO add external id + authority = issuer_options.get("authority").name.upper() + cert = response_dict['Response']['Certificate'] + external_id = response_dict['Response']['Transaction_ID'] + chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE")) + return cert, chain, external_id @staticmethod def create_authority(options): From ed3472d029c88b678e1e9e5104151bd888b207b4 Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Mon, 17 Feb 2020 15:21:29 +0100 Subject: [PATCH 16/17] Update plugin.py --- lemur/plugins/lemur_verisign/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 2d108071..479f3215 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -209,7 +209,7 @@ class VerisignIssuerPlugin(IssuerPlugin): response = self.session.post(url, data=data) try: - response_dict = handle_response(response.content) + response_dict = handle_response(response.content) except KeyError: metrics.send( "verisign_create_certificate_error", @@ -225,7 +225,7 @@ class VerisignIssuerPlugin(IssuerPlugin): authority = issuer_options.get("authority").name.upper() cert = response_dict['Response']['Certificate'] external_id = response_dict['Response']['Transaction_ID'] - chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE")) + chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE")) return cert, chain, external_id @staticmethod From e75df1ddc92a79dc197c698d38078f8c3d80157a Mon Sep 17 00:00:00 2001 From: sirferl <41906265+sirferl@users.noreply.github.com> Date: Mon, 17 Feb 2020 19:04:20 +0100 Subject: [PATCH 17/17] Update plugin.py --- lemur/plugins/lemur_verisign/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lemur/plugins/lemur_verisign/plugin.py b/lemur/plugins/lemur_verisign/plugin.py index 479f3215..f913861c 100644 --- a/lemur/plugins/lemur_verisign/plugin.py +++ b/lemur/plugins/lemur_verisign/plugin.py @@ -221,10 +221,11 @@ class VerisignIssuerPlugin(IssuerPlugin): extra={"common_name": issuer_options.get("common_name", "")} ) raise Exception(f"Error with Verisign: {response.content}") - # DONE: TODO add external id authority = issuer_options.get("authority").name.upper() cert = response_dict['Response']['Certificate'] - external_id = response_dict['Response']['Transaction_ID'] + external_id = None + if 'Transaction_ID' in response_dict['Response'].keys(): + external_id = response_dict['Response']['Transaction_ID'] chain = current_app.config.get("VERISIGN_INTERMEDIATE_{0}".format(authority), current_app.config.get("VERISIGN_INTERMEDIATE")) return cert, chain, external_id