From 4077893d08decc9afcb6eb401fb46c8ce28ccceb Mon Sep 17 00:00:00 2001 From: kevgliss Date: Mon, 4 Jul 2016 15:30:20 -0700 Subject: [PATCH] Ensuring that destinations require private keys by default. (#390) * Ensuring that destinations require private keys by default. --- docs/developer/plugins/index.rst | 6 ++++++ lemur/authorities/models.py | 5 +++++ lemur/certificates/models.py | 1 + lemur/certificates/views.py | 14 +++++++++++--- lemur/destinations/models.py | 6 +----- lemur/plugins/bases/destination.py | 1 + lemur/plugins/lemur_aws/plugin.py | 21 +++++++++------------ lemur/plugins/lemur_java/plugin.py | 3 --- lemur/tests/test_authorities.py | 2 +- 9 files changed, 35 insertions(+), 24 deletions(-) diff --git a/docs/developer/plugins/index.rst b/docs/developer/plugins/index.rst index b3badbff..12f24fdd 100644 --- a/docs/developer/plugins/index.rst +++ b/docs/developer/plugins/index.rst @@ -137,6 +137,12 @@ Destination Destination plugins allow you to propagate certificates managed by Lemur to additional third parties. This provides flexibility when different orchestration systems have their own way of manage certificates or there is an existing system you wish to integrate with Lemur. +By default destination plugins have a private key requirement. If your plugin does not require a certificates private key mark `requires_key = False` +in the plugins base class like so:: + + class MyDestinationPlugin(DestinationPlugin): + requires_key = False + The DestinationPlugin requires only one function to be implemented:: def upload(self, cert, private_key, cert_chain, options, **kwargs): diff --git a/lemur/authorities/models.py b/lemur/authorities/models.py index 1777d7dd..57fdf657 100644 --- a/lemur/authorities/models.py +++ b/lemur/authorities/models.py @@ -11,6 +11,7 @@ from sqlalchemy import Column, Integer, String, Text, func, ForeignKey, DateTime from sqlalchemy.dialects.postgresql import JSON from lemur.database import db +from lemur.plugins.base import plugins from lemur.models import roles_authorities @@ -38,3 +39,7 @@ class Authority(db.Model): self.description = kwargs.get('description') self.authority_certificate = kwargs['authority_certificate'] self.plugin_name = kwargs['plugin']['slug'] + + @property + def plugin(self): + return plugins.get(self.plugin_name) diff --git a/lemur/certificates/models.py b/lemur/certificates/models.py index f75b4207..ee15dece 100644 --- a/lemur/certificates/models.py +++ b/lemur/certificates/models.py @@ -158,6 +158,7 @@ def update_destinations(target, value, initiator): :return: """ destination_plugin = plugins.get(value.plugin_name) + try: destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options) except Exception as e: diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index b50e7f23..d41a239a 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -584,6 +584,11 @@ class Certificates(AuthenticatedResource): permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles]) if permission.can(): + for destination in data['destinations']: + if destination.plugin.requires_key: + if not cert.private_key: + return dict('Unable to add destination: {0}. Certificate does not have required private key.'.format(destination.label)) + return service.update( certificate_id, data['owner'], @@ -871,10 +876,13 @@ class CertificateExport(AuthenticatedResource): plugin = data['plugin']['plugin_object'] if plugin.requires_key: - if permission.can(): - extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options) + if cert.private_key: + if permission.can(): + extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options) + else: + return dict(message='You are not authorized to export this certificate'), 403 else: - return dict(message='You are not authorized to export this certificate'), 403 + return dict(message='Unable to export certificate, plugin: {0} requires a private key but no key was found.'.format(plugin.slug)) else: extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options) diff --git a/lemur/destinations/models.py b/lemur/destinations/models.py index c4cc8a76..9c5dbc0e 100644 --- a/lemur/destinations/models.py +++ b/lemur/destinations/models.py @@ -5,7 +5,6 @@ :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson """ -import copy from sqlalchemy import Column, Integer, String, Text from sqlalchemy_utils import JSONType from lemur.database import db @@ -23,7 +22,4 @@ class Destination(db.Model): @property def plugin(self): - p = plugins.get(self.plugin_name) - c = copy.deepcopy(p) - c.options = self.options - return c + return plugins.get(self.plugin_name) diff --git a/lemur/plugins/bases/destination.py b/lemur/plugins/bases/destination.py index 61c908eb..38f3c022 100644 --- a/lemur/plugins/bases/destination.py +++ b/lemur/plugins/bases/destination.py @@ -11,6 +11,7 @@ from lemur.plugins.base import Plugin class DestinationPlugin(Plugin): type = 'destination' + requires_key = True def upload(self): raise NotImplemented diff --git a/lemur/plugins/lemur_aws/plugin.py b/lemur/plugins/lemur_aws/plugin.py index 91bced7d..3cd7c5fa 100644 --- a/lemur/plugins/lemur_aws/plugin.py +++ b/lemur/plugins/lemur_aws/plugin.py @@ -68,19 +68,16 @@ class AWSDestinationPlugin(DestinationPlugin): # } def upload(self, name, body, private_key, cert_chain, options, **kwargs): - if private_key: - try: - iam.upload_cert(self.get_option('accountNumber', options), name, body, private_key, - cert_chain=cert_chain) - except BotoServerError as e: - if e.error_code != 'EntityAlreadyExists': - raise Exception(e) + try: + iam.upload_cert(self.get_option('accountNumber', options), name, body, private_key, + cert_chain=cert_chain) + except BotoServerError as e: + if e.error_code != 'EntityAlreadyExists': + raise Exception(e) - e = self.get_option('elb', options) - if e: - attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId']) - else: - raise Exception("Unable to upload to AWS, private key is required") + e = self.get_option('elb', options) + if e: + attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId']) class AWSSourcePlugin(SourcePlugin): diff --git a/lemur/plugins/lemur_java/plugin.py b/lemur/plugins/lemur_java/plugin.py index eafb01d4..0501c3ed 100644 --- a/lemur/plugins/lemur_java/plugin.py +++ b/lemur/plugins/lemur_java/plugin.py @@ -236,9 +236,6 @@ class JavaKeystoreExportPlugin(ExportPlugin): alias = "blah" with mktemppath() as jks_tmp: - if not key: - raise Exception("Unable to export, no private key found.") - create_keystore(body, chain, jks_tmp, key, alias, passphrase) with open(jks_tmp, 'rb') as f: diff --git a/lemur/tests/test_authorities.py b/lemur/tests/test_authorities.py index 407199a9..b2e1e840 100644 --- a/lemur/tests/test_authorities.py +++ b/lemur/tests/test_authorities.py @@ -25,7 +25,7 @@ def test_authority_input_schema(client, role): assert not errors -def test_user_authority(session, client, authority, role, user): +def test_user_authority(session, client, authority, role, user, issuer_plugin): assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0 u = user['user'] u.roles.append(role)