From 1ac1a44e83b33387af5a73f755b0a6a391c0cc54 Mon Sep 17 00:00:00 2001 From: kevgliss Date: Mon, 31 Oct 2016 11:00:15 -0700 Subject: [PATCH] San alt name (#468) --- .pre-commit-config.yaml | 5 ++ .travis.yml | 1 + lemur/certificates/schemas.py | 6 +++ lemur/certificates/service.py | 46 +++++++++++++++++++ lemur/certificates/views.py | 12 +++++ lemur/common/defaults.py | 2 +- lemur/plugins/lemur_java/tests/test_java.py | 2 +- lemur/schemas.py | 2 +- .../certificates/certificate/certificate.js | 2 +- .../app/angular/certificates/services.js | 2 +- .../destinations/destination/destination.js | 2 +- .../app/angular/sources/source/source.js | 2 +- lemur/tests/conftest.py | 4 +- lemur/tests/test_certificates.py | 38 +++++++++++++-- lemur/tests/test_validators.py | 7 +++ setup.py | 1 + 16 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..a4c598af --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +- repo: git://github.com/pre-commit/pre-commit-hooks + sha: 18d7035de5388cc7775be57f529c154bf541aab9 + hooks: + - id: trailing-whitespace + - id: flake8 diff --git a/.travis.yml b/.travis.yml index 336db5e3..5bfe3cd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ before_script: - psql -c "create user lemur with password 'lemur;'" -U postgres - npm config set registry https://registry.npmjs.org - npm install -g bower + - pip install --upgrade setuptools script: - make test diff --git a/lemur/certificates/schemas.py b/lemur/certificates/schemas.py index 48f6baa3..4741e494 100644 --- a/lemur/certificates/schemas.py +++ b/lemur/certificates/schemas.py @@ -115,6 +115,12 @@ class CertificateNestedOutputSchema(LemurOutputSchema): issuer = fields.Nested(AuthorityNestedOutputSchema) +class CertificateCloneSchema(LemurOutputSchema): + __envelope__ = False + description = fields.String() + common_name = fields.String() + + class CertificateOutputSchema(LemurOutputSchema): id = fields.Integer() active = fields.Boolean() diff --git a/lemur/certificates/service.py b/lemur/certificates/service.py index 7fa9b05c..b7ef156f 100644 --- a/lemur/certificates/service.py +++ b/lemur/certificates/service.py @@ -456,3 +456,49 @@ def get_name_from_arn(arn): :return: name of the certificate as uploaded to AWS """ 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 + ) diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index 008383a2..32c70327 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -890,12 +890,24 @@ class CertificateExport(AuthenticatedResource): 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(Certificates, '/certificates/', endpoint='certificate') api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificateStats') api.add_resource(CertificatesUpload, '/certificates/upload', endpoint='certificateUpload') api.add_resource(CertificatePrivateKey, '/certificates//key', endpoint='privateKeyCertificates') api.add_resource(CertificateExport, '/certificates//export', endpoint='exportCertificate') +api.add_resource(CertificateClone, '/certificates//clone', endpoint='cloneCertificate') api.add_resource(NotificationCertificatesList, '/notifications//certificates', endpoint='notificationCertificates') api.add_resource(CertificatesReplacementsList, '/certificates//replacements', diff --git a/lemur/common/defaults.py b/lemur/common/defaults.py index 7ef03c25..6e6c11dc 100644 --- a/lemur/common/defaults.py +++ b/lemur/common/defaults.py @@ -15,7 +15,7 @@ def certificate_name(common_name, issuer, not_before, not_after, san): :param not_after: :param issuer: :param not_before: - :rtype : str + :rtype: str :return: """ if san: diff --git a/lemur/plugins/lemur_java/tests/test_java.py b/lemur/plugins/lemur_java/tests/test_java.py index 6df7ff1c..037031e7 100644 --- a/lemur/plugins/lemur_java/tests/test_java.py +++ b/lemur/plugins/lemur_java/tests/test_java.py @@ -24,7 +24,7 @@ def test_export_truststore_default_password(app): actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options) assert actual[0] == 'jks' - assert isinstance(actual[1], str) + assert isinstance(actual[1], six.string_types) assert isinstance(actual[2], bytes) diff --git a/lemur/schemas.py b/lemur/schemas.py index a876af59..4574a5a0 100644 --- a/lemur/schemas.py +++ b/lemur/schemas.py @@ -220,7 +220,7 @@ class SubAltNameSchema(BaseExtensionSchema): @validates_schema def check_sensitive(self, data): - if data['name_type'] == 'DNSName': + if data.get('name_type') == 'DNSName': validators.sensitive_domain(data['value']) diff --git a/lemur/static/app/angular/certificates/certificate/certificate.js b/lemur/static/app/angular/certificates/certificate/certificate.js index ce767919..d7832115 100644 --- a/lemur/static/app/angular/certificates/certificate/certificate.js +++ b/lemur/static/app/angular/certificates/certificate/certificate.js @@ -99,7 +99,7 @@ angular.module('lemur') $scope.authorities = authorities; }); }; - + $scope.dateOptions = { formatYear: 'yy', maxDate: new Date(2020, 5, 22), diff --git a/lemur/static/app/angular/certificates/services.js b/lemur/static/app/angular/certificates/services.js index 2a7e0d27..2d1faf9c 100644 --- a/lemur/static/app/angular/certificates/services.js +++ b/lemur/static/app/angular/certificates/services.js @@ -28,7 +28,7 @@ angular.module('lemur') } if (!angular.isString(this.subAltType)) { - this.subAltType = 'CNAME'; + this.subAltType = 'DNSName'; } if (angular.isString(this.subAltValue) && angular.isString(this.subAltType)) { diff --git a/lemur/static/app/angular/destinations/destination/destination.js b/lemur/static/app/angular/destinations/destination/destination.js index 5b35d515..dcca9962 100644 --- a/lemur/static/app/angular/destinations/destination/destination.js +++ b/lemur/static/app/angular/destinations/destination/destination.js @@ -47,7 +47,7 @@ angular.module('lemur') }); }); }); - + $scope.save = function (destination) { DestinationService.update(destination).then( function () { diff --git a/lemur/static/app/angular/sources/source/source.js b/lemur/static/app/angular/sources/source/source.js index 6ae256ba..1d5c1641 100644 --- a/lemur/static/app/angular/sources/source/source.js +++ b/lemur/static/app/angular/sources/source/source.js @@ -47,7 +47,7 @@ angular.module('lemur') }); }); }); - + PluginService.getByType('source').then(function (plugins) { $scope.plugins = plugins; _.each($scope.plugins, function (plugin) { diff --git a/lemur/tests/conftest.py b/lemur/tests/conftest.py index 24637277..61614266 100644 --- a/lemur/tests/conftest.py +++ b/lemur/tests/conftest.py @@ -107,7 +107,9 @@ def notification(session): @pytest.fixture def certificate(session): - c = CertificateFactory() + u = UserFactory() + a = AuthorityFactory() + c = CertificateFactory(user=u, authority=a) session.commit() return c diff --git a/lemur/tests/test_certificates.py b/lemur/tests/test_certificates.py index 9b4b9944..f308fb75 100644 --- a/lemur/tests/test_certificates.py +++ b/lemur/tests/test_certificates.py @@ -1,7 +1,10 @@ from __future__ import unicode_literals # at top of module -import pytest import json +import pytest +import datetime + +from freezegun import freeze_time 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 +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): from lemur.certificates.schemas import CertificateEditInputSchema @@ -203,7 +232,7 @@ def test_certificate_valid_dates(client, authority): assert not errors -def test_sub_alt_name_schema(): +def test_sub_alt_name_schema(session): from lemur.schemas import SubAltNameSchema, SubAltNamesSchema 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'} data, errors = SubAltNameSchema().dumps(data) - assert data == json.dumps(input_data) assert not errors input_datas = {'names': [input_data]} @@ -225,6 +253,10 @@ def test_sub_alt_name_schema(): assert data == json.dumps(input_datas) assert not errors + input_data = {'nameType': 'CNAME', 'value': 'test.example.com'} + data, errors = SubAltNameSchema().load(input_data) + assert errors + def test_key_usage_schema(): from lemur.schemas import KeyUsageSchema diff --git a/lemur/tests/test_validators.py b/lemur/tests/test_validators.py index 5e31ac5d..37527b1c 100644 --- a/lemur/tests/test_validators.py +++ b/lemur/tests/test_validators.py @@ -14,6 +14,13 @@ def test_private_key(session): 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): from lemur.common.validators import dates diff --git a/setup.py b/setup.py index 52d4afcd..029d9efd 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ docs_require = [ dev_requires = [ 'flake8>=2.0,<3.0', + 'pre-commit', 'invoke', 'twine' ]