Ensuring that destinations require private keys by default. (#390)

* Ensuring that destinations require private keys by default.
This commit is contained in:
kevgliss 2016-07-04 15:30:20 -07:00 committed by GitHub
parent 4ee1c21144
commit 4077893d08
9 changed files with 35 additions and 24 deletions

View File

@ -137,6 +137,12 @@ Destination
Destination plugins allow you to propagate certificates managed by Lemur to additional third parties. This provides flexibility when 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. 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:: The DestinationPlugin requires only one function to be implemented::
def upload(self, cert, private_key, cert_chain, options, **kwargs): def upload(self, cert, private_key, cert_chain, options, **kwargs):

View File

@ -11,6 +11,7 @@ from sqlalchemy import Column, Integer, String, Text, func, ForeignKey, DateTime
from sqlalchemy.dialects.postgresql import JSON from sqlalchemy.dialects.postgresql import JSON
from lemur.database import db from lemur.database import db
from lemur.plugins.base import plugins
from lemur.models import roles_authorities from lemur.models import roles_authorities
@ -38,3 +39,7 @@ class Authority(db.Model):
self.description = kwargs.get('description') self.description = kwargs.get('description')
self.authority_certificate = kwargs['authority_certificate'] self.authority_certificate = kwargs['authority_certificate']
self.plugin_name = kwargs['plugin']['slug'] self.plugin_name = kwargs['plugin']['slug']
@property
def plugin(self):
return plugins.get(self.plugin_name)

View File

@ -158,6 +158,7 @@ def update_destinations(target, value, initiator):
:return: :return:
""" """
destination_plugin = plugins.get(value.plugin_name) destination_plugin = plugins.get(value.plugin_name)
try: try:
destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options) destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options)
except Exception as e: except Exception as e:

View File

@ -584,6 +584,11 @@ class Certificates(AuthenticatedResource):
permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles]) permission = CertificatePermission(cert.id, owner_role, [x.name for x in cert.roles])
if permission.can(): 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( return service.update(
certificate_id, certificate_id,
data['owner'], data['owner'],
@ -871,10 +876,13 @@ class CertificateExport(AuthenticatedResource):
plugin = data['plugin']['plugin_object'] plugin = data['plugin']['plugin_object']
if plugin.requires_key: if plugin.requires_key:
if permission.can(): if cert.private_key:
extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options) 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: 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: else:
extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options) extension, passphrase, data = plugin.export(cert.body, cert.chain, cert.private_key, options)

View File

@ -5,7 +5,6 @@
:license: Apache, see LICENSE for more details. :license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com> .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
""" """
import copy
from sqlalchemy import Column, Integer, String, Text from sqlalchemy import Column, Integer, String, Text
from sqlalchemy_utils import JSONType from sqlalchemy_utils import JSONType
from lemur.database import db from lemur.database import db
@ -23,7 +22,4 @@ class Destination(db.Model):
@property @property
def plugin(self): def plugin(self):
p = plugins.get(self.plugin_name) return plugins.get(self.plugin_name)
c = copy.deepcopy(p)
c.options = self.options
return c

View File

@ -11,6 +11,7 @@ from lemur.plugins.base import Plugin
class DestinationPlugin(Plugin): class DestinationPlugin(Plugin):
type = 'destination' type = 'destination'
requires_key = True
def upload(self): def upload(self):
raise NotImplemented raise NotImplemented

View File

@ -68,19 +68,16 @@ class AWSDestinationPlugin(DestinationPlugin):
# } # }
def upload(self, name, body, private_key, cert_chain, options, **kwargs): def upload(self, name, body, private_key, cert_chain, options, **kwargs):
if private_key: try:
try: iam.upload_cert(self.get_option('accountNumber', options), name, body, private_key,
iam.upload_cert(self.get_option('accountNumber', options), name, body, private_key, cert_chain=cert_chain)
cert_chain=cert_chain) except BotoServerError as e:
except BotoServerError as e: if e.error_code != 'EntityAlreadyExists':
if e.error_code != 'EntityAlreadyExists': raise Exception(e)
raise Exception(e)
e = self.get_option('elb', options) e = self.get_option('elb', options)
if e: if e:
attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId']) attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId'])
else:
raise Exception("Unable to upload to AWS, private key is required")
class AWSSourcePlugin(SourcePlugin): class AWSSourcePlugin(SourcePlugin):

View File

@ -236,9 +236,6 @@ class JavaKeystoreExportPlugin(ExportPlugin):
alias = "blah" alias = "blah"
with mktemppath() as jks_tmp: 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) create_keystore(body, chain, jks_tmp, key, alias, passphrase)
with open(jks_tmp, 'rb') as f: with open(jks_tmp, 'rb') as f:

View File

@ -25,7 +25,7 @@ def test_authority_input_schema(client, role):
assert not errors 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 assert client.get(api.url_for(AuthoritiesList), headers=user['token']).json['total'] == 0
u = user['user'] u = user['user']
u.roles.append(role) u.roles.append(role)