San alt name (#468)
This commit is contained in:
parent
f990f92977
commit
1ac1a44e83
|
@ -0,0 +1,5 @@
|
||||||
|
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||||
|
sha: 18d7035de5388cc7775be57f529c154bf541aab9
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: flake8
|
|
@ -29,6 +29,7 @@ before_script:
|
||||||
- psql -c "create user lemur with password 'lemur;'" -U postgres
|
- psql -c "create user lemur with password 'lemur;'" -U postgres
|
||||||
- npm config set registry https://registry.npmjs.org
|
- npm config set registry https://registry.npmjs.org
|
||||||
- npm install -g bower
|
- npm install -g bower
|
||||||
|
- pip install --upgrade setuptools
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make test
|
- make test
|
||||||
|
|
|
@ -115,6 +115,12 @@ class CertificateNestedOutputSchema(LemurOutputSchema):
|
||||||
issuer = fields.Nested(AuthorityNestedOutputSchema)
|
issuer = fields.Nested(AuthorityNestedOutputSchema)
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateCloneSchema(LemurOutputSchema):
|
||||||
|
__envelope__ = False
|
||||||
|
description = fields.String()
|
||||||
|
common_name = fields.String()
|
||||||
|
|
||||||
|
|
||||||
class CertificateOutputSchema(LemurOutputSchema):
|
class CertificateOutputSchema(LemurOutputSchema):
|
||||||
id = fields.Integer()
|
id = fields.Integer()
|
||||||
active = fields.Boolean()
|
active = fields.Boolean()
|
||||||
|
|
|
@ -456,3 +456,49 @@ def get_name_from_arn(arn):
|
||||||
:return: name of the certificate as uploaded to AWS
|
:return: name of the certificate as uploaded to AWS
|
||||||
"""
|
"""
|
||||||
return arn.split("/", 1)[1]
|
return arn.split("/", 1)[1]
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_reissue_range(start, end):
|
||||||
|
"""
|
||||||
|
Determine what the new validity_start and validity_end dates should be.
|
||||||
|
:param start:
|
||||||
|
:param end:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
span = end - start
|
||||||
|
|
||||||
|
new_start = arrow.utcnow().date()
|
||||||
|
new_end = new_start + span
|
||||||
|
|
||||||
|
return new_start, new_end
|
||||||
|
|
||||||
|
|
||||||
|
# TODO pull the OU, O, CN, etc + other extensions.
|
||||||
|
def get_certificate_primitives(certificate):
|
||||||
|
"""
|
||||||
|
Retrieve key primitive from a certificate such that the certificate
|
||||||
|
could be recreated with new expiration or be used to build upon.
|
||||||
|
:param certificate:
|
||||||
|
:return: dict of certificate primitives, should be enough to effectively re-issue
|
||||||
|
certificate via `create`.
|
||||||
|
"""
|
||||||
|
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
|
||||||
|
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
|
||||||
|
|
||||||
|
extensions = {
|
||||||
|
'sub_alt_names': {
|
||||||
|
'names': names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
authority=certificate.authority,
|
||||||
|
common_name=certificate.cn,
|
||||||
|
description=certificate.description,
|
||||||
|
validity_start=start,
|
||||||
|
validity_end=end,
|
||||||
|
destinations=certificate.destinations,
|
||||||
|
roles=certificate.roles,
|
||||||
|
extensions=extensions,
|
||||||
|
owner=certificate.owner
|
||||||
|
)
|
||||||
|
|
|
@ -890,12 +890,24 @@ class CertificateExport(AuthenticatedResource):
|
||||||
return dict(extension=extension, passphrase=passphrase, data=base64.b64encode(data).decode('utf-8'))
|
return dict(extension=extension, passphrase=passphrase, data=base64.b64encode(data).decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateClone(AuthenticatedResource):
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(CertificateExport, self).__init__()
|
||||||
|
|
||||||
|
@validate_schema(None, certificate_output_schema)
|
||||||
|
def get(self, certificate_id):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(CertificatesList, '/certificates', endpoint='certificates')
|
api.add_resource(CertificatesList, '/certificates', endpoint='certificates')
|
||||||
api.add_resource(Certificates, '/certificates/<int:certificate_id>', endpoint='certificate')
|
api.add_resource(Certificates, '/certificates/<int:certificate_id>', endpoint='certificate')
|
||||||
api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats')
|
api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats')
|
||||||
api.add_resource(CertificatesUpload, '/certificates/upload', endpoint='certificateUpload')
|
api.add_resource(CertificatesUpload, '/certificates/upload', endpoint='certificateUpload')
|
||||||
api.add_resource(CertificatePrivateKey, '/certificates/<int:certificate_id>/key', endpoint='privateKeyCertificates')
|
api.add_resource(CertificatePrivateKey, '/certificates/<int:certificate_id>/key', endpoint='privateKeyCertificates')
|
||||||
api.add_resource(CertificateExport, '/certificates/<int:certificate_id>/export', endpoint='exportCertificate')
|
api.add_resource(CertificateExport, '/certificates/<int:certificate_id>/export', endpoint='exportCertificate')
|
||||||
|
api.add_resource(CertificateClone, '/certificates/<int:certificate_id>/clone', endpoint='cloneCertificate')
|
||||||
api.add_resource(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates',
|
api.add_resource(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates',
|
||||||
endpoint='notificationCertificates')
|
endpoint='notificationCertificates')
|
||||||
api.add_resource(CertificatesReplacementsList, '/certificates/<int:certificate_id>/replacements',
|
api.add_resource(CertificatesReplacementsList, '/certificates/<int:certificate_id>/replacements',
|
||||||
|
|
|
@ -15,7 +15,7 @@ def certificate_name(common_name, issuer, not_before, not_after, san):
|
||||||
:param not_after:
|
:param not_after:
|
||||||
:param issuer:
|
:param issuer:
|
||||||
:param not_before:
|
:param not_before:
|
||||||
:rtype : str
|
:rtype: str
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if san:
|
if san:
|
||||||
|
|
|
@ -24,7 +24,7 @@ def test_export_truststore_default_password(app):
|
||||||
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
||||||
|
|
||||||
assert actual[0] == 'jks'
|
assert actual[0] == 'jks'
|
||||||
assert isinstance(actual[1], str)
|
assert isinstance(actual[1], six.string_types)
|
||||||
assert isinstance(actual[2], bytes)
|
assert isinstance(actual[2], bytes)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ class SubAltNameSchema(BaseExtensionSchema):
|
||||||
|
|
||||||
@validates_schema
|
@validates_schema
|
||||||
def check_sensitive(self, data):
|
def check_sensitive(self, data):
|
||||||
if data['name_type'] == 'DNSName':
|
if data.get('name_type') == 'DNSName':
|
||||||
validators.sensitive_domain(data['value'])
|
validators.sensitive_domain(data['value'])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ angular.module('lemur')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!angular.isString(this.subAltType)) {
|
if (!angular.isString(this.subAltType)) {
|
||||||
this.subAltType = 'CNAME';
|
this.subAltType = 'DNSName';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (angular.isString(this.subAltValue) && angular.isString(this.subAltType)) {
|
if (angular.isString(this.subAltValue) && angular.isString(this.subAltType)) {
|
||||||
|
|
|
@ -107,7 +107,9 @@ def notification(session):
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def certificate(session):
|
def certificate(session):
|
||||||
c = CertificateFactory()
|
u = UserFactory()
|
||||||
|
a = AuthorityFactory()
|
||||||
|
c = CertificateFactory(user=u, authority=a)
|
||||||
session.commit()
|
session.commit()
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from __future__ import unicode_literals # at top of module
|
from __future__ import unicode_literals # at top of module
|
||||||
|
|
||||||
import pytest
|
|
||||||
import json
|
import json
|
||||||
|
import pytest
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
from lemur.certificates.views import * # noqa
|
from lemur.certificates.views import * # noqa
|
||||||
|
|
||||||
|
@ -9,6 +12,32 @@ from lemur.tests.vectors import VALID_ADMIN_HEADER_TOKEN, VALID_USER_HEADER_TOKE
|
||||||
INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
|
INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, PRIVATE_KEY_STR
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_certificate_primitives(certificate):
|
||||||
|
from lemur.certificates.service import get_certificate_primitives
|
||||||
|
|
||||||
|
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'common_name': certificate.cn,
|
||||||
|
'owner': certificate.owner,
|
||||||
|
'authority': certificate.authority,
|
||||||
|
'description': certificate.description,
|
||||||
|
'extensions': {
|
||||||
|
'sub_alt_names': {
|
||||||
|
'names': names
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'destinations': [],
|
||||||
|
'roles': [],
|
||||||
|
'validity_end': datetime.date(year=2021, month=5, day=7),
|
||||||
|
'validity_start': datetime.date(year=2016, month=10, day=30)
|
||||||
|
}
|
||||||
|
|
||||||
|
with freeze_time(datetime.date(year=2016, month=10, day=30)):
|
||||||
|
primitives = get_certificate_primitives(certificate)
|
||||||
|
assert data == primitives
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_edit_schema(session):
|
def test_certificate_edit_schema(session):
|
||||||
from lemur.certificates.schemas import CertificateEditInputSchema
|
from lemur.certificates.schemas import CertificateEditInputSchema
|
||||||
|
|
||||||
|
@ -203,7 +232,7 @@ def test_certificate_valid_dates(client, authority):
|
||||||
assert not errors
|
assert not errors
|
||||||
|
|
||||||
|
|
||||||
def test_sub_alt_name_schema():
|
def test_sub_alt_name_schema(session):
|
||||||
from lemur.schemas import SubAltNameSchema, SubAltNamesSchema
|
from lemur.schemas import SubAltNameSchema, SubAltNamesSchema
|
||||||
input_data = {'nameType': 'DNSName', 'value': 'test.example.com'}
|
input_data = {'nameType': 'DNSName', 'value': 'test.example.com'}
|
||||||
|
|
||||||
|
@ -212,7 +241,6 @@ def test_sub_alt_name_schema():
|
||||||
assert data == {'name_type': 'DNSName', 'value': 'test.example.com'}
|
assert data == {'name_type': 'DNSName', 'value': 'test.example.com'}
|
||||||
|
|
||||||
data, errors = SubAltNameSchema().dumps(data)
|
data, errors = SubAltNameSchema().dumps(data)
|
||||||
assert data == json.dumps(input_data)
|
|
||||||
assert not errors
|
assert not errors
|
||||||
|
|
||||||
input_datas = {'names': [input_data]}
|
input_datas = {'names': [input_data]}
|
||||||
|
@ -225,6 +253,10 @@ def test_sub_alt_name_schema():
|
||||||
assert data == json.dumps(input_datas)
|
assert data == json.dumps(input_datas)
|
||||||
assert not errors
|
assert not errors
|
||||||
|
|
||||||
|
input_data = {'nameType': 'CNAME', 'value': 'test.example.com'}
|
||||||
|
data, errors = SubAltNameSchema().load(input_data)
|
||||||
|
assert errors
|
||||||
|
|
||||||
|
|
||||||
def test_key_usage_schema():
|
def test_key_usage_schema():
|
||||||
from lemur.schemas import KeyUsageSchema
|
from lemur.schemas import KeyUsageSchema
|
||||||
|
|
|
@ -14,6 +14,13 @@ def test_private_key(session):
|
||||||
private_key('invalid_private_key')
|
private_key('invalid_private_key')
|
||||||
|
|
||||||
|
|
||||||
|
def test_sub_alt_type(session):
|
||||||
|
from lemur.common.validators import sub_alt_type
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
sub_alt_type('CNAME')
|
||||||
|
|
||||||
|
|
||||||
def test_dates(session):
|
def test_dates(session):
|
||||||
from lemur.common.validators import dates
|
from lemur.common.validators import dates
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue