Black lint all the things
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
try:
|
||||
VERSION = __import__('pkg_resources') \
|
||||
.get_distribution(__name__).version
|
||||
VERSION = __import__("pkg_resources").get_distribution(__name__).version
|
||||
except Exception as e:
|
||||
VERSION = 'unknown'
|
||||
VERSION = "unknown"
|
||||
|
@@ -5,24 +5,24 @@ from flask import current_app
|
||||
|
||||
|
||||
def cf_api_call():
|
||||
cf_key = current_app.config.get('ACME_CLOUDFLARE_KEY', '')
|
||||
cf_email = current_app.config.get('ACME_CLOUDFLARE_EMAIL', '')
|
||||
cf_key = current_app.config.get("ACME_CLOUDFLARE_KEY", "")
|
||||
cf_email = current_app.config.get("ACME_CLOUDFLARE_EMAIL", "")
|
||||
return CloudFlare.CloudFlare(email=cf_email, token=cf_key)
|
||||
|
||||
|
||||
def find_zone_id(host):
|
||||
elements = host.split('.')
|
||||
elements = host.split(".")
|
||||
cf = cf_api_call()
|
||||
|
||||
n = 1
|
||||
|
||||
while n < 5:
|
||||
n = n + 1
|
||||
domain = '.'.join(elements[-n:])
|
||||
domain = ".".join(elements[-n:])
|
||||
current_app.logger.debug("Trying to get ID for zone {0}".format(domain))
|
||||
|
||||
try:
|
||||
zone = cf.zones.get(params={'name': domain, 'per_page': 1})
|
||||
zone = cf.zones.get(params={"name": domain, "per_page": 1})
|
||||
except Exception as e:
|
||||
current_app.logger.error("Cloudflare API error: %s" % e)
|
||||
pass
|
||||
@@ -31,10 +31,10 @@ def find_zone_id(host):
|
||||
break
|
||||
|
||||
if len(zone) == 0:
|
||||
current_app.logger.error('No zone found')
|
||||
current_app.logger.error("No zone found")
|
||||
return
|
||||
else:
|
||||
return zone[0]['id']
|
||||
return zone[0]["id"]
|
||||
|
||||
|
||||
def wait_for_dns_change(change_id, account_number=None):
|
||||
@@ -42,8 +42,8 @@ def wait_for_dns_change(change_id, account_number=None):
|
||||
zone_id, record_id = change_id
|
||||
while True:
|
||||
r = cf.zones.get(zone_id, record_id)
|
||||
current_app.logger.debug("Record status: %s" % r['status'])
|
||||
if r['status'] == 'active':
|
||||
current_app.logger.debug("Record status: %s" % r["status"])
|
||||
if r["status"] == "active":
|
||||
break
|
||||
time.sleep(1)
|
||||
return
|
||||
@@ -55,15 +55,19 @@ def create_txt_record(host, value, account_number):
|
||||
if not zone_id:
|
||||
return
|
||||
|
||||
txt_record = {'name': host, 'type': 'TXT', 'content': value}
|
||||
txt_record = {"name": host, "type": "TXT", "content": value}
|
||||
|
||||
current_app.logger.debug("Creating TXT record {0} with value {1}".format(host, value))
|
||||
current_app.logger.debug(
|
||||
"Creating TXT record {0} with value {1}".format(host, value)
|
||||
)
|
||||
|
||||
try:
|
||||
r = cf.zones.dns_records.post(zone_id, data=txt_record)
|
||||
except Exception as e:
|
||||
current_app.logger.error('/zones.dns_records.post %s: %s' % (txt_record['name'], e))
|
||||
return zone_id, r['id']
|
||||
current_app.logger.error(
|
||||
"/zones.dns_records.post %s: %s" % (txt_record["name"], e)
|
||||
)
|
||||
return zone_id, r["id"]
|
||||
|
||||
|
||||
def delete_txt_record(change_ids, account_number, host, value):
|
||||
@@ -74,4 +78,4 @@ def delete_txt_record(change_ids, account_number, host, value):
|
||||
try:
|
||||
cf.zones.dns_records.delete(zone_id, record_id)
|
||||
except Exception as e:
|
||||
current_app.logger.error('/zones.dns_records.post: %s' % e)
|
||||
current_app.logger.error("/zones.dns_records.post: %s" % e)
|
||||
|
@@ -5,7 +5,12 @@ import dns.exception
|
||||
import dns.name
|
||||
import dns.query
|
||||
import dns.resolver
|
||||
from dyn.tm.errors import DynectCreateError, DynectDeleteError, DynectGetError, DynectUpdateError
|
||||
from dyn.tm.errors import (
|
||||
DynectCreateError,
|
||||
DynectDeleteError,
|
||||
DynectGetError,
|
||||
DynectUpdateError,
|
||||
)
|
||||
from dyn.tm.session import DynectSession
|
||||
from dyn.tm.zones import Node, Zone, get_all_zones
|
||||
from flask import current_app
|
||||
@@ -16,13 +21,13 @@ from lemur.extensions import metrics, sentry
|
||||
def get_dynect_session():
|
||||
try:
|
||||
dynect_session = DynectSession(
|
||||
current_app.config.get('ACME_DYN_CUSTOMER_NAME', ''),
|
||||
current_app.config.get('ACME_DYN_USERNAME', ''),
|
||||
current_app.config.get('ACME_DYN_PASSWORD', ''),
|
||||
current_app.config.get("ACME_DYN_CUSTOMER_NAME", ""),
|
||||
current_app.config.get("ACME_DYN_USERNAME", ""),
|
||||
current_app.config.get("ACME_DYN_PASSWORD", ""),
|
||||
)
|
||||
except Exception as e:
|
||||
sentry.captureException()
|
||||
metrics.send('get_dynect_session_fail', 'counter', 1)
|
||||
metrics.send("get_dynect_session_fail", "counter", 1)
|
||||
current_app.logger.debug("Unable to establish connection to Dyn", exc_info=True)
|
||||
raise
|
||||
return dynect_session
|
||||
@@ -33,17 +38,17 @@ def _has_dns_propagated(name, token):
|
||||
try:
|
||||
dns_resolver = dns.resolver.Resolver()
|
||||
dns_resolver.nameservers = [get_authoritative_nameserver(name)]
|
||||
dns_response = dns_resolver.query(name, 'TXT')
|
||||
dns_response = dns_resolver.query(name, "TXT")
|
||||
for rdata in dns_response:
|
||||
for txt_record in rdata.strings:
|
||||
txt_records.append(txt_record.decode("utf-8"))
|
||||
except dns.exception.DNSException:
|
||||
metrics.send('has_dns_propagated_fail', 'counter', 1)
|
||||
metrics.send("has_dns_propagated_fail", "counter", 1)
|
||||
return False
|
||||
|
||||
for txt_record in txt_records:
|
||||
if txt_record == token:
|
||||
metrics.send('has_dns_propagated_success', 'counter', 1)
|
||||
metrics.send("has_dns_propagated_success", "counter", 1)
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -56,18 +61,19 @@ def wait_for_dns_change(change_id, account_number=None):
|
||||
status = _has_dns_propagated(fqdn, token)
|
||||
current_app.logger.debug("Record status for fqdn: {}: {}".format(fqdn, status))
|
||||
if status:
|
||||
metrics.send('wait_for_dns_change_success', 'counter', 1)
|
||||
metrics.send("wait_for_dns_change_success", "counter", 1)
|
||||
break
|
||||
time.sleep(10)
|
||||
if not status:
|
||||
# TODO: Delete associated DNS text record here
|
||||
metrics.send('wait_for_dns_change_fail', 'counter', 1)
|
||||
sentry.captureException(
|
||||
extra={
|
||||
"fqdn": str(fqdn), "txt_record": str(token)}
|
||||
metrics.send("wait_for_dns_change_fail", "counter", 1)
|
||||
sentry.captureException(extra={"fqdn": str(fqdn), "txt_record": str(token)})
|
||||
metrics.send(
|
||||
"wait_for_dns_change_error",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"fqdn": fqdn, "txt_record": token},
|
||||
)
|
||||
metrics.send('wait_for_dns_change_error', 'counter', 1,
|
||||
metric_tags={'fqdn': fqdn, 'txt_record': token})
|
||||
return
|
||||
|
||||
|
||||
@@ -84,7 +90,7 @@ def get_zone_name(domain):
|
||||
if z.name.count(".") > zone_name.count("."):
|
||||
zone_name = z.name
|
||||
if not zone_name:
|
||||
metrics.send('dyn_no_zone_name', 'counter', 1)
|
||||
metrics.send("dyn_no_zone_name", "counter", 1)
|
||||
raise Exception("No Dyn zone found for domain: {}".format(domain))
|
||||
return zone_name
|
||||
|
||||
@@ -101,23 +107,28 @@ def get_zones(account_number):
|
||||
def create_txt_record(domain, token, account_number):
|
||||
get_dynect_session()
|
||||
zone_name = get_zone_name(domain)
|
||||
zone_parts = len(zone_name.split('.'))
|
||||
node_name = '.'.join(domain.split('.')[:-zone_parts])
|
||||
zone_parts = len(zone_name.split("."))
|
||||
node_name = ".".join(domain.split(".")[:-zone_parts])
|
||||
fqdn = "{0}.{1}".format(node_name, zone_name)
|
||||
zone = Zone(zone_name)
|
||||
|
||||
try:
|
||||
zone.add_record(node_name, record_type='TXT', txtdata="\"{}\"".format(token), ttl=5)
|
||||
zone.add_record(
|
||||
node_name, record_type="TXT", txtdata='"{}"'.format(token), ttl=5
|
||||
)
|
||||
zone.publish()
|
||||
current_app.logger.debug("TXT record created: {0}, token: {1}".format(fqdn, token))
|
||||
current_app.logger.debug(
|
||||
"TXT record created: {0}, token: {1}".format(fqdn, token)
|
||||
)
|
||||
except (DynectCreateError, DynectUpdateError) as e:
|
||||
if "Cannot duplicate existing record data" in e.message:
|
||||
current_app.logger.debug(
|
||||
"Unable to add record. Domain: {}. Token: {}. "
|
||||
"Record already exists: {}".format(domain, token, e), exc_info=True
|
||||
"Record already exists: {}".format(domain, token, e),
|
||||
exc_info=True,
|
||||
)
|
||||
else:
|
||||
metrics.send('create_txt_record_error', 'counter', 1)
|
||||
metrics.send("create_txt_record_error", "counter", 1)
|
||||
sentry.captureException()
|
||||
raise
|
||||
|
||||
@@ -132,17 +143,17 @@ def delete_txt_record(change_id, account_number, domain, token):
|
||||
return
|
||||
|
||||
zone_name = get_zone_name(domain)
|
||||
zone_parts = len(zone_name.split('.'))
|
||||
node_name = '.'.join(domain.split('.')[:-zone_parts])
|
||||
zone_parts = len(zone_name.split("."))
|
||||
node_name = ".".join(domain.split(".")[:-zone_parts])
|
||||
fqdn = "{0}.{1}".format(node_name, zone_name)
|
||||
|
||||
zone = Zone(zone_name)
|
||||
node = Node(zone_name, fqdn)
|
||||
|
||||
try:
|
||||
all_txt_records = node.get_all_records_by_type('TXT')
|
||||
all_txt_records = node.get_all_records_by_type("TXT")
|
||||
except DynectGetError:
|
||||
metrics.send('delete_txt_record_geterror', 'counter', 1)
|
||||
metrics.send("delete_txt_record_geterror", "counter", 1)
|
||||
# No Text Records remain or host is not in the zone anymore because all records have been deleted.
|
||||
return
|
||||
for txt_record in all_txt_records:
|
||||
@@ -153,22 +164,36 @@ def delete_txt_record(change_id, account_number, domain, token):
|
||||
except DynectDeleteError:
|
||||
sentry.captureException(
|
||||
extra={
|
||||
"fqdn": str(fqdn), "zone_name": str(zone_name), "node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata)}
|
||||
"fqdn": str(fqdn),
|
||||
"zone_name": str(zone_name),
|
||||
"node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata),
|
||||
}
|
||||
)
|
||||
metrics.send(
|
||||
"delete_txt_record_deleteerror",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"fqdn": fqdn, "txt_record": txt_record.txtdata},
|
||||
)
|
||||
metrics.send('delete_txt_record_deleteerror', 'counter', 1,
|
||||
metric_tags={'fqdn': fqdn, 'txt_record': txt_record.txtdata})
|
||||
|
||||
try:
|
||||
zone.publish()
|
||||
except DynectUpdateError:
|
||||
sentry.captureException(
|
||||
extra={
|
||||
"fqdn": str(fqdn), "zone_name": str(zone_name), "node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata)}
|
||||
"fqdn": str(fqdn),
|
||||
"zone_name": str(zone_name),
|
||||
"node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata),
|
||||
}
|
||||
)
|
||||
metrics.send(
|
||||
"delete_txt_record_publish_error",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"fqdn": str(fqdn), "txt_record": str(txt_record.txtdata)},
|
||||
)
|
||||
metrics.send('delete_txt_record_publish_error', 'counter', 1,
|
||||
metric_tags={'fqdn': str(fqdn), 'txt_record': str(txt_record.txtdata)})
|
||||
|
||||
|
||||
def delete_acme_txt_records(domain):
|
||||
@@ -180,18 +205,21 @@ def delete_acme_txt_records(domain):
|
||||
if not domain.startswith(acme_challenge_string):
|
||||
current_app.logger.debug(
|
||||
"delete_acme_txt_records: Domain {} doesn't start with string {}. "
|
||||
"Cowardly refusing to delete TXT records".format(domain, acme_challenge_string))
|
||||
"Cowardly refusing to delete TXT records".format(
|
||||
domain, acme_challenge_string
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
zone_name = get_zone_name(domain)
|
||||
zone_parts = len(zone_name.split('.'))
|
||||
node_name = '.'.join(domain.split('.')[:-zone_parts])
|
||||
zone_parts = len(zone_name.split("."))
|
||||
node_name = ".".join(domain.split(".")[:-zone_parts])
|
||||
fqdn = "{0}.{1}".format(node_name, zone_name)
|
||||
|
||||
zone = Zone(zone_name)
|
||||
node = Node(zone_name, fqdn)
|
||||
|
||||
all_txt_records = node.get_all_records_by_type('TXT')
|
||||
all_txt_records = node.get_all_records_by_type("TXT")
|
||||
for txt_record in all_txt_records:
|
||||
current_app.logger.debug("Deleting TXT record name: {0}".format(fqdn))
|
||||
try:
|
||||
@@ -199,16 +227,23 @@ def delete_acme_txt_records(domain):
|
||||
except DynectDeleteError:
|
||||
sentry.captureException(
|
||||
extra={
|
||||
"fqdn": str(fqdn), "zone_name": str(zone_name), "node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata)}
|
||||
"fqdn": str(fqdn),
|
||||
"zone_name": str(zone_name),
|
||||
"node_name": str(node_name),
|
||||
"txt_record": str(txt_record.txtdata),
|
||||
}
|
||||
)
|
||||
metrics.send(
|
||||
"delete_txt_record_deleteerror",
|
||||
"counter",
|
||||
1,
|
||||
metric_tags={"fqdn": fqdn, "txt_record": txt_record.txtdata},
|
||||
)
|
||||
metrics.send('delete_txt_record_deleteerror', 'counter', 1,
|
||||
metric_tags={'fqdn': fqdn, 'txt_record': txt_record.txtdata})
|
||||
zone.publish()
|
||||
|
||||
|
||||
def get_authoritative_nameserver(domain):
|
||||
if current_app.config.get('ACME_DYN_GET_AUTHORATATIVE_NAMESERVER'):
|
||||
if current_app.config.get("ACME_DYN_GET_AUTHORATATIVE_NAMESERVER"):
|
||||
n = dns.name.from_text(domain)
|
||||
|
||||
depth = 2
|
||||
@@ -219,7 +254,7 @@ def get_authoritative_nameserver(domain):
|
||||
while not last:
|
||||
s = n.split(depth)
|
||||
|
||||
last = s[0].to_unicode() == u'@'
|
||||
last = s[0].to_unicode() == u"@"
|
||||
sub = s[1]
|
||||
|
||||
query = dns.message.make_query(sub, dns.rdatatype.NS)
|
||||
@@ -227,11 +262,11 @@ def get_authoritative_nameserver(domain):
|
||||
|
||||
rcode = response.rcode()
|
||||
if rcode != dns.rcode.NOERROR:
|
||||
metrics.send('get_authoritative_nameserver_error', 'counter', 1)
|
||||
metrics.send("get_authoritative_nameserver_error", "counter", 1)
|
||||
if rcode == dns.rcode.NXDOMAIN:
|
||||
raise Exception('%s does not exist.' % sub)
|
||||
raise Exception("%s does not exist." % sub)
|
||||
else:
|
||||
raise Exception('Error %s' % dns.rcode.to_text(rcode))
|
||||
raise Exception("Error %s" % dns.rcode.to_text(rcode))
|
||||
|
||||
if len(response.authority) > 0:
|
||||
rrset = response.authority[0]
|
||||
|
@@ -48,7 +48,7 @@ class AcmeHandler(object):
|
||||
try:
|
||||
self.all_dns_providers = dns_provider_service.get_all_dns_providers()
|
||||
except Exception as e:
|
||||
metrics.send('AcmeHandler_init_error', 'counter', 1)
|
||||
metrics.send("AcmeHandler_init_error", "counter", 1)
|
||||
sentry.captureException()
|
||||
current_app.logger.error(f"Unable to fetch DNS Providers: {e}")
|
||||
self.all_dns_providers = []
|
||||
@@ -67,45 +67,60 @@ class AcmeHandler(object):
|
||||
return host.replace("*.", "")
|
||||
|
||||
def maybe_add_extension(self, host, dns_provider_options):
|
||||
if dns_provider_options and dns_provider_options.get("acme_challenge_extension"):
|
||||
if dns_provider_options and dns_provider_options.get(
|
||||
"acme_challenge_extension"
|
||||
):
|
||||
host = host + dns_provider_options.get("acme_challenge_extension")
|
||||
return host
|
||||
|
||||
def start_dns_challenge(self, acme_client, account_number, host, dns_provider, order, dns_provider_options):
|
||||
def start_dns_challenge(
|
||||
self,
|
||||
acme_client,
|
||||
account_number,
|
||||
host,
|
||||
dns_provider,
|
||||
order,
|
||||
dns_provider_options,
|
||||
):
|
||||
current_app.logger.debug("Starting DNS challenge for {0}".format(host))
|
||||
|
||||
change_ids = []
|
||||
|
||||
host_to_validate = self.maybe_remove_wildcard(host)
|
||||
dns_challenges = self.find_dns_challenge(host_to_validate, order.authorizations)
|
||||
host_to_validate = self.maybe_add_extension(host_to_validate, dns_provider_options)
|
||||
host_to_validate = self.maybe_add_extension(
|
||||
host_to_validate, dns_provider_options
|
||||
)
|
||||
|
||||
if not dns_challenges:
|
||||
sentry.captureException()
|
||||
metrics.send('start_dns_challenge_error_no_dns_challenges', 'counter', 1)
|
||||
metrics.send("start_dns_challenge_error_no_dns_challenges", "counter", 1)
|
||||
raise Exception("Unable to determine DNS challenges from authorizations")
|
||||
|
||||
for dns_challenge in dns_challenges:
|
||||
change_id = dns_provider.create_txt_record(
|
||||
dns_challenge.validation_domain_name(host_to_validate),
|
||||
dns_challenge.validation(acme_client.client.net.key),
|
||||
account_number
|
||||
account_number,
|
||||
)
|
||||
change_ids.append(change_id)
|
||||
|
||||
return AuthorizationRecord(
|
||||
host,
|
||||
order.authorizations,
|
||||
dns_challenges,
|
||||
change_ids
|
||||
host, order.authorizations, dns_challenges, change_ids
|
||||
)
|
||||
|
||||
def complete_dns_challenge(self, acme_client, authz_record):
|
||||
current_app.logger.debug("Finalizing DNS challenge for {0}".format(authz_record.authz[0].body.identifier.value))
|
||||
current_app.logger.debug(
|
||||
"Finalizing DNS challenge for {0}".format(
|
||||
authz_record.authz[0].body.identifier.value
|
||||
)
|
||||
)
|
||||
dns_providers = self.dns_providers_for_domain.get(authz_record.host)
|
||||
if not dns_providers:
|
||||
metrics.send('complete_dns_challenge_error_no_dnsproviders', 'counter', 1)
|
||||
raise Exception("No DNS providers found for domain: {}".format(authz_record.host))
|
||||
metrics.send("complete_dns_challenge_error_no_dnsproviders", "counter", 1)
|
||||
raise Exception(
|
||||
"No DNS providers found for domain: {}".format(authz_record.host)
|
||||
)
|
||||
|
||||
for dns_provider in dns_providers:
|
||||
# Grab account number (For Route53)
|
||||
@@ -114,13 +129,17 @@ class AcmeHandler(object):
|
||||
dns_provider_plugin = self.get_dns_provider(dns_provider.provider_type)
|
||||
for change_id in authz_record.change_id:
|
||||
try:
|
||||
dns_provider_plugin.wait_for_dns_change(change_id, account_number=account_number)
|
||||
dns_provider_plugin.wait_for_dns_change(
|
||||
change_id, account_number=account_number
|
||||
)
|
||||
except Exception:
|
||||
metrics.send('complete_dns_challenge_error', 'counter', 1)
|
||||
metrics.send("complete_dns_challenge_error", "counter", 1)
|
||||
sentry.captureException()
|
||||
current_app.logger.debug(
|
||||
f"Unable to resolve DNS challenge for change_id: {change_id}, account_id: "
|
||||
f"{account_number}", exc_info=True)
|
||||
f"{account_number}",
|
||||
exc_info=True,
|
||||
)
|
||||
raise
|
||||
|
||||
for dns_challenge in authz_record.dns_challenge:
|
||||
@@ -129,11 +148,11 @@ class AcmeHandler(object):
|
||||
verified = response.simple_verify(
|
||||
dns_challenge.chall,
|
||||
authz_record.host,
|
||||
acme_client.client.net.key.public_key()
|
||||
acme_client.client.net.key.public_key(),
|
||||
)
|
||||
|
||||
if not verified:
|
||||
metrics.send('complete_dns_challenge_verification_error', 'counter', 1)
|
||||
metrics.send("complete_dns_challenge_verification_error", "counter", 1)
|
||||
raise ValueError("Failed verification")
|
||||
|
||||
time.sleep(5)
|
||||
@@ -152,8 +171,10 @@ class AcmeHandler(object):
|
||||
|
||||
except (AcmeError, TimeoutError):
|
||||
sentry.captureException(extra={"order_url": str(order.uri)})
|
||||
metrics.send('request_certificate_error', 'counter', 1)
|
||||
current_app.logger.error(f"Unable to resolve Acme order: {order.uri}", exc_info=True)
|
||||
metrics.send("request_certificate_error", "counter", 1)
|
||||
current_app.logger.error(
|
||||
f"Unable to resolve Acme order: {order.uri}", exc_info=True
|
||||
)
|
||||
raise
|
||||
except errors.ValidationError:
|
||||
if order.fullchain_pem:
|
||||
@@ -161,12 +182,19 @@ class AcmeHandler(object):
|
||||
else:
|
||||
raise
|
||||
|
||||
pem_certificate = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
|
||||
OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
|
||||
orderr.fullchain_pem)).decode()
|
||||
pem_certificate_chain = orderr.fullchain_pem[len(pem_certificate):].lstrip()
|
||||
pem_certificate = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM,
|
||||
OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, orderr.fullchain_pem
|
||||
),
|
||||
).decode()
|
||||
pem_certificate_chain = orderr.fullchain_pem[
|
||||
len(pem_certificate) : # noqa
|
||||
].lstrip()
|
||||
|
||||
current_app.logger.debug("{0} {1}".format(type(pem_certificate), type(pem_certificate_chain)))
|
||||
current_app.logger.debug(
|
||||
"{0} {1}".format(type(pem_certificate), type(pem_certificate_chain))
|
||||
)
|
||||
return pem_certificate, pem_certificate_chain
|
||||
|
||||
def setup_acme_client(self, authority):
|
||||
@@ -176,30 +204,40 @@ class AcmeHandler(object):
|
||||
|
||||
for option in json.loads(authority.options):
|
||||
options[option["name"]] = option.get("value")
|
||||
email = options.get('email', current_app.config.get('ACME_EMAIL'))
|
||||
tel = options.get('telephone', current_app.config.get('ACME_TEL'))
|
||||
directory_url = options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))
|
||||
email = options.get("email", current_app.config.get("ACME_EMAIL"))
|
||||
tel = options.get("telephone", current_app.config.get("ACME_TEL"))
|
||||
directory_url = options.get(
|
||||
"acme_url", current_app.config.get("ACME_DIRECTORY_URL")
|
||||
)
|
||||
|
||||
existing_key = options.get('acme_private_key', current_app.config.get('ACME_PRIVATE_KEY'))
|
||||
existing_regr = options.get('acme_regr', current_app.config.get('ACME_REGR'))
|
||||
existing_key = options.get(
|
||||
"acme_private_key", current_app.config.get("ACME_PRIVATE_KEY")
|
||||
)
|
||||
existing_regr = options.get("acme_regr", current_app.config.get("ACME_REGR"))
|
||||
|
||||
if existing_key and existing_regr:
|
||||
# Reuse the same account for each certificate issuance
|
||||
key = jose.JWK.json_loads(existing_key)
|
||||
regr = messages.RegistrationResource.json_loads(existing_regr)
|
||||
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
|
||||
current_app.logger.debug(
|
||||
"Connecting with directory at {0}".format(directory_url)
|
||||
)
|
||||
net = ClientNetwork(key, account=regr)
|
||||
client = BackwardsCompatibleClientV2(net, key, directory_url)
|
||||
return client, {}
|
||||
else:
|
||||
# Create an account for each certificate issuance
|
||||
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
|
||||
key = jose.JWKRSA(key=generate_private_key("RSA2048"))
|
||||
|
||||
current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
|
||||
current_app.logger.debug(
|
||||
"Connecting with directory at {0}".format(directory_url)
|
||||
)
|
||||
|
||||
net = ClientNetwork(key, account=None, timeout=3600)
|
||||
client = BackwardsCompatibleClientV2(net, key, directory_url)
|
||||
registration = client.new_account_and_tos(messages.NewRegistration.from_data(email=email))
|
||||
registration = client.new_account_and_tos(
|
||||
messages.NewRegistration.from_data(email=email)
|
||||
)
|
||||
current_app.logger.debug("Connected: {0}".format(registration.uri))
|
||||
|
||||
return client, registration
|
||||
@@ -212,9 +250,9 @@ class AcmeHandler(object):
|
||||
"""
|
||||
current_app.logger.debug("Fetching domains")
|
||||
|
||||
domains = [options['common_name']]
|
||||
if options.get('extensions'):
|
||||
for name in options['extensions']['sub_alt_names']['names']:
|
||||
domains = [options["common_name"]]
|
||||
if options.get("extensions"):
|
||||
for name in options["extensions"]["sub_alt_names"]["names"]:
|
||||
domains.append(name)
|
||||
|
||||
current_app.logger.debug("Got these domains: {0}".format(domains))
|
||||
@@ -225,16 +263,22 @@ class AcmeHandler(object):
|
||||
|
||||
for domain in order_info.domains:
|
||||
if not self.dns_providers_for_domain.get(domain):
|
||||
metrics.send('get_authorizations_no_dns_provider_for_domain', 'counter', 1)
|
||||
metrics.send(
|
||||
"get_authorizations_no_dns_provider_for_domain", "counter", 1
|
||||
)
|
||||
raise Exception("No DNS providers found for domain: {}".format(domain))
|
||||
for dns_provider in self.dns_providers_for_domain[domain]:
|
||||
dns_provider_plugin = self.get_dns_provider(dns_provider.provider_type)
|
||||
dns_provider_options = json.loads(dns_provider.credentials)
|
||||
account_number = dns_provider_options.get("account_id")
|
||||
authz_record = self.start_dns_challenge(acme_client, account_number, domain,
|
||||
dns_provider_plugin,
|
||||
order,
|
||||
dns_provider.options)
|
||||
authz_record = self.start_dns_challenge(
|
||||
acme_client,
|
||||
account_number,
|
||||
domain,
|
||||
dns_provider_plugin,
|
||||
order,
|
||||
dns_provider.options,
|
||||
)
|
||||
authorizations.append(authz_record)
|
||||
return authorizations
|
||||
|
||||
@@ -268,16 +312,20 @@ class AcmeHandler(object):
|
||||
dns_providers = self.dns_providers_for_domain.get(authz_record.host)
|
||||
for dns_provider in dns_providers:
|
||||
# Grab account number (For Route53)
|
||||
dns_provider_plugin = self.get_dns_provider(dns_provider.provider_type)
|
||||
dns_provider_plugin = self.get_dns_provider(
|
||||
dns_provider.provider_type
|
||||
)
|
||||
dns_provider_options = json.loads(dns_provider.credentials)
|
||||
account_number = dns_provider_options.get("account_id")
|
||||
host_to_validate = self.maybe_remove_wildcard(authz_record.host)
|
||||
host_to_validate = self.maybe_add_extension(host_to_validate, dns_provider_options)
|
||||
host_to_validate = self.maybe_add_extension(
|
||||
host_to_validate, dns_provider_options
|
||||
)
|
||||
dns_provider_plugin.delete_txt_record(
|
||||
authz_record.change_id,
|
||||
account_number,
|
||||
dns_challenge.validation_domain_name(host_to_validate),
|
||||
dns_challenge.validation(acme_client.client.net.key)
|
||||
dns_challenge.validation(acme_client.client.net.key),
|
||||
)
|
||||
|
||||
return authorizations
|
||||
@@ -302,7 +350,9 @@ class AcmeHandler(object):
|
||||
account_number = dns_provider_options.get("account_id")
|
||||
dns_challenges = authz_record.dns_challenge
|
||||
host_to_validate = self.maybe_remove_wildcard(authz_record.host)
|
||||
host_to_validate = self.maybe_add_extension(host_to_validate, dns_provider_options)
|
||||
host_to_validate = self.maybe_add_extension(
|
||||
host_to_validate, dns_provider_options
|
||||
)
|
||||
dns_provider_plugin = self.get_dns_provider(dns_provider.provider_type)
|
||||
for dns_challenge in dns_challenges:
|
||||
try:
|
||||
@@ -310,21 +360,17 @@ class AcmeHandler(object):
|
||||
authz_record.change_id,
|
||||
account_number,
|
||||
dns_challenge.validation_domain_name(host_to_validate),
|
||||
dns_challenge.validation(acme_client.client.net.key)
|
||||
dns_challenge.validation(acme_client.client.net.key),
|
||||
)
|
||||
except Exception as e:
|
||||
# If this fails, it's most likely because the record doesn't exist (It was already cleaned up)
|
||||
# or we're not authorized to modify it.
|
||||
metrics.send('cleanup_dns_challenges_error', 'counter', 1)
|
||||
metrics.send("cleanup_dns_challenges_error", "counter", 1)
|
||||
sentry.captureException()
|
||||
pass
|
||||
|
||||
def get_dns_provider(self, type):
|
||||
provider_types = {
|
||||
'cloudflare': cloudflare,
|
||||
'dyn': dyn,
|
||||
'route53': route53,
|
||||
}
|
||||
provider_types = {"cloudflare": cloudflare, "dyn": dyn, "route53": route53}
|
||||
provider = provider_types.get(type)
|
||||
if not provider:
|
||||
raise UnknownProvider("No such DNS provider: {}".format(type))
|
||||
@@ -332,41 +378,43 @@ class AcmeHandler(object):
|
||||
|
||||
|
||||
class ACMEIssuerPlugin(IssuerPlugin):
|
||||
title = 'Acme'
|
||||
slug = 'acme-issuer'
|
||||
description = 'Enables the creation of certificates via ACME CAs (including Let\'s Encrypt)'
|
||||
title = "Acme"
|
||||
slug = "acme-issuer"
|
||||
description = (
|
||||
"Enables the creation of certificates via ACME CAs (including Let's Encrypt)"
|
||||
)
|
||||
version = acme.VERSION
|
||||
|
||||
author = 'Netflix'
|
||||
author_url = 'https://github.com/netflix/lemur.git'
|
||||
author = "Netflix"
|
||||
author_url = "https://github.com/netflix/lemur.git"
|
||||
|
||||
options = [
|
||||
{
|
||||
'name': 'acme_url',
|
||||
'type': 'str',
|
||||
'required': True,
|
||||
'validation': '/^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$/',
|
||||
'helpMessage': 'Must be a valid web url starting with http[s]://',
|
||||
"name": "acme_url",
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"validation": "/^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$/",
|
||||
"helpMessage": "Must be a valid web url starting with http[s]://",
|
||||
},
|
||||
{
|
||||
'name': 'telephone',
|
||||
'type': 'str',
|
||||
'default': '',
|
||||
'helpMessage': 'Telephone to use'
|
||||
"name": "telephone",
|
||||
"type": "str",
|
||||
"default": "",
|
||||
"helpMessage": "Telephone to use",
|
||||
},
|
||||
{
|
||||
'name': 'email',
|
||||
'type': 'str',
|
||||
'default': '',
|
||||
'validation': '/^?([-a-zA-Z0-9.`?{}]+@\w+\.\w+)$/',
|
||||
'helpMessage': 'Email to use'
|
||||
"name": "email",
|
||||
"type": "str",
|
||||
"default": "",
|
||||
"validation": "/^?([-a-zA-Z0-9.`?{}]+@\w+\.\w+)$/",
|
||||
"helpMessage": "Email to use",
|
||||
},
|
||||
{
|
||||
'name': 'certificate',
|
||||
'type': 'textarea',
|
||||
'default': '',
|
||||
'validation': '/^-----BEGIN CERTIFICATE-----/',
|
||||
'helpMessage': 'Certificate to use'
|
||||
"name": "certificate",
|
||||
"type": "textarea",
|
||||
"default": "",
|
||||
"validation": "/^-----BEGIN CERTIFICATE-----/",
|
||||
"helpMessage": "Certificate to use",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -376,11 +424,7 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
def get_dns_provider(self, type):
|
||||
self.acme = AcmeHandler()
|
||||
|
||||
provider_types = {
|
||||
'cloudflare': cloudflare,
|
||||
'dyn': dyn,
|
||||
'route53': route53,
|
||||
}
|
||||
provider_types = {"cloudflare": cloudflare, "dyn": dyn, "route53": route53}
|
||||
provider = provider_types.get(type)
|
||||
if not provider:
|
||||
raise UnknownProvider("No such DNS provider: {}".format(type))
|
||||
@@ -411,24 +455,31 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
try:
|
||||
order = acme_client.new_order(pending_cert.csr)
|
||||
except WildcardUnsupportedError:
|
||||
metrics.send('get_ordered_certificate_wildcard_unsupported', 'counter', 1)
|
||||
raise Exception("The currently selected ACME CA endpoint does"
|
||||
" not support issuing wildcard certificates.")
|
||||
metrics.send("get_ordered_certificate_wildcard_unsupported", "counter", 1)
|
||||
raise Exception(
|
||||
"The currently selected ACME CA endpoint does"
|
||||
" not support issuing wildcard certificates."
|
||||
)
|
||||
try:
|
||||
authorizations = self.acme.get_authorizations(acme_client, order, order_info)
|
||||
authorizations = self.acme.get_authorizations(
|
||||
acme_client, order, order_info
|
||||
)
|
||||
except ClientError:
|
||||
sentry.captureException()
|
||||
metrics.send('get_ordered_certificate_error', 'counter', 1)
|
||||
current_app.logger.error(f"Unable to resolve pending cert: {pending_cert.name}", exc_info=True)
|
||||
metrics.send("get_ordered_certificate_error", "counter", 1)
|
||||
current_app.logger.error(
|
||||
f"Unable to resolve pending cert: {pending_cert.name}", exc_info=True
|
||||
)
|
||||
return False
|
||||
|
||||
authorizations = self.acme.finalize_authorizations(acme_client, authorizations)
|
||||
pem_certificate, pem_certificate_chain = self.acme.request_certificate(
|
||||
acme_client, authorizations, order)
|
||||
acme_client, authorizations, order
|
||||
)
|
||||
cert = {
|
||||
'body': "\n".join(str(pem_certificate).splitlines()),
|
||||
'chain': "\n".join(str(pem_certificate_chain).splitlines()),
|
||||
'external_id': str(pending_cert.external_id)
|
||||
"body": "\n".join(str(pem_certificate).splitlines()),
|
||||
"chain": "\n".join(str(pem_certificate_chain).splitlines()),
|
||||
"external_id": str(pending_cert.external_id),
|
||||
}
|
||||
return cert
|
||||
|
||||
@@ -438,10 +489,14 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
certs = []
|
||||
for pending_cert in pending_certs:
|
||||
try:
|
||||
acme_client, registration = self.acme.setup_acme_client(pending_cert.authority)
|
||||
acme_client, registration = self.acme.setup_acme_client(
|
||||
pending_cert.authority
|
||||
)
|
||||
order_info = authorization_service.get(pending_cert.external_id)
|
||||
if pending_cert.dns_provider_id:
|
||||
dns_provider = dns_provider_service.get(pending_cert.dns_provider_id)
|
||||
dns_provider = dns_provider_service.get(
|
||||
pending_cert.dns_provider_id
|
||||
)
|
||||
|
||||
for domain in order_info.domains:
|
||||
# Currently, we only support specifying one DNS provider per certificate, even if that
|
||||
@@ -455,70 +510,79 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
order = acme_client.new_order(pending_cert.csr)
|
||||
except WildcardUnsupportedError:
|
||||
sentry.captureException()
|
||||
metrics.send('get_ordered_certificates_wildcard_unsupported_error', 'counter', 1)
|
||||
raise Exception("The currently selected ACME CA endpoint does"
|
||||
" not support issuing wildcard certificates.")
|
||||
metrics.send(
|
||||
"get_ordered_certificates_wildcard_unsupported_error",
|
||||
"counter",
|
||||
1,
|
||||
)
|
||||
raise Exception(
|
||||
"The currently selected ACME CA endpoint does"
|
||||
" not support issuing wildcard certificates."
|
||||
)
|
||||
|
||||
authorizations = self.acme.get_authorizations(acme_client, order, order_info)
|
||||
authorizations = self.acme.get_authorizations(
|
||||
acme_client, order, order_info
|
||||
)
|
||||
|
||||
pending.append({
|
||||
"acme_client": acme_client,
|
||||
"authorizations": authorizations,
|
||||
"pending_cert": pending_cert,
|
||||
"order": order,
|
||||
})
|
||||
pending.append(
|
||||
{
|
||||
"acme_client": acme_client,
|
||||
"authorizations": authorizations,
|
||||
"pending_cert": pending_cert,
|
||||
"order": order,
|
||||
}
|
||||
)
|
||||
except (ClientError, ValueError, Exception) as e:
|
||||
sentry.captureException()
|
||||
metrics.send('get_ordered_certificates_pending_creation_error', 'counter', 1)
|
||||
current_app.logger.error(f"Unable to resolve pending cert: {pending_cert}", exc_info=True)
|
||||
metrics.send(
|
||||
"get_ordered_certificates_pending_creation_error", "counter", 1
|
||||
)
|
||||
current_app.logger.error(
|
||||
f"Unable to resolve pending cert: {pending_cert}", exc_info=True
|
||||
)
|
||||
|
||||
error = e
|
||||
if globals().get("order") and order:
|
||||
error += f" Order uri: {order.uri}"
|
||||
certs.append({
|
||||
"cert": False,
|
||||
"pending_cert": pending_cert,
|
||||
"last_error": e,
|
||||
})
|
||||
certs.append(
|
||||
{"cert": False, "pending_cert": pending_cert, "last_error": e}
|
||||
)
|
||||
|
||||
for entry in pending:
|
||||
try:
|
||||
entry["authorizations"] = self.acme.finalize_authorizations(
|
||||
entry["acme_client"],
|
||||
entry["authorizations"],
|
||||
entry["acme_client"], entry["authorizations"]
|
||||
)
|
||||
pem_certificate, pem_certificate_chain = self.acme.request_certificate(
|
||||
entry["acme_client"],
|
||||
entry["authorizations"],
|
||||
entry["order"]
|
||||
entry["acme_client"], entry["authorizations"], entry["order"]
|
||||
)
|
||||
|
||||
cert = {
|
||||
'body': "\n".join(str(pem_certificate).splitlines()),
|
||||
'chain': "\n".join(str(pem_certificate_chain).splitlines()),
|
||||
'external_id': str(entry["pending_cert"].external_id)
|
||||
"body": "\n".join(str(pem_certificate).splitlines()),
|
||||
"chain": "\n".join(str(pem_certificate_chain).splitlines()),
|
||||
"external_id": str(entry["pending_cert"].external_id),
|
||||
}
|
||||
certs.append({
|
||||
"cert": cert,
|
||||
"pending_cert": entry["pending_cert"],
|
||||
})
|
||||
certs.append({"cert": cert, "pending_cert": entry["pending_cert"]})
|
||||
except (PollError, AcmeError, Exception) as e:
|
||||
sentry.captureException()
|
||||
metrics.send('get_ordered_certificates_resolution_error', 'counter', 1)
|
||||
metrics.send("get_ordered_certificates_resolution_error", "counter", 1)
|
||||
order_url = order.uri
|
||||
error = f"{e}. Order URI: {order_url}"
|
||||
current_app.logger.error(
|
||||
f"Unable to resolve pending cert: {pending_cert}. "
|
||||
f"Check out {order_url} for more information.", exc_info=True)
|
||||
certs.append({
|
||||
"cert": False,
|
||||
"pending_cert": entry["pending_cert"],
|
||||
"last_error": error,
|
||||
})
|
||||
f"Check out {order_url} for more information.",
|
||||
exc_info=True,
|
||||
)
|
||||
certs.append(
|
||||
{
|
||||
"cert": False,
|
||||
"pending_cert": entry["pending_cert"],
|
||||
"last_error": error,
|
||||
}
|
||||
)
|
||||
# Ensure DNS records get deleted
|
||||
self.acme.cleanup_dns_challenges(
|
||||
entry["acme_client"],
|
||||
entry["authorizations"],
|
||||
entry["acme_client"], entry["authorizations"]
|
||||
)
|
||||
return certs
|
||||
|
||||
@@ -531,20 +595,26 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
:return: :raise Exception:
|
||||
"""
|
||||
self.acme = AcmeHandler()
|
||||
authority = issuer_options.get('authority')
|
||||
create_immediately = issuer_options.get('create_immediately', False)
|
||||
authority = issuer_options.get("authority")
|
||||
create_immediately = issuer_options.get("create_immediately", False)
|
||||
acme_client, registration = self.acme.setup_acme_client(authority)
|
||||
dns_provider = issuer_options.get('dns_provider', {})
|
||||
dns_provider = issuer_options.get("dns_provider", {})
|
||||
|
||||
if dns_provider:
|
||||
dns_provider_options = dns_provider.options
|
||||
credentials = json.loads(dns_provider.credentials)
|
||||
current_app.logger.debug("Using DNS provider: {0}".format(dns_provider.provider_type))
|
||||
dns_provider_plugin = __import__(dns_provider.provider_type, globals(), locals(), [], 1)
|
||||
current_app.logger.debug(
|
||||
"Using DNS provider: {0}".format(dns_provider.provider_type)
|
||||
)
|
||||
dns_provider_plugin = __import__(
|
||||
dns_provider.provider_type, globals(), locals(), [], 1
|
||||
)
|
||||
account_number = credentials.get("account_id")
|
||||
provider_type = dns_provider.provider_type
|
||||
if provider_type == "route53" and not account_number:
|
||||
error = "Route53 DNS Provider {} does not have an account number configured.".format(dns_provider.name)
|
||||
error = "Route53 DNS Provider {} does not have an account number configured.".format(
|
||||
dns_provider.name
|
||||
)
|
||||
current_app.logger.error(error)
|
||||
raise InvalidConfiguration(error)
|
||||
else:
|
||||
@@ -563,16 +633,29 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
else:
|
||||
authz_domains.append(d.value)
|
||||
|
||||
dns_authorization = authorization_service.create(account_number, authz_domains,
|
||||
provider_type)
|
||||
dns_authorization = authorization_service.create(
|
||||
account_number, authz_domains, provider_type
|
||||
)
|
||||
# Return id of the DNS Authorization
|
||||
return None, None, dns_authorization.id
|
||||
|
||||
authorizations = self.acme.get_authorizations(acme_client, account_number, domains, dns_provider_plugin,
|
||||
dns_provider_options)
|
||||
self.acme.finalize_authorizations(acme_client, account_number, dns_provider_plugin, authorizations,
|
||||
dns_provider_options)
|
||||
pem_certificate, pem_certificate_chain = self.acme.request_certificate(acme_client, authorizations, csr)
|
||||
authorizations = self.acme.get_authorizations(
|
||||
acme_client,
|
||||
account_number,
|
||||
domains,
|
||||
dns_provider_plugin,
|
||||
dns_provider_options,
|
||||
)
|
||||
self.acme.finalize_authorizations(
|
||||
acme_client,
|
||||
account_number,
|
||||
dns_provider_plugin,
|
||||
authorizations,
|
||||
dns_provider_options,
|
||||
)
|
||||
pem_certificate, pem_certificate_chain = self.acme.request_certificate(
|
||||
acme_client, authorizations, csr
|
||||
)
|
||||
# TODO add external ID (if possible)
|
||||
return pem_certificate, pem_certificate_chain, None
|
||||
|
||||
@@ -585,18 +668,18 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
role = {'username': '', 'password': '', 'name': 'acme'}
|
||||
plugin_options = options.get('plugin', {}).get('plugin_options')
|
||||
role = {"username": "", "password": "", "name": "acme"}
|
||||
plugin_options = options.get("plugin", {}).get("plugin_options")
|
||||
if not plugin_options:
|
||||
error = "Invalid options for lemur_acme plugin: {}".format(options)
|
||||
current_app.logger.error(error)
|
||||
raise InvalidConfiguration(error)
|
||||
# Define static acme_root based off configuration variable by default. However, if user has passed a
|
||||
# certificate, use this certificate as the root.
|
||||
acme_root = current_app.config.get('ACME_ROOT')
|
||||
acme_root = current_app.config.get("ACME_ROOT")
|
||||
for option in plugin_options:
|
||||
if option.get('name') == 'certificate':
|
||||
acme_root = option.get('value')
|
||||
if option.get("name") == "certificate":
|
||||
acme_root = option.get("value")
|
||||
return acme_root, "", [role]
|
||||
|
||||
def cancel_ordered_certificate(self, pending_cert, **kwargs):
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
from lemur.plugins.lemur_aws.sts import sts_client
|
||||
|
||||
|
||||
@sts_client('route53')
|
||||
@sts_client("route53")
|
||||
def wait_for_dns_change(change_id, client=None):
|
||||
_, change_id = change_id
|
||||
|
||||
@@ -14,7 +14,7 @@ def wait_for_dns_change(change_id, client=None):
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
@sts_client('route53')
|
||||
@sts_client("route53")
|
||||
def find_zone_id(domain, client=None):
|
||||
paginator = client.get_paginator("list_hosted_zones")
|
||||
zones = []
|
||||
@@ -25,34 +25,35 @@ def find_zone_id(domain, client=None):
|
||||
zones.append((zone["Name"], zone["Id"]))
|
||||
|
||||
if not zones:
|
||||
raise ValueError(
|
||||
"Unable to find a Route53 hosted zone for {}".format(domain)
|
||||
)
|
||||
raise ValueError("Unable to find a Route53 hosted zone for {}".format(domain))
|
||||
return zones[0][1]
|
||||
|
||||
|
||||
@sts_client('route53')
|
||||
@sts_client("route53")
|
||||
def get_zones(client=None):
|
||||
paginator = client.get_paginator("list_hosted_zones")
|
||||
zones = []
|
||||
for page in paginator.paginate():
|
||||
for zone in page["HostedZones"]:
|
||||
zones.append(zone["Name"][:-1]) # We need [:-1] to strip out the trailing dot.
|
||||
zones.append(
|
||||
zone["Name"][:-1]
|
||||
) # We need [:-1] to strip out the trailing dot.
|
||||
return zones
|
||||
|
||||
|
||||
@sts_client('route53')
|
||||
@sts_client("route53")
|
||||
def change_txt_record(action, zone_id, domain, value, client=None):
|
||||
current_txt_records = []
|
||||
try:
|
||||
current_records = client.list_resource_record_sets(
|
||||
HostedZoneId=zone_id,
|
||||
StartRecordName=domain,
|
||||
StartRecordType='TXT',
|
||||
MaxItems="1")["ResourceRecordSets"]
|
||||
StartRecordType="TXT",
|
||||
MaxItems="1",
|
||||
)["ResourceRecordSets"]
|
||||
|
||||
for record in current_records:
|
||||
if record.get('Type') == 'TXT':
|
||||
if record.get("Type") == "TXT":
|
||||
current_txt_records.extend(record.get("ResourceRecords", []))
|
||||
except Exception as e:
|
||||
# Current Resource Record does not exist
|
||||
@@ -72,7 +73,9 @@ def change_txt_record(action, zone_id, domain, value, client=None):
|
||||
# If we want to delete one record out of many, we'll update the record to not include the deleted value instead.
|
||||
# This allows us to support concurrent issuance.
|
||||
current_txt_records = [
|
||||
record for record in current_txt_records if not (record.get('Value') == '"{}"'.format(value))
|
||||
record
|
||||
for record in current_txt_records
|
||||
if not (record.get("Value") == '"{}"'.format(value))
|
||||
]
|
||||
action = "UPSERT"
|
||||
|
||||
@@ -87,10 +90,10 @@ def change_txt_record(action, zone_id, domain, value, client=None):
|
||||
"Type": "TXT",
|
||||
"TTL": 300,
|
||||
"ResourceRecords": current_txt_records,
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
return response["ChangeInfo"]["Id"]
|
||||
|
||||
@@ -98,11 +101,7 @@ def change_txt_record(action, zone_id, domain, value, client=None):
|
||||
def create_txt_record(host, value, account_number):
|
||||
zone_id = find_zone_id(host, account_number=account_number)
|
||||
change_id = change_txt_record(
|
||||
"UPSERT",
|
||||
zone_id,
|
||||
host,
|
||||
value,
|
||||
account_number=account_number
|
||||
"UPSERT", zone_id, host, value, account_number=account_number
|
||||
)
|
||||
|
||||
return zone_id, change_id
|
||||
@@ -113,11 +112,7 @@ def delete_txt_record(change_ids, account_number, host, value):
|
||||
zone_id, _ = change_id
|
||||
try:
|
||||
change_txt_record(
|
||||
"DELETE",
|
||||
zone_id,
|
||||
host,
|
||||
value,
|
||||
account_number=account_number
|
||||
"DELETE", zone_id, host, value, account_number=account_number
|
||||
)
|
||||
except Exception as e:
|
||||
if "but it was not found" in e.response.get("Error", {}).get("Message"):
|
||||
|
@@ -6,8 +6,7 @@ from lemur.plugins.lemur_acme import plugin
|
||||
|
||||
|
||||
class TestAcme(unittest.TestCase):
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.dns_provider_service')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.dns_provider_service")
|
||||
def setUp(self, mock_dns_provider_service):
|
||||
self.ACMEIssuerPlugin = plugin.ACMEIssuerPlugin()
|
||||
self.acme = plugin.AcmeHandler()
|
||||
@@ -15,14 +14,17 @@ class TestAcme(unittest.TestCase):
|
||||
mock_dns_provider.name = "cloudflare"
|
||||
mock_dns_provider.credentials = "{}"
|
||||
mock_dns_provider.provider_type = "cloudflare"
|
||||
self.acme.dns_providers_for_domain = {"www.test.com": [mock_dns_provider],
|
||||
"test.fakedomain.net": [mock_dns_provider]}
|
||||
self.acme.dns_providers_for_domain = {
|
||||
"www.test.com": [mock_dns_provider],
|
||||
"test.fakedomain.net": [mock_dns_provider],
|
||||
}
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.len', return_value=1)
|
||||
@patch("lemur.plugins.lemur_acme.plugin.len", return_value=1)
|
||||
def test_find_dns_challenge(self, mock_len):
|
||||
assert mock_len
|
||||
|
||||
from acme import challenges
|
||||
|
||||
c = challenges.DNS01()
|
||||
|
||||
mock_authz = Mock()
|
||||
@@ -37,11 +39,13 @@ class TestAcme(unittest.TestCase):
|
||||
a = plugin.AuthorizationRecord("host", "authz", "challenge", "id")
|
||||
self.assertEqual(type(a), plugin.AuthorizationRecord)
|
||||
|
||||
@patch('acme.client.Client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.len', return_value=1)
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.find_dns_challenge')
|
||||
def test_start_dns_challenge(self, mock_find_dns_challenge, mock_len, mock_app, mock_acme):
|
||||
@patch("acme.client.Client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.len", return_value=1)
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.find_dns_challenge")
|
||||
def test_start_dns_challenge(
|
||||
self, mock_find_dns_challenge, mock_len, mock_app, mock_acme
|
||||
):
|
||||
assert mock_len
|
||||
mock_order = Mock()
|
||||
mock_app.logger.debug = Mock()
|
||||
@@ -49,6 +53,7 @@ class TestAcme(unittest.TestCase):
|
||||
mock_authz.body.resolved_combinations = []
|
||||
mock_entry = MagicMock()
|
||||
from acme import challenges
|
||||
|
||||
c = challenges.DNS01()
|
||||
mock_entry.chall = TestAcme.test_complete_dns_challenge_fail
|
||||
mock_authz.body.resolved_combinations.append(mock_entry)
|
||||
@@ -60,13 +65,17 @@ class TestAcme(unittest.TestCase):
|
||||
iterable = mock_find_dns_challenge.return_value
|
||||
iterator = iter(values)
|
||||
iterable.__iter__.return_value = iterator
|
||||
result = self.acme.start_dns_challenge(mock_acme, "accountid", "host", mock_dns_provider, mock_order, {})
|
||||
result = self.acme.start_dns_challenge(
|
||||
mock_acme, "accountid", "host", mock_dns_provider, mock_order, {}
|
||||
)
|
||||
self.assertEqual(type(result), plugin.AuthorizationRecord)
|
||||
|
||||
@patch('acme.client.Client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.cloudflare.wait_for_dns_change')
|
||||
def test_complete_dns_challenge_success(self, mock_wait_for_dns_change, mock_current_app, mock_acme):
|
||||
@patch("acme.client.Client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.cloudflare.wait_for_dns_change")
|
||||
def test_complete_dns_challenge_success(
|
||||
self, 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()
|
||||
@@ -84,10 +93,12 @@ class TestAcme(unittest.TestCase):
|
||||
mock_authz.dns_challenge.append(dns_challenge)
|
||||
self.acme.complete_dns_challenge(mock_acme, mock_authz)
|
||||
|
||||
@patch('acme.client.Client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.cloudflare.wait_for_dns_change')
|
||||
def test_complete_dns_challenge_fail(self, mock_wait_for_dns_change, mock_current_app, mock_acme):
|
||||
@patch("acme.client.Client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.cloudflare.wait_for_dns_change")
|
||||
def test_complete_dns_challenge_fail(
|
||||
self, 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)
|
||||
|
||||
@@ -105,16 +116,22 @@ class TestAcme(unittest.TestCase):
|
||||
dns_challenge = Mock()
|
||||
mock_authz.dns_challenge.append(dns_challenge)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
self.acme.complete_dns_challenge(mock_acme, mock_authz)
|
||||
ValueError, self.acme.complete_dns_challenge(mock_acme, mock_authz)
|
||||
)
|
||||
|
||||
@patch('acme.client.Client')
|
||||
@patch('OpenSSL.crypto', return_value="mock_cert")
|
||||
@patch('josepy.util.ComparableX509')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.find_dns_challenge')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
def test_request_certificate(self, mock_current_app, mock_find_dns_challenge, mock_jose, mock_crypto, mock_acme):
|
||||
@patch("acme.client.Client")
|
||||
@patch("OpenSSL.crypto", return_value="mock_cert")
|
||||
@patch("josepy.util.ComparableX509")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.find_dns_challenge")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
def test_request_certificate(
|
||||
self,
|
||||
mock_current_app,
|
||||
mock_find_dns_challenge,
|
||||
mock_jose,
|
||||
mock_crypto,
|
||||
mock_acme,
|
||||
):
|
||||
mock_cert_response = Mock()
|
||||
mock_cert_response.body = "123"
|
||||
mock_cert_response_full = [mock_cert_response, True]
|
||||
@@ -124,7 +141,7 @@ class TestAcme(unittest.TestCase):
|
||||
mock_authz_record.authz = Mock()
|
||||
mock_authz.append(mock_authz_record)
|
||||
mock_acme.fetch_chain = Mock(return_value="mock_chain")
|
||||
mock_crypto.dump_certificate = Mock(return_value=b'chain')
|
||||
mock_crypto.dump_certificate = Mock(return_value=b"chain")
|
||||
mock_order = Mock()
|
||||
self.acme.request_certificate(mock_acme, [], mock_order)
|
||||
|
||||
@@ -134,8 +151,8 @@ class TestAcme(unittest.TestCase):
|
||||
with self.assertRaises(Exception):
|
||||
self.acme.setup_acme_client(mock_authority)
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.BackwardsCompatibleClientV2')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.BackwardsCompatibleClientV2")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
def test_setup_acme_client_success(self, mock_current_app, mock_acme):
|
||||
mock_authority = Mock()
|
||||
mock_authority.options = '[{"name": "mock_name", "value": "mock_value"}]'
|
||||
@@ -150,31 +167,29 @@ class TestAcme(unittest.TestCase):
|
||||
assert result_client
|
||||
assert result_registration
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
def test_get_domains_single(self, mock_current_app):
|
||||
options = {
|
||||
"common_name": "test.netflix.net"
|
||||
}
|
||||
options = {"common_name": "test.netflix.net"}
|
||||
result = self.acme.get_domains(options)
|
||||
self.assertEqual(result, [options["common_name"]])
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
def test_get_domains_multiple(self, mock_current_app):
|
||||
options = {
|
||||
"common_name": "test.netflix.net",
|
||||
"extensions": {
|
||||
"sub_alt_names": {
|
||||
"names": [
|
||||
"test2.netflix.net",
|
||||
"test3.netflix.net"
|
||||
]
|
||||
}
|
||||
}
|
||||
"sub_alt_names": {"names": ["test2.netflix.net", "test3.netflix.net"]}
|
||||
},
|
||||
}
|
||||
result = self.acme.get_domains(options)
|
||||
self.assertEqual(result, [options["common_name"], "test2.netflix.net", "test3.netflix.net"])
|
||||
self.assertEqual(
|
||||
result, [options["common_name"], "test2.netflix.net", "test3.netflix.net"]
|
||||
)
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.start_dns_challenge', return_value="test")
|
||||
@patch(
|
||||
"lemur.plugins.lemur_acme.plugin.AcmeHandler.start_dns_challenge",
|
||||
return_value="test",
|
||||
)
|
||||
def test_get_authorizations(self, mock_start_dns_challenge):
|
||||
mock_order = Mock()
|
||||
mock_order.body.identifiers = []
|
||||
@@ -183,10 +198,15 @@ class TestAcme(unittest.TestCase):
|
||||
mock_order_info = Mock()
|
||||
mock_order_info.account_number = 1
|
||||
mock_order_info.domains = ["test.fakedomain.net"]
|
||||
result = self.acme.get_authorizations("acme_client", mock_order, mock_order_info)
|
||||
result = self.acme.get_authorizations(
|
||||
"acme_client", mock_order, mock_order_info
|
||||
)
|
||||
self.assertEqual(result, ["test"])
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.complete_dns_challenge', return_value="test")
|
||||
@patch(
|
||||
"lemur.plugins.lemur_acme.plugin.AcmeHandler.complete_dns_challenge",
|
||||
return_value="test",
|
||||
)
|
||||
def test_finalize_authorizations(self, mock_complete_dns_challenge):
|
||||
mock_authz = []
|
||||
mock_authz_record = MagicMock()
|
||||
@@ -202,28 +222,28 @@ class TestAcme(unittest.TestCase):
|
||||
result = self.acme.finalize_authorizations(mock_acme_client, mock_authz)
|
||||
self.assertEqual(result, mock_authz)
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
def test_create_authority(self, mock_current_app):
|
||||
mock_current_app.config = Mock()
|
||||
options = {
|
||||
"plugin": {
|
||||
"plugin_options": [{
|
||||
"name": "certificate",
|
||||
"value": "123"
|
||||
}]
|
||||
}
|
||||
"plugin": {"plugin_options": [{"name": "certificate", "value": "123"}]}
|
||||
}
|
||||
acme_root, b, role = self.ACMEIssuerPlugin.create_authority(options)
|
||||
self.assertEqual(acme_root, "123")
|
||||
self.assertEqual(b, "")
|
||||
self.assertEqual(role, [{'username': '', 'password': '', 'name': 'acme'}])
|
||||
self.assertEqual(role, [{"username": "", "password": "", "name": "acme"}])
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.dyn.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.cloudflare.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.dns_provider_service')
|
||||
def test_get_dns_provider(self, mock_dns_provider_service, mock_current_app_cloudflare, mock_current_app_dyn,
|
||||
mock_current_app):
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.dyn.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.cloudflare.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.dns_provider_service")
|
||||
def test_get_dns_provider(
|
||||
self,
|
||||
mock_dns_provider_service,
|
||||
mock_current_app_cloudflare,
|
||||
mock_current_app_dyn,
|
||||
mock_current_app,
|
||||
):
|
||||
provider = plugin.ACMEIssuerPlugin()
|
||||
route53 = provider.get_dns_provider("route53")
|
||||
assert route53
|
||||
@@ -232,16 +252,23 @@ class TestAcme(unittest.TestCase):
|
||||
dyn = provider.get_dns_provider("dyn")
|
||||
assert dyn
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.authorization_service')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.dns_provider_service')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.authorization_service")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.dns_provider_service")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate")
|
||||
def test_get_ordered_certificate(
|
||||
self, mock_request_certificate, mock_finalize_authorizations, mock_get_authorizations,
|
||||
mock_dns_provider_service, mock_authorization_service, mock_current_app, mock_acme):
|
||||
self,
|
||||
mock_request_certificate,
|
||||
mock_finalize_authorizations,
|
||||
mock_get_authorizations,
|
||||
mock_dns_provider_service,
|
||||
mock_authorization_service,
|
||||
mock_current_app,
|
||||
mock_acme,
|
||||
):
|
||||
mock_client = Mock()
|
||||
mock_acme.return_value = (mock_client, "")
|
||||
mock_request_certificate.return_value = ("pem_certificate", "chain")
|
||||
@@ -253,24 +280,26 @@ class TestAcme(unittest.TestCase):
|
||||
provider.get_dns_provider = Mock()
|
||||
result = provider.get_ordered_certificate(mock_cert)
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
'body': "pem_certificate",
|
||||
'chain': "chain",
|
||||
'external_id': "1"
|
||||
}
|
||||
result, {"body": "pem_certificate", "chain": "chain", "external_id": "1"}
|
||||
)
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.authorization_service')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.dns_provider_service')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate')
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.authorization_service")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.dns_provider_service")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate")
|
||||
def test_get_ordered_certificates(
|
||||
self, mock_request_certificate, mock_finalize_authorizations, mock_get_authorizations,
|
||||
mock_dns_provider_service, mock_authorization_service, mock_current_app, mock_acme):
|
||||
self,
|
||||
mock_request_certificate,
|
||||
mock_finalize_authorizations,
|
||||
mock_get_authorizations,
|
||||
mock_dns_provider_service,
|
||||
mock_authorization_service,
|
||||
mock_current_app,
|
||||
mock_acme,
|
||||
):
|
||||
mock_client = Mock()
|
||||
mock_acme.return_value = (mock_client, "")
|
||||
mock_request_certificate.return_value = ("pem_certificate", "chain")
|
||||
@@ -285,19 +314,32 @@ class TestAcme(unittest.TestCase):
|
||||
provider.get_dns_provider = Mock()
|
||||
result = provider.get_ordered_certificates([mock_cert, mock_cert2])
|
||||
self.assertEqual(len(result), 2)
|
||||
self.assertEqual(result[0]['cert'], {'body': 'pem_certificate', 'chain': 'chain', 'external_id': '1'})
|
||||
self.assertEqual(result[1]['cert'], {'body': 'pem_certificate', 'chain': 'chain', 'external_id': '2'})
|
||||
self.assertEqual(
|
||||
result[0]["cert"],
|
||||
{"body": "pem_certificate", "chain": "chain", "external_id": "1"},
|
||||
)
|
||||
self.assertEqual(
|
||||
result[1]["cert"],
|
||||
{"body": "pem_certificate", "chain": "chain", "external_id": "2"},
|
||||
)
|
||||
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.dns_provider_service')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate')
|
||||
@patch('lemur.plugins.lemur_acme.plugin.authorization_service')
|
||||
def test_create_certificate(self, mock_authorization_service, mock_request_certificate,
|
||||
mock_finalize_authorizations, mock_get_authorizations,
|
||||
mock_current_app, mock_dns_provider_service, mock_acme):
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.dns_provider_service")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.current_app")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.get_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.finalize_authorizations")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.request_certificate")
|
||||
@patch("lemur.plugins.lemur_acme.plugin.authorization_service")
|
||||
def test_create_certificate(
|
||||
self,
|
||||
mock_authorization_service,
|
||||
mock_request_certificate,
|
||||
mock_finalize_authorizations,
|
||||
mock_get_authorizations,
|
||||
mock_current_app,
|
||||
mock_dns_provider_service,
|
||||
mock_acme,
|
||||
):
|
||||
provider = plugin.ACMEIssuerPlugin()
|
||||
mock_authority = Mock()
|
||||
|
||||
@@ -310,9 +352,9 @@ class TestAcme(unittest.TestCase):
|
||||
mock_dns_provider_service.get.return_value = mock_dns_provider
|
||||
|
||||
issuer_options = {
|
||||
'authority': mock_authority,
|
||||
'dns_provider': mock_dns_provider,
|
||||
"common_name": "test.netflix.net"
|
||||
"authority": mock_authority,
|
||||
"dns_provider": mock_dns_provider,
|
||||
"common_name": "test.netflix.net",
|
||||
}
|
||||
csr = "123"
|
||||
mock_request_certificate.return_value = ("pem_certificate", "chain")
|
||||
|
Reference in New Issue
Block a user