Initial work allowing certificates to be revoked. (#941)
* Initial work allowing for certificates to be revoked.
This commit is contained in:
parent
ea6f5c920b
commit
bb08b1e637
@ -80,6 +80,7 @@ def get_or_increase_name(name):
|
||||
class Certificate(db.Model):
|
||||
__tablename__ = 'certificates'
|
||||
id = Column(Integer, primary_key=True)
|
||||
external_id = Column(String(128))
|
||||
owner = Column(String(128), nullable=False)
|
||||
name = Column(String(128), unique=True)
|
||||
description = Column(String(1024))
|
||||
@ -162,6 +163,7 @@ class Certificate(db.Model):
|
||||
self.signing_algorithm = defaults.signing_algorithm(cert)
|
||||
self.bits = defaults.bitstrength(cert)
|
||||
self.serial = defaults.serial(cert)
|
||||
self.external_id = kwargs.get('external_id')
|
||||
|
||||
for domain in defaults.domains(cert):
|
||||
self.domains.append(Domain(name=domain))
|
||||
|
@ -172,6 +172,7 @@ class CertificateCloneSchema(LemurOutputSchema):
|
||||
|
||||
class CertificateOutputSchema(LemurOutputSchema):
|
||||
id = fields.Integer()
|
||||
external_id = fields.String()
|
||||
bits = fields.Integer()
|
||||
body = fields.String()
|
||||
chain = fields.String()
|
||||
@ -253,6 +254,10 @@ class CertificateNotificationOutputSchema(LemurOutputSchema):
|
||||
endpoints = fields.Nested(EndpointNestedOutputSchema, many=True, missing=[])
|
||||
|
||||
|
||||
class CertificateRevokeSchema(LemurInputSchema):
|
||||
comments = fields.String()
|
||||
|
||||
|
||||
certificate_input_schema = CertificateInputSchema()
|
||||
certificate_output_schema = CertificateOutputSchema()
|
||||
certificates_output_schema = CertificateOutputSchema(many=True)
|
||||
@ -260,3 +265,4 @@ certificate_upload_input_schema = CertificateUploadInputSchema()
|
||||
certificate_export_input_schema = CertificateExportInputSchema()
|
||||
certificate_edit_input_schema = CertificateEditInputSchema()
|
||||
certificate_notification_output_schema = CertificateNotificationOutputSchema()
|
||||
certificate_revoke_schema = CertificateRevokeSchema()
|
||||
|
@ -180,8 +180,8 @@ def mint(**kwargs):
|
||||
private_key = None
|
||||
csr_imported.send(authority=authority, csr=csr)
|
||||
|
||||
cert_body, cert_chain = issuer.create_certificate(csr, kwargs)
|
||||
return cert_body, private_key, cert_chain,
|
||||
cert_body, cert_chain, external_id = issuer.create_certificate(csr, kwargs)
|
||||
return cert_body, private_key, cert_chain, external_id
|
||||
|
||||
|
||||
def import_certificate(**kwargs):
|
||||
@ -234,10 +234,11 @@ def create(**kwargs):
|
||||
"""
|
||||
Creates a new certificate.
|
||||
"""
|
||||
cert_body, private_key, cert_chain = mint(**kwargs)
|
||||
cert_body, private_key, cert_chain, external_id = mint(**kwargs)
|
||||
kwargs['body'] = cert_body
|
||||
kwargs['private_key'] = private_key
|
||||
kwargs['chain'] = cert_chain
|
||||
kwargs['external_id'] = external_id
|
||||
|
||||
roles = create_certificate_roles(**kwargs)
|
||||
|
||||
|
@ -18,8 +18,16 @@ from lemur.auth.service import AuthenticatedResource
|
||||
from lemur.auth.permissions import AuthorityPermission, CertificatePermission
|
||||
|
||||
from lemur.certificates import service
|
||||
from lemur.certificates.schemas import certificate_input_schema, certificate_output_schema, \
|
||||
certificate_upload_input_schema, certificates_output_schema, certificate_export_input_schema, certificate_edit_input_schema
|
||||
from lemur.plugins.base import plugins
|
||||
from lemur.certificates.schemas import (
|
||||
certificate_input_schema,
|
||||
certificate_output_schema,
|
||||
certificate_upload_input_schema,
|
||||
certificates_output_schema,
|
||||
certificate_export_input_schema,
|
||||
certificate_edit_input_schema,
|
||||
certificate_revoke_schema
|
||||
)
|
||||
|
||||
from lemur.roles import service as role_service
|
||||
from lemur.logs import service as log_service
|
||||
@ -944,6 +952,73 @@ class CertificateExport(AuthenticatedResource):
|
||||
return dict(extension=extension, passphrase=passphrase, data=base64.b64encode(data).decode('utf-8'))
|
||||
|
||||
|
||||
class CertificateRevoke(AuthenticatedResource):
|
||||
def __init__(self):
|
||||
self.reqparse = reqparse.RequestParser()
|
||||
super(CertificateRevoke, self).__init__()
|
||||
|
||||
@validate_schema(certificate_revoke_schema, None)
|
||||
def put(self, certificate_id, data=None):
|
||||
"""
|
||||
.. http:put:: /certificates/1/revoke
|
||||
|
||||
Revoke a certificate
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /certificates/1/revoke HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
{
|
||||
"comments": "Certificate no longer needed"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: text/javascript
|
||||
|
||||
{
|
||||
'id': 1
|
||||
}
|
||||
|
||||
:reqheader Authorization: OAuth token to authenticate
|
||||
:statuscode 200: no error
|
||||
:statuscode 403: unauthenticated
|
||||
|
||||
"""
|
||||
cert = service.get(certificate_id)
|
||||
|
||||
if not cert:
|
||||
return dict(message="Cannot find specified certificate"), 404
|
||||
|
||||
# allow creators
|
||||
if g.current_user != cert.user:
|
||||
owner_role = role_service.get_by_name(cert.owner)
|
||||
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
|
||||
|
||||
if not permission.can():
|
||||
return dict(message='You are not authorized to revoke this certificate.'), 403
|
||||
|
||||
if not cert.external_id:
|
||||
return dict(message='Cannot revoke certificate. No external id found.'), 400
|
||||
|
||||
if cert.endpoints:
|
||||
return dict(message='Cannot revoke certificate. Endpoints are deployed with the given certificate.'), 403
|
||||
|
||||
plugin = plugins.get(cert.authority.plugin_name)
|
||||
plugin.revoke_certificate(cert, data)
|
||||
log_service.create(g.current_user, 'revoke_cert', certificate=cert)
|
||||
return dict(id=cert.id)
|
||||
|
||||
|
||||
api.add_resource(CertificateRevoke, '/certificates/<int:certificate_id>/revoke', endpoint='revokeCertificate')
|
||||
api.add_resource(CertificatesList, '/certificates', endpoint='certificates')
|
||||
api.add_resource(Certificates, '/certificates/<int:certificate_id>', endpoint='certificate')
|
||||
api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats')
|
||||
|
@ -18,6 +18,6 @@ class Log(db.Model):
|
||||
__tablename__ = 'logs'
|
||||
id = Column(Integer, primary_key=True)
|
||||
certificate_id = Column(Integer, ForeignKey('certificates.id'))
|
||||
log_type = Column(Enum('key_view', 'create_cert', 'update_cert', name='log_type'), nullable=False)
|
||||
log_type = Column(Enum('key_view', 'create_cert', 'update_cert', 'revoke_cert', name='log_type'), nullable=False)
|
||||
logged_at = Column(ArrowType(), PassiveDefault(func.now()), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
|
||||
|
@ -3,6 +3,9 @@ from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from logging.config import fileConfig
|
||||
|
||||
import alembic_autogenerate_enums
|
||||
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
@ -14,28 +14,8 @@ from alembic import op
|
||||
|
||||
|
||||
def upgrade():
|
||||
connection = None
|
||||
|
||||
if not op.get_context().as_sql:
|
||||
connection = op.get_bind()
|
||||
connection.execution_options(isolation_level='AUTOCOMMIT')
|
||||
|
||||
op.execute("ALTER TYPE log_type ADD VALUE 'create_cert'")
|
||||
op.execute("ALTER TYPE log_type ADD VALUE 'update_cert'")
|
||||
|
||||
if connection is not None:
|
||||
connection.execution_options(isolation_level='READ_COMMITTED')
|
||||
op.sync_enum_values('public', 'log_type', ['key_view'], ['create_cert', 'key_view', 'update_cert'])
|
||||
|
||||
|
||||
def downgrade():
|
||||
connection = None
|
||||
|
||||
if not op.get_context().as_sql:
|
||||
connection = op.get_bind()
|
||||
connection.execution_options(isolation_level='AUTOCOMMIT')
|
||||
|
||||
op.execute("ALTER TYPE log_type DROP VALUE 'create_cert'")
|
||||
op.execute("ALTER TYPE log_type DROP VALUE 'update_cert'")
|
||||
|
||||
if connection is not None:
|
||||
connection.execution_options(isolation_level='READ_COMMITTED')
|
||||
op.sync_enum_values('public', 'log_type', ['create_cert', 'key_view', 'update_cert'], ['key_view'])
|
||||
|
28
lemur/migrations/versions/b29e2c4bf8c9_.py
Normal file
28
lemur/migrations/versions/b29e2c4bf8c9_.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""Adds external ID checking and modifying enum
|
||||
|
||||
Revision ID: b29e2c4bf8c9
|
||||
Revises: 1ae8e3104db8
|
||||
Create Date: 2017-09-26 10:50:35.740367
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b29e2c4bf8c9'
|
||||
down_revision = '1ae8e3104db8'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('certificates', sa.Column('external_id', sa.String(128), nullable=True))
|
||||
op.sync_enum_values('public', 'log_type', ['create_cert', 'key_view', 'update_cert'], ['create_cert', 'key_view', 'revoke_cert', 'update_cert'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.sync_enum_values('public', 'log_type', ['create_cert', 'key_view', 'revoke_cert', 'update_cert'], ['create_cert', 'key_view', 'update_cert'])
|
||||
op.drop_column('certificates', 'external_id')
|
||||
# ### end Alembic commands ###
|
@ -21,3 +21,6 @@ class IssuerPlugin(Plugin):
|
||||
|
||||
def create_authority(self, options):
|
||||
raise NotImplementedError
|
||||
|
||||
def revoke_certificate(self, certificate, comments):
|
||||
raise NotImplementedError
|
||||
|
@ -49,7 +49,8 @@ class CfsslIssuerPlugin(IssuerPlugin):
|
||||
response_json = json.loads(response.content.decode('utf_8'))
|
||||
cert = response_json['result']['certificate']
|
||||
|
||||
return cert, current_app.config.get('CFSSL_INTERMEDIATE'),
|
||||
# TODO add external ID
|
||||
return cert, current_app.config.get('CFSSL_INTERMEDIATE'), None
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
@ -187,7 +187,7 @@ class CryptographyIssuerPlugin(IssuerPlugin):
|
||||
"""
|
||||
current_app.logger.debug("Issuing new cryptography certificate with options: {0}".format(options))
|
||||
cert_pem, chain_cert_pem = issue_certificate(csr, options)
|
||||
return cert_pem, chain_cert_pem
|
||||
return cert_pem, chain_cert_pem, None
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
@ -312,7 +312,17 @@ class DigiCertIssuerPlugin(IssuerPlugin):
|
||||
# retrieve certificate
|
||||
certificate_url = "{0}/services/v2/certificate/{1}/download/format/pem_all".format(base_url, certificate_id)
|
||||
end_entity, intermediate, root = pem.parse(self.session.get(certificate_url).content)
|
||||
return "\n".join(str(end_entity).splitlines()), "\n".join(str(intermediate).splitlines())
|
||||
return "\n".join(str(end_entity).splitlines()), "\n".join(str(intermediate).splitlines()), certificate_id
|
||||
|
||||
def revoke_certificate(self, certificate, comments):
|
||||
"""Revoke a Digicert certificate."""
|
||||
base_url = current_app.config.get('DIGICERT_URL')
|
||||
|
||||
# make certificate revoke request
|
||||
create_url = '{0}/certificate/{1}/revoke'.format(base_url, certificate.external_id)
|
||||
metrics.send('digicert_revoke_certificate', 'counter', 1)
|
||||
response = self.session.put(create_url, data=json.dumps({'comments': comments}))
|
||||
return handle_response(response)
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
@ -379,7 +389,22 @@ class DigiCertCISIssuerPlugin(IssuerPlugin):
|
||||
|
||||
self.session.headers.pop('Accept')
|
||||
end_entity = pem.parse(certificate_pem)[0]
|
||||
return "\n".join(str(end_entity).splitlines()), current_app.config.get('DIGICERT_CIS_INTERMEDIATE')
|
||||
return "\n".join(str(end_entity).splitlines()), current_app.config.get('DIGICERT_CIS_INTERMEDIATE'), data['id']
|
||||
|
||||
def revoke_certificate(self, certificate, comments):
|
||||
"""Revoke a Digicert certificate."""
|
||||
base_url = current_app.config.get('DIGICERT_CIS_URL')
|
||||
|
||||
# make certificate revoke request
|
||||
revoke_url = '{0}/platform/cis/certificate/{1}/revoke'.format(base_url, certificate.external_id)
|
||||
metrics.send('digicert_revoke_certificate_success', 'counter', 1)
|
||||
response = self.session.put(revoke_url, data=json.dumps({'comments': comments}))
|
||||
|
||||
if response.status_code != 204:
|
||||
metrics.send('digicert_revoke_certificate_failure', 'counter', 1)
|
||||
raise Exception('Failed to revoke certificate.')
|
||||
|
||||
metrics.send('digicert_revoke_certificate_success', 'counter', 1)
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
@ -171,7 +171,7 @@ ghi
|
||||
adapter.register_uri('GET', 'mock://www.digicert.com/services/v2/certificate/cert123/download/format/pem_all', text=pem_fixture)
|
||||
subject.session.mount('mock', adapter)
|
||||
|
||||
cert, intermediate = subject.create_certificate("", {'common_name': 'test.com'})
|
||||
cert, intermediate, external_id = subject.create_certificate("", {'common_name': 'test.com'})
|
||||
|
||||
assert cert == "-----BEGIN CERTIFICATE-----\nabc\n-----END CERTIFICATE-----"
|
||||
assert intermediate == "-----BEGIN CERTIFICATE-----\ndef\n-----END CERTIFICATE-----"
|
||||
|
@ -195,7 +195,8 @@ class VerisignIssuerPlugin(IssuerPlugin):
|
||||
|
||||
response = self.session.post(url, data=data)
|
||||
cert = handle_response(response.content)['Response']['Certificate']
|
||||
return cert, current_app.config.get('VERISIGN_INTERMEDIATE'),
|
||||
# TODO add external id
|
||||
return cert, current_app.config.get('VERISIGN_INTERMEDIATE'), None
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
19
lemur/static/app/angular/app.js
vendored
19
lemur/static/app/angular/app.js
vendored
@ -58,6 +58,25 @@
|
||||
});
|
||||
});
|
||||
|
||||
lemur.directive('compareTo', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
scope: {
|
||||
otherModelValue: '=compareTo'
|
||||
},
|
||||
link: function(scope, element, attributes, ngModel) {
|
||||
|
||||
ngModel.$validators.compareTo = function(modelValue) {
|
||||
return modelValue === scope.otherModelValue;
|
||||
};
|
||||
|
||||
scope.$watch('otherModelValue', function() {
|
||||
ngModel.$validate();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
lemur.service('MomentService', function () {
|
||||
this.diffMoment = function (start, end) {
|
||||
if (end !== 'None') {
|
||||
|
@ -328,4 +328,36 @@ angular.module('lemur')
|
||||
$scope.authorityService = AuthorityService;
|
||||
$scope.destinationService = DestinationService;
|
||||
$scope.notificationService = NotificationService;
|
||||
})
|
||||
|
||||
.controller('CertificateRevokeController', function ($scope, $uibModalInstance, CertificateApi, CertificateService, LemurRestangular, NotificationService, toaster, revokeId) {
|
||||
CertificateApi.get(revokeId).then(function (certificate) {
|
||||
$scope.certificate = certificate;
|
||||
});
|
||||
|
||||
$scope.cancel = function () {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.revoke = function (certificate) {
|
||||
CertificateService.revoke(certificate).then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
body: 'Successfully revoked!'
|
||||
});
|
||||
$uibModalInstance.close();
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
body: 'lemur-bad-request',
|
||||
bodyOutputType: 'directive',
|
||||
directiveData: response.data,
|
||||
timeout: 100000
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
@ -0,0 +1,48 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 class="modal-title">Revoke <span class="text-muted"><small>{{ certificate.name }}</small></span></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form name="revokeForm" ng-if="!certificate.endpoints.length" novalidate>
|
||||
<p><strong>Certificate revocation is final. Once revoked the certificate is no longer valid.</strong></p>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': revokeForm.confirm.$invalid, 'has-success': !revokeForm.$invalid&&revokeForm.confirm.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Confirm Revocation
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="confirm" ng-model="confirm" placeholder='{{ certificate.name }}'
|
||||
uib-tooltip="Confirm revocation by entering '{{ certificate.name }}'"
|
||||
class="form-control"
|
||||
compare-to="certificate.name"
|
||||
required/>
|
||||
|
||||
<p ng-show="revokeForm.confirm.$invalid && !revokeForm.confirm.$pristine" class="help-block">
|
||||
You must confirm certificate revocation.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div ng-if="certificate.endpoints.length">
|
||||
<p><strong>Certificate cannot be revoked, it is associated with the following endpoints. Disassociate this
|
||||
certificate
|
||||
before revoking.</strong></p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="endpoint in certificate.endpoints">
|
||||
<span class="pull-right"><label class="label label-default">{{ endpoint.type }}</label></span>
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ endpoint.name }}</li>
|
||||
<li><span class="text-muted">{{ endpoint.dnsname }}</span></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" ng-click="revoke(certificate)" ng-disabled="revokeForm.confirm.$invalid"
|
||||
class="btn btn-danger">Revoke
|
||||
</button>
|
||||
<button ng-click="cancel()" class="btn">Cancel</button>
|
||||
</div>
|
@ -258,5 +258,9 @@ angular.module('lemur')
|
||||
return certificate.customPOST(certificate.exportOptions, 'export');
|
||||
};
|
||||
|
||||
CertificateService.revoke = function (certificate) {
|
||||
return certificate.customPUT(certificate.externalId, 'revoke');
|
||||
};
|
||||
|
||||
return CertificateService;
|
||||
});
|
||||
|
@ -190,4 +190,19 @@ angular.module('lemur')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.revoke = function (certificateId) {
|
||||
$uibModal.open({
|
||||
animation: true,
|
||||
controller: 'CertificateRevokeController',
|
||||
templateUrl: '/angular/certificates/certificate/revoke.tpl.html',
|
||||
size: 'lg',
|
||||
backdrop: 'static',
|
||||
resolve: {
|
||||
revokeId: function () {
|
||||
return certificateId;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
@ -60,6 +60,7 @@
|
||||
<li><a href ng-click="edit(certificate.id)">Edit</a></li>
|
||||
<li><a href ng-click="clone(certificate.id)">Clone</a></li>
|
||||
<li><a href ng-click="export(certificate.id)">Export</a></li>
|
||||
<li><a href ng-click="revoke(certificate.id)">Revoke</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -188,27 +189,21 @@
|
||||
<uib-tab>
|
||||
<uib-tab-heading>
|
||||
Chain
|
||||
<button class="btn btn-xs btn-default clipboard-btn glyphicon glyphicon-copy"
|
||||
uib-tooltip="Copy chain to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||
text="certificate.chain"></button>
|
||||
<i class="glyphicon glyphicon-copy" style="cursor: pointer" clipboard text="certificate.chain"></i>
|
||||
</uib-tab-heading>
|
||||
<pre style="width: 100%">{{ certificate.chain }}</pre>
|
||||
</uib-tab>
|
||||
<uib-tab>
|
||||
<uib-tab-heading>
|
||||
Public Certificate
|
||||
<button class="btn btn-xs btn-default clipboard-btn glyphicon glyphicon-copy"
|
||||
uib-tooltip="Copy certificate to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||
text="certificate.body"></button>
|
||||
<i class="glyphicon glyphicon-copy" style="cursor: pointer" clipboard text="certificate.body"></i>
|
||||
</uib-tab-heading>
|
||||
<pre style="width: 100%">{{ certificate.body }}</pre>
|
||||
</uib-tab>
|
||||
<uib-tab ng-click="loadPrivateKey(certificate)">
|
||||
<uib-tab-heading>
|
||||
Private Key
|
||||
<button class="btn btn-xs btn-default clipboard-btn glyphicon glyphicon-copy"
|
||||
uib-tooltip="Copy key to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||
text="certificate.privateKey"></button>
|
||||
<i class="glyphicon glyphicon-copy" style="cursor: pointer" clipboard text="certificate.privateKey"></i>
|
||||
</uib-tab-heading>
|
||||
<pre style="width: 100%">{{ certificate.privateKey }}</pre>
|
||||
</uib-tab>
|
||||
|
@ -15,7 +15,7 @@ class TestIssuerPlugin(IssuerPlugin):
|
||||
super(TestIssuerPlugin, self).__init__(*args, **kwargs)
|
||||
|
||||
def create_certificate(self, csr, issuer_options):
|
||||
return INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR
|
||||
return INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, None
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
@ -430,7 +430,7 @@ def test_get_account_number(client):
|
||||
|
||||
def test_mint_certificate(issuer_plugin, authority):
|
||||
from lemur.certificates.service import mint
|
||||
cert_body, private_key, chain = mint(authority=authority, csr=CSR_STR)
|
||||
cert_body, private_key, chain, external_id = mint(authority=authority, csr=CSR_STR)
|
||||
assert cert_body == INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR
|
||||
|
||||
|
||||
|
5
setup.py
5
setup.py
@ -65,8 +65,9 @@ install_requires = [
|
||||
'pem==17.1.0',
|
||||
'raven[flask]==6.2.1',
|
||||
'jinja2==2.9.6',
|
||||
'pyldap==2.4.37', # required by ldap auth provider
|
||||
'paramiko==2.3.1' # required for lemur_linuxdst plugin
|
||||
'pyldap==2.4.37', # required by ldap auth provider
|
||||
'paramiko==2.3.1', # required for lemur_linuxdst plugin
|
||||
'alembic-autogenerate-enums==0.0.2'
|
||||
]
|
||||
|
||||
tests_require = [
|
||||
|
Loading…
Reference in New Issue
Block a user