diff --git a/.travis.yml b/.travis.yml index f9db8d3b..5f59badb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,3 +51,4 @@ notifications: - lemur@netflix.com on_success: never on_failure: always + on_cancel: never # Dependbot cancels Travis before rebase and triggers too many emails diff --git a/docs/administration.rst b/docs/administration.rst index bd0b5f96..59611c0f 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -151,6 +151,15 @@ Specifying the `SQLALCHEMY_MAX_OVERFLOW` to 0 will enforce limit to not create c to start. Multiple keys can be provided to facilitate key rotation. The first key in the list is used for encryption and all keys are tried for decryption until one works. Each key must be 32 URL safe base-64 encoded bytes. + Only fields of type ``Vault`` will be encrypted. At present, only the following fields are encrypted: + + * ``certificates.private_key`` + * ``pending_certificates.private_key`` + * ``dns_providers.credentials`` + * ``roles.password`` + + For implementation details, see ``Vault`` in ``utils.py``. + Running lemur create_config will securely generate a key for your configuration file. If you would like to generate your own, we recommend the following method: diff --git a/docs/quickstart/index.rst b/docs/quickstart/index.rst index e2f3476f..0fa55a09 100644 --- a/docs/quickstart/index.rst +++ b/docs/quickstart/index.rst @@ -130,7 +130,7 @@ Once created, you will need to update the configuration file with information ab vi ~/.lemur/lemur.conf.py .. note:: If you are unfamiliar with the SQLALCHEMY_DATABASE_URI string it can be broken up like so: - ``postgresql://userame:password@:/`` + ``postgresql://username:password@:/`` Before Lemur will run you need to fill in a few required variables in the configuration file: diff --git a/gulp/build.js b/gulp/build.js index 5aca8094..d86ce425 100644 --- a/gulp/build.js +++ b/gulp/build.js @@ -21,7 +21,6 @@ var gulp = require('gulp'), useref = require('gulp-useref'), filter = require('gulp-filter'), rev = require('gulp-rev'), - revReplace = require('gulp-rev-replace'), imagemin = require('gulp-imagemin'), minifyHtml = require('gulp-minify-html'), bowerFiles = require('main-bower-files'), @@ -29,16 +28,19 @@ var gulp = require('gulp'), replace = require('gulp-replace'), argv = require('yargs').argv; -gulp.task('default', ['clean'], function () { - gulp.start('fonts', 'styles'); + +gulp.task('clean', function (done) { + del(['.tmp', 'lemur/static/dist'], done); + done(); }); -gulp.task('clean', function (cb) { - del(['.tmp', 'lemur/static/dist'], cb); -}); +gulp.task('default', gulp.series(['clean'], function () { + gulp.start('fonts', 'styles'); +})); gulp.task('test', function (done) { - new karma.Server({ + // returning the promise + return new karma.Server({ configFile: __dirname + '/karma.conf.js', singleRun: true }, function() { @@ -47,25 +49,25 @@ gulp.task('test', function (done) { }); gulp.task('dev:fonts', function () { - var fileList = [ + let fileList = [ 'bower_components/bootstrap/dist/fonts/*', 'bower_components/fontawesome/fonts/*' ]; return gulp.src(fileList) - .pipe(gulp.dest('.tmp/fonts')); + .pipe(gulp.dest('.tmp/fonts')); // returns a stream making it async }); gulp.task('dev:styles', function () { - var baseContent = '@import "bower_components/bootstrap/less/bootstrap.less";@import "bower_components/bootswatch/$theme$/variables.less";@import "bower_components/bootswatch/$theme$/bootswatch.less";@import "bower_components/bootstrap/less/utilities.less";'; - var isBootswatchFile = function (file) { + let baseContent = '@import "bower_components/bootstrap/less/bootstrap.less";@import "bower_components/bootswatch/$theme$/variables.less";@import "bower_components/bootswatch/$theme$/bootswatch.less";@import "bower_components/bootstrap/less/utilities.less";'; + let isBootswatchFile = function (file) { - var suffix = 'bootswatch.less'; + let suffix = 'bootswatch.less'; return file.path.indexOf(suffix, file.path.length - suffix.length) !== -1; }; - var isBootstrapFile = function (file) { - var suffix = 'bootstrap-', + let isBootstrapFile = function (file) { + let suffix = 'bootstrap-', fileName = path.basename(file.path); return fileName.indexOf(suffix) === 0; @@ -74,7 +76,6 @@ gulp.task('dev:styles', function () { var fileList = [ 'bower_components/bootswatch/sandstone/bootswatch.less', 'bower_components/fontawesome/css/font-awesome.css', - 'bower_components/angular-spinkit/src/angular-spinkit.css', 'bower_components/angular-chart.js/dist/angular-chart.css', 'bower_components/angular-loading-bar/src/loading-bar.css', 'bower_components/angular-ui-switch/angular-ui-switch.css', @@ -87,7 +88,7 @@ gulp.task('dev:styles', function () { return gulp.src(fileList) .pipe(gulpif(isBootswatchFile, foreach(function (stream, file) { - var themeName = path.basename(path.dirname(file.path)), + let themeName = path.basename(path.dirname(file.path)), content = replaceAll(baseContent, '$theme$', themeName), file2 = string_src('bootstrap-' + themeName + '.less', content); @@ -95,12 +96,12 @@ gulp.task('dev:styles', function () { }))) .pipe(less()) .pipe(gulpif(isBootstrapFile, foreach(function (stream, file) { - var fileName = path.basename(file.path), + let fileName = path.basename(file.path), themeName = fileName.substring(fileName.indexOf('-') + 1, fileName.indexOf('.')); // http://stackoverflow.com/questions/21719833/gulp-how-to-add-src-files-in-the-middle-of-a-pipe // https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md - return merge(stream, gulp.src(['.tmp/styles/font-awesome.css', '.tmp/styles/lemur.css'])) + return merge(stream, gulp.src(['.tmp/styles/font-awesome.css', '.tmp/styles/lemur.css'], { allowEmpty: true })) .pipe(concat('style-' + themeName + '.css')); }))) .pipe(plumber()) @@ -121,7 +122,7 @@ function replaceAll(string, find, replace) { } function string_src(filename, string) { - var src = require('stream').Readable({ objectMode: true }); + let src = require('stream').Readable({ objectMode: true }); src._read = function () { this.push(new gutil.File({ cwd: '', base: '', path: filename, contents: new Buffer(string) })); this.push(null); @@ -162,7 +163,7 @@ function injectHtml(isDev) { })) .pipe( gulpif(!isDev, - inject(gulp.src('lemur/static/dist/ngviews/ngviews.min.js'), { + inject(gulp.src('lemur/static/dist/ngviews/ngviews.min.js', { allowEmpty: true }), { starttag: '', addRootSlash: false }) @@ -170,13 +171,9 @@ function injectHtml(isDev) { ).pipe(gulp.dest('.tmp/')); } -gulp.task('dev:inject', ['dev:styles', 'dev:scripts'], function () { +gulp.task('dev:inject', gulp.series(['dev:styles', 'dev:scripts'], function () { return injectHtml(true); -}); - -gulp.task('build:inject', ['dev:styles', 'dev:scripts', 'build:ngviews'], function () { - return injectHtml(false); -}); +})); gulp.task('build:ngviews', function () { return gulp.src(['lemur/static/app/angular/**/*.html']) @@ -189,9 +186,13 @@ gulp.task('build:ngviews', function () { .pipe(size()); }); -gulp.task('build:html', ['dev:styles', 'dev:scripts', 'build:ngviews', 'build:inject'], function () { - var jsFilter = filter(['**/*.js'], {'restore': true}); - var cssFilter = filter(['**/*.css'], {'restore': true}); +gulp.task('build:inject', gulp.series(['dev:styles', 'dev:scripts', 'build:ngviews'], function () { + return injectHtml(false); +})); + +gulp.task('build:html', gulp.series(['build:inject'], function () { + let jsFilter = filter(['**/*.js'], {'restore': true}); + let cssFilter = filter(['**/*.css'], {'restore': true}); return gulp.src('.tmp/index.html') .pipe(jsFilter) @@ -203,12 +204,12 @@ gulp.task('build:html', ['dev:styles', 'dev:scripts', 'build:ngviews', 'build:in .pipe(useref()) .pipe(gulp.dest('lemur/static/dist')) .pipe(size()); -}); +})); -gulp.task('build:fonts', ['dev:fonts'], function () { +gulp.task('build:fonts', gulp.series(['dev:fonts'], function () { return gulp.src('.tmp/fonts/**/*') .pipe(gulp.dest('lemur/static/dist/fonts')); -}); +})); gulp.task('build:images', function () { return gulp.src('lemur/static/app/images/**/*') @@ -230,35 +231,28 @@ gulp.task('package:strip', function () { .pipe(size()); }); -gulp.task('addUrlContextPath',['addUrlContextPath:revreplace'], function(){ - var urlContextPathExists = argv.urlContextPath ? true : false; - ['lemur/static/dist/scripts/main*.js', - 'lemur/static/dist/angular/**/*.html'] - .forEach(function(file){ - return gulp.src(file) - .pipe(gulpif(urlContextPathExists, replace('api/', argv.urlContextPath + '/api/'))) - .pipe(gulpif(urlContextPathExists, replace('/angular/', '/' + argv.urlContextPath + '/angular/'))) - .pipe(gulp.dest(function(file){ - return file.base; - })) - }) -}); - gulp.task('addUrlContextPath:revision', function(){ return gulp.src(['lemur/static/dist/**/*.css','lemur/static/dist/**/*.js']) .pipe(rev()) .pipe(gulp.dest('lemur/static/dist')) .pipe(rev.manifest()) .pipe(gulp.dest('lemur/static/dist')) -}) +}); -gulp.task('addUrlContextPath:revreplace', ['addUrlContextPath:revision'], function(){ - var manifest = gulp.src("lemur/static/dist/rev-manifest.json"); - var urlContextPathExists = argv.urlContextPath ? true : false; +gulp.task('addUrlContextPath:revreplace', gulp.series(['addUrlContextPath:revision'], function(){ return gulp.src( "lemur/static/dist/index.html") .pipe(gulp.dest('lemur/static/dist')); -}) +})); +gulp.task('addUrlContextPath', gulp.series(['addUrlContextPath:revreplace'], function(){ + let urlContextPathExists = argv.urlContextPath ? true : false; + return gulp.src(['lemur/static/dist/scripts/main*.js', 'lemur/static/dist/angular/**/*.html']) + .pipe(gulpif(urlContextPathExists, replace('api/', argv.urlContextPath + '/api/'))) + .pipe(gulpif(urlContextPathExists, replace('/angular/', '/' + argv.urlContextPath + '/angular/'))) + .pipe(gulp.dest(function(file){ + return file.base; + })) +})); -gulp.task('build', ['build:ngviews', 'build:inject', 'build:images', 'build:fonts', 'build:html', 'build:extras']); -gulp.task('package', ['addUrlContextPath', 'package:strip']); +gulp.task('build', gulp.series(['build:images', 'build:fonts', 'build:html', 'build:extras'])); +gulp.task('package', gulp.series(['addUrlContextPath', 'package:strip'])); diff --git a/gulp/server.js b/gulp/server.js index 6c61273e..0c4af91a 100644 --- a/gulp/server.js +++ b/gulp/server.js @@ -1,6 +1,7 @@ 'use strict'; var gulp = require('gulp'); +const watch = require('./watch') var browserSync = require('browser-sync'); var httpProxy = require('http-proxy'); @@ -38,7 +39,7 @@ function browserSyncInit(baseDir, files, browser) { } -gulp.task('serve', ['watch'], function () { +gulp.task('serve', gulp.series(['watch'], function (done) { browserSyncInit([ '.tmp', 'lemur/static/app' @@ -51,9 +52,12 @@ gulp.task('serve', ['watch'], function () { 'lemur/static/app/angular/**/*', 'lemur/static/app/index.html' ]); -}); + + done(); +})); -gulp.task('serve:dist', ['build'], function () { +gulp.task('serve:dist', gulp.series(['build'], function (done) { browserSyncInit('lemur/static/dist'); -}); + done(); +})); diff --git a/gulp/watch.js b/gulp/watch.js index 460a935b..a406eea3 100644 --- a/gulp/watch.js +++ b/gulp/watch.js @@ -3,10 +3,13 @@ var gulp = require('gulp'); -gulp.task('watch', ['dev:styles', 'dev:scripts', 'dev:inject', 'dev:fonts'] ,function () { - gulp.watch('app/styles/**/*.less', ['dev:styles']); - gulp.watch('app/styles/**/*.css', ['dev:styles']); - gulp.watch('app/**/*.js', ['dev:scripts']); - gulp.watch('app/images/**/*', ['build:images']); - gulp.watch('bower.json', ['dev:inject']); -}); +const watch = gulp.task('watch', gulp.series(['dev:inject', 'dev:fonts'] ,function (done) { + gulp.watch('app/styles/**/*.less', gulp.series('dev:styles')); + gulp.watch('app/styles/**/*.css', gulp.series('dev:styles')); + gulp.watch('app/**/*.js', gulp.series('dev:scripts')); + gulp.watch('app/images/**/*', gulp.series('build:images')); + gulp.watch('bower.json', gulp.series('dev:inject')); + done(); +})); + +module.exports = {watch:watch} diff --git a/lemur/certificates/service.py b/lemur/certificates/service.py index b22090b6..e6414933 100644 --- a/lemur/certificates/service.py +++ b/lemur/certificates/service.py @@ -563,10 +563,15 @@ def query_common_name(common_name, args): :return: """ owner = args.pop("owner") + page = args.pop("page") + count = args.pop("count") + + paginate = page and count + query = database.session_query(Certificate) if paginate else Certificate.query + # only not expired certificates current_time = arrow.utcnow() - - query = Certificate.query.filter(Certificate.not_after >= current_time.format("YYYY-MM-DD"))\ + query = query.filter(Certificate.not_after >= current_time.format("YYYY-MM-DD"))\ .filter(not_(Certificate.revoked))\ .filter(not_(Certificate.replaced.any())) # ignore rotated certificates to avoid duplicates @@ -577,6 +582,9 @@ def query_common_name(common_name, args): # if common_name is a wildcard ('%'), no need to include it in the query query = query.filter(Certificate.cn.ilike(common_name)) + if paginate: + return database.paginate(query, page, count) + return query.all() @@ -795,6 +803,15 @@ def reissue_certificate(certificate, replace=None, user=None): else: primitives["description"] = f"{reissue_message_prefix}{certificate.id}" + # Rotate the certificate to ECCPRIME256V1 if cert owner is present in the configured list + # This is a temporary change intending to rotate certificates to ECC, if opted in by certificate owners + # Unless identified a use case, this will be removed in mid-Q2 2021 + ecc_reissue_owner_list = current_app.config.get("ROTATE_TO_ECC_OWNER_LIST", []) + ecc_reissue_exclude_cn_list = current_app.config.get("ECC_NON_COMPATIBLE_COMMON_NAMES", []) + + if (certificate.owner in ecc_reissue_owner_list) and (certificate.cn not in ecc_reissue_exclude_cn_list): + primitives["key_type"] = "ECCPRIME256V1" + new_cert = create(**primitives) return new_cert diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index 56d0a9c8..47c076fc 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -51,17 +51,20 @@ class CertificatesListValid(AuthenticatedResource): """ .. http:get:: /certificates/valid/ - The current list of not-expired certificates for a given common name, and owner + The current list of not-expired certificates for a given common name, and owner. The API offers + optional pagination. One can send page number(>=1) and desired count per page. The returned data + contains total number of certificates which can help in determining the last page. Pagination + will not be offered if page or count info is not sent or if it is zero. **Example request**: .. sourcecode:: http - GET /certificates/valid?filter=cn;*.test.example.net&owner=joe@example.com + GET /certificates/valid?filter=cn;*.test.example.net&owner=joe@example.com&page=1&count=20 HTTP/1.1 Host: example.com Accept: application/json, text/javascript - **Example response**: + **Example response (with single cert to be concise)**: .. sourcecode:: http @@ -128,10 +131,15 @@ class CertificatesListValid(AuthenticatedResource): :statuscode 403: unauthenticated """ - parser = paginated_parser.copy() - args = parser.parse_args() + # using non-paginated parser to ensure backward compatibility + self.reqparse.add_argument("filter", type=str, location="args") + self.reqparse.add_argument("owner", type=str, location="args") + self.reqparse.add_argument("count", type=int, location="args") + self.reqparse.add_argument("page", type=int, location="args") + + args = self.reqparse.parse_args() args["user"] = g.user - common_name = args["filter"].split(";")[1] + common_name = args.pop("filter").split(";")[1] return service.query_common_name(common_name, args) diff --git a/lemur/database.py b/lemur/database.py index a9610325..0453c381 100644 --- a/lemur/database.py +++ b/lemur/database.py @@ -225,7 +225,9 @@ def paginate(query, page, count): :param page: :param count: """ - return query.paginate(page, count) + total = get_count(query) + items = query.paginate(page, count).items + return dict(items=items, total=total, current=len(items)) def update_list(model, model_attr, item_model, items): diff --git a/lemur/plugins/lemur_acme/tests/test_acme_http.py b/lemur/plugins/lemur_acme/tests/test_acme_http.py index 5a546165..0df9e6b2 100644 --- a/lemur/plugins/lemur_acme/tests/test_acme_http.py +++ b/lemur/plugins/lemur_acme/tests/test_acme_http.py @@ -1,8 +1,8 @@ import unittest from unittest.mock import patch, Mock -from flask import Flask from acme import challenges +from flask import Flask from lemur.plugins.lemur_acme import plugin @@ -51,7 +51,8 @@ class TestAcmeHttp(unittest.TestCase): mock_order_resource = Mock() mock_order_resource.authorizations = [Mock()] mock_order_resource.authorizations[0].body.challenges = [Mock()] - mock_order_resource.authorizations[0].body.challenges[0].response_and_validation.return_value = (Mock(), "Anything-goes") + mock_order_resource.authorizations[0].body.challenges[0].response_and_validation.return_value = ( + Mock(), "Anything-goes") mock_order_resource.authorizations[0].body.challenges[0].chall = challenges.HTTP01( token=b'\x0f\x1c\xbe#od\xd1\x9c\xa6j\\\xa4\r\xed\xe5\xbf0pz\xeaxnl)\xea[i\xbc\x95\x08\x96\x1f') @@ -60,7 +61,91 @@ class TestAcmeHttp(unittest.TestCase): mock_client.answer_challenge.return_value = True mock_finalized_order = Mock() - mock_finalized_order.fullchain_pem = "-----BEGIN CERTIFICATE-----\nMIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw\nGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2\nMDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0\n8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym\noLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0\nZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN\nxDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56\ndhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9\nAgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw\nHQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0\nBggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu\nY3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq\nhkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF\nUGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9\nAFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp\nDQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7\nIkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf\nzWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI\nPTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w\nSVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em\n2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0\nWzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt\nn5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw\nGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2\nMDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0\n8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym\noLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0\nZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN\nxDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56\ndhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9\nAgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw\nHQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0\nBggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu\nY3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq\nhkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF\nUGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9\nAFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp\nDQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7\nIkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf\nzWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI\nPTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w\nSVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em\n2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0\nWzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt\nn5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=\n-----END CERTIFICATE-----\n" + mock_finalized_order.fullchain_pem = """ +-----BEGIN CERTIFICATE----- +MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw +GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2 +MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0 +8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym +oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0 +ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN +xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56 +dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9 +AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0 +BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu +b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu +Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq +hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF +UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9 +AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp +DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7 +IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf +zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI +PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w +SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em +2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0 +WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt +n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw +GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2 +MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0 +8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym +oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0 +ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN +xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56 +dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9 +AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0 +BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu +b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu +Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq +hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF +UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9 +AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp +DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7 +IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf +zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI +PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w +SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em +2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0 +WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt +n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFATCCAumgAwIBAgIRAKc9ZKBASymy5TLOEp57N98wDQYJKoZIhvcNAQELBQAw +GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDMyMzIyNTM0NloXDTM2 +MDMyMzIyNTM0NlowGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA+pYHvQw5iU3v2b3iNuYNKYgsWD6KU7aJ +diddtZQxSWYzUI3U0I1UsRPTxnhTifs/M9NW4ZlV13ZfB7APwC8oqKOIiwo7IwlP +xg0VKgyz+kT8RJfYr66PPIYP0fpTeu42LpMJ+CKo9sbpgVNDZN2z/qiXrRNX/VtG +TkPV7a44fZ5bHHVruAxvDnylpQxJobtCBWlJSsbIRGFHMc2z88eUz9NmIOWUKGGj +EmP76x8OfRHpIpuxRSCjn0+i9+hR2siIOpcMOGd+40uVJxbRRP5ZXnUFa2fF5FWd +O0u0RPI8HON0ovhrwPJY+4eWKkQzyC611oLPYGQ4EbifRsTsCxUZqyUuStGyp8oa +aoSKfF6X0+KzGgwwnrjRTUpIl19A92KR0Noo6h622OX+4sZiO/JQdkuX5w/HupK0 +A0M0WSMCvU6GOhjGotmh2VTEJwHHY4+TUk0iQYRtv1crONklyZoAQPD76hCrC8Cr +IbgsZLfTMC8TWUoMbyUDgvgYkHKMoPm0VGVVuwpRKJxv7+2wXO+pivrrUl2Q9fPe +Kk055nJLMV9yPUdig8othUKrRfSxli946AEV1eEOhxddfEwBE3Lt2xn0hhiIedbb +Ftf/5kEWFZkXyUmMJK8Ra76Kus2ABueUVEcZ48hrRr1Hf1N9n59VbTUaXgeiZA50 +qXf2bymE6F8CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFMEmdKSKRKDm+iAo2FwjmkWIGHngMA0GCSqGSIb3DQEBCwUA +A4ICAQBCPw74M9X/Xx04K1VAES3ypgQYH5bf9FXVDrwhRFSVckria/7dMzoF5wln +uq9NGsjkkkDg17AohcQdr8alH4LvPdxpKr3BjpvEcmbqF8xH+MbbeUEnmbSfLI8H +sefuhXF9AF/9iYvpVNC8FmJ0OhiVv13VgMQw0CRKkbtjZBf8xaEhq/YqxWVsgOjm +dm5CAQ2X0aX7502x8wYRgMnZhA5goC1zVWBVAi8yhhmlhhoDUfg17cXkmaJC5pDd +oenZ9NVhW8eDb03MFCrWNvIh89DDeCGWuWfDltDq0n3owyL0IeSn7RfpSclpxVmV +/53jkYjwIgxIG7Gsv0LKMbsf6QdBcTjhvfZyMIpBRkTe3zuHd2feKzY9lEkbRvRQ +zbh4Ps5YBnG6CKJPTbe2hfi3nhnw/MyEmF3zb0hzvLWNrR9XW3ibb2oL3424XOwc +VjrTSCLzO9Rv6s5wi03qoWvKAQQAElqTYRHhynJ3w6wuvKYF5zcZF3MDnrVGLbh1 +Q9ePRFBCiXOQ6wPLoUhrrbZ8LpFUFYDXHMtYM7P9sc9IAWoONXREJaO08zgFtMp4 +8iyIYUyQAbsvx8oD2M8kRvrIRSrRJSl6L957b4AFiLIQ/GgV2curs0jje7Edx34c +idWw1VrejtwclobqNMVtG3EiPUIpJGpbMcJgbiLSmKkrvQtGng== +-----END CERTIFICATE----- +""" mock_client.poll_and_finalize.return_value = mock_finalized_order mock_acme.return_value = (mock_client, "") @@ -84,8 +169,63 @@ class TestAcmeHttp(unittest.TestCase): pem_certificate, pem_certificate_chain, _ = provider.create_certificate(csr, issuer_options) self.assertEqual(pem_certificate, "-----BEGIN CERTIFICATE-----\nMIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw\nGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2\nMDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0\n8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym\noLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0\nZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN\nxDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56\ndhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9\nAgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw\nHQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0\nBggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu\nY3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq\nhkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF\nUGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9\nAFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp\nDQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7\nIkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf\nzWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI\nPTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w\nSVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em\n2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0\nWzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt\nn5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=\n-----END CERTIFICATE-----\n") - self.assertEqual(pem_certificate_chain, - "-----BEGIN CERTIFICATE-----\nMIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw\nGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2\nMDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw\nggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0\n8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym\noLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0\nZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN\nxDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56\ndhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9\nAgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw\nHQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0\nBggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu\nb3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu\nY3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq\nhkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF\nUGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9\nAFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp\nDQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7\nIkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf\nzWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI\nPTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w\nSVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em\n2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0\nWzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt\nn5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=\n-----END CERTIFICATE-----\n") + self.assertEqual(pem_certificate_chain, """-----BEGIN CERTIFICATE----- +MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw +GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2 +MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0 +8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym +oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0 +ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN +xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56 +dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9 +AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0 +BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu +b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu +Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq +hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF +UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9 +AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp +DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7 +IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf +zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI +PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w +SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em +2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0 +WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt +n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFATCCAumgAwIBAgIRAKc9ZKBASymy5TLOEp57N98wDQYJKoZIhvcNAQELBQAw +GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDMyMzIyNTM0NloXDTM2 +MDMyMzIyNTM0NlowGjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA+pYHvQw5iU3v2b3iNuYNKYgsWD6KU7aJ +diddtZQxSWYzUI3U0I1UsRPTxnhTifs/M9NW4ZlV13ZfB7APwC8oqKOIiwo7IwlP +xg0VKgyz+kT8RJfYr66PPIYP0fpTeu42LpMJ+CKo9sbpgVNDZN2z/qiXrRNX/VtG +TkPV7a44fZ5bHHVruAxvDnylpQxJobtCBWlJSsbIRGFHMc2z88eUz9NmIOWUKGGj +EmP76x8OfRHpIpuxRSCjn0+i9+hR2siIOpcMOGd+40uVJxbRRP5ZXnUFa2fF5FWd +O0u0RPI8HON0ovhrwPJY+4eWKkQzyC611oLPYGQ4EbifRsTsCxUZqyUuStGyp8oa +aoSKfF6X0+KzGgwwnrjRTUpIl19A92KR0Noo6h622OX+4sZiO/JQdkuX5w/HupK0 +A0M0WSMCvU6GOhjGotmh2VTEJwHHY4+TUk0iQYRtv1crONklyZoAQPD76hCrC8Cr +IbgsZLfTMC8TWUoMbyUDgvgYkHKMoPm0VGVVuwpRKJxv7+2wXO+pivrrUl2Q9fPe +Kk055nJLMV9yPUdig8othUKrRfSxli946AEV1eEOhxddfEwBE3Lt2xn0hhiIedbb +Ftf/5kEWFZkXyUmMJK8Ra76Kus2ABueUVEcZ48hrRr1Hf1N9n59VbTUaXgeiZA50 +qXf2bymE6F8CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFMEmdKSKRKDm+iAo2FwjmkWIGHngMA0GCSqGSIb3DQEBCwUA +A4ICAQBCPw74M9X/Xx04K1VAES3ypgQYH5bf9FXVDrwhRFSVckria/7dMzoF5wln +uq9NGsjkkkDg17AohcQdr8alH4LvPdxpKr3BjpvEcmbqF8xH+MbbeUEnmbSfLI8H +sefuhXF9AF/9iYvpVNC8FmJ0OhiVv13VgMQw0CRKkbtjZBf8xaEhq/YqxWVsgOjm +dm5CAQ2X0aX7502x8wYRgMnZhA5goC1zVWBVAi8yhhmlhhoDUfg17cXkmaJC5pDd +oenZ9NVhW8eDb03MFCrWNvIh89DDeCGWuWfDltDq0n3owyL0IeSn7RfpSclpxVmV +/53jkYjwIgxIG7Gsv0LKMbsf6QdBcTjhvfZyMIpBRkTe3zuHd2feKzY9lEkbRvRQ +zbh4Ps5YBnG6CKJPTbe2hfi3nhnw/MyEmF3zb0hzvLWNrR9XW3ibb2oL3424XOwc +VjrTSCLzO9Rv6s5wi03qoWvKAQQAElqTYRHhynJ3w6wuvKYF5zcZF3MDnrVGLbh1 +Q9ePRFBCiXOQ6wPLoUhrrbZ8LpFUFYDXHMtYM7P9sc9IAWoONXREJaO08zgFtMp4 +8iyIYUyQAbsvx8oD2M8kRvrIRSrRJSl6L957b4AFiLIQ/GgV2curs0jje7Edx34c +idWw1VrejtwclobqNMVtG3EiPUIpJGpbMcJgbiLSmKkrvQtGng== +-----END CERTIFICATE----- +""") @patch("lemur.plugins.lemur_acme.plugin.AcmeHandler.setup_acme_client") @patch("lemur.plugins.base.manager.PluginManager.get") diff --git a/lemur/static/app/angular/notifications/view/view.tpl.html b/lemur/static/app/angular/notifications/view/view.tpl.html index 05a5fe51..03b7283c 100644 --- a/lemur/static/app/angular/notifications/view/view.tpl.html +++ b/lemur/static/app/angular/notifications/view/view.tpl.html @@ -1,7 +1,7 @@

Notifications - you have to speak up son!

+ you have to speak up!
diff --git a/lemur/tests/conftest.py b/lemur/tests/conftest.py index 2efd65d9..f388acc6 100644 --- a/lemur/tests/conftest.py +++ b/lemur/tests/conftest.py @@ -56,7 +56,7 @@ def pytest_runtest_makereport(item, call): parent._previousfailed = item -@pytest.yield_fixture(scope="session") +@pytest.fixture(scope="session") def app(request): """ Creates a new Flask application for a test duration. @@ -73,7 +73,7 @@ def app(request): ctx.pop() -@pytest.yield_fixture(scope="session") +@pytest.fixture(scope="session") def db(app, request): _db.drop_all() _db.engine.execute(text("CREATE EXTENSION IF NOT EXISTS pg_trgm")) @@ -92,7 +92,7 @@ def db(app, request): _db.drop_all() -@pytest.yield_fixture(scope="function") +@pytest.fixture(scope="function") def session(db, request): """ Creates a new database session with (with working transaction) @@ -103,7 +103,7 @@ def session(db, request): db.session.rollback() -@pytest.yield_fixture(scope="function") +@pytest.fixture(scope="function") def client(app, session, client): yield client @@ -276,14 +276,14 @@ def source_plugin(): return TestSourcePlugin -@pytest.yield_fixture(scope="function") +@pytest.fixture(scope="function") def logged_in_user(session, app): with app.test_request_context(): identity_changed.send(current_app._get_current_object(), identity=Identity(1)) yield -@pytest.yield_fixture(scope="function") +@pytest.fixture(scope="function") def logged_in_admin(session, app): with app.test_request_context(): identity_changed.send(current_app._get_current_object(), identity=Identity(2)) diff --git a/package.json b/package.json index 828f8899..f2d87ab7 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "test": "gulp test" }, "devDependencies": { - "gulp": "^3.9.1", + "gulp": "^4.0.2", "jshint": "^2.11.0", "karma-chrome-launcher": "^2.0.0" } diff --git a/requirements-dev.txt b/requirements-dev.txt index 50c5d08b..af69b1cf 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -18,7 +18,7 @@ filelock==3.0.12 # via virtualenv flake8==3.8.4 # via -r requirements-dev.in identify==1.4.14 # via pre-commit idna==2.9 # via requests -invoke==1.4.1 # via -r requirements-dev.in +invoke==1.5.0 # via -r requirements-dev.in jeepney==0.4.3 # via keyring, secretstorage keyring==21.2.0 # via twine mccabe==0.6.1 # via flake8 @@ -32,13 +32,13 @@ pygments==2.6.1 # via readme-renderer pyyaml==5.3.1 # via -r requirements-dev.in, pre-commit readme-renderer==25.0 # via twine requests-toolbelt==0.9.1 # via twine -requests==2.25.0 # via requests-toolbelt, twine +requests==2.25.1 # via requests-toolbelt, twine rfc3986==1.4.0 # via twine secretstorage==3.1.2 # via keyring six==1.15.0 # via bleach, cryptography, readme-renderer, virtualenv toml==0.10.0 # via pre-commit tqdm==4.45.0 # via twine -twine==3.2.0 # via -r requirements-dev.in +twine==3.3.0 # via -r requirements-dev.in urllib3==1.25.8 # via requests virtualenv==20.0.17 # via pre-commit webencodings==0.5.1 # via bleach diff --git a/requirements-docs.txt b/requirements-docs.txt index 624b6981..f5c94cea 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -17,11 +17,11 @@ packaging==20.3 # via sphinx pygments==2.6.1 # via sphinx pyparsing==2.4.7 # via packaging pytz==2019.3 # via babel -requests==2.25.0 # via sphinx +requests==2.25.1 # via sphinx six==1.15.0 # via packaging, sphinxcontrib-httpdomain snowballstemmer==2.0.0 # via sphinx -sphinx-rtd-theme==0.5.0 # via -r requirements-docs.in -sphinx==3.3.1 # via -r requirements-docs.in, sphinx-rtd-theme, sphinxcontrib-httpdomain +sphinx-rtd-theme==0.5.1 # via -r requirements-docs.in +sphinx==3.4.3 # via -r requirements-docs.in, sphinx-rtd-theme, sphinxcontrib-httpdomain sphinxcontrib-applehelp==1.0.2 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==1.0.3 # via sphinx diff --git a/requirements-tests.txt b/requirements-tests.txt index a3f4b4b7..cb1365fb 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -10,28 +10,28 @@ aws-sam-translator==1.22.0 # via cfn-lint aws-xray-sdk==2.5.0 # via moto bandit==1.7.0 # via -r requirements-tests.in black==20.8b1 # via -r requirements-tests.in -boto3==1.16.35 # via aws-sam-translator, moto +boto3==1.16.51 # via aws-sam-translator, moto boto==2.49.0 # via moto -botocore==1.19.35 # via aws-xray-sdk, boto3, moto, s3transfer +botocore==1.19.51 # via aws-xray-sdk, boto3, moto, s3transfer certifi==2020.12.5 # via requests cffi==1.14.0 # via cryptography cfn-lint==0.29.5 # via moto chardet==3.0.4 # via requests click==7.1.2 # via black, flask -coverage==5.3 # via -r requirements-tests.in +coverage==5.3.1 # via -r requirements-tests.in cryptography==3.3.1 # via moto, python-jose, sshpubkeys decorator==4.4.2 # via networkx docker==4.2.0 # via moto ecdsa==0.14.1 # via moto, python-jose, sshpubkeys -factory-boy==3.1.0 # via -r requirements-tests.in -faker==5.0.1 # via -r requirements-tests.in, factory-boy +factory-boy==3.2.0 # via -r requirements-tests.in +faker==5.4.1 # via -r requirements-tests.in, factory-boy fakeredis==1.4.5 # via -r requirements-tests.in flask==1.1.2 # via pytest-flask freezegun==1.0.0 # via -r requirements-tests.in future==0.18.2 # via aws-xray-sdk gitdb==4.0.4 # via gitpython gitpython==3.1.1 # via bandit -idna==2.8 # via moto, requests +idna==2.9 # via moto, requests importlib-metadata==1.6.0 # via jsonpickle iniconfig==1.0.1 # via pytest itsdangerous==1.1.0 # via flask @@ -60,8 +60,8 @@ pyflakes==2.2.0 # via -r requirements-tests.in pyparsing==2.4.7 # via packaging pyrsistent==0.16.0 # via jsonschema pytest-flask==1.1.0 # via -r requirements-tests.in -pytest-mock==3.3.1 # via -r requirements-tests.in -pytest==6.2.0 # via -r requirements-tests.in, pytest-flask, pytest-mock +pytest-mock==3.5.1 # via -r requirements-tests.in +pytest==6.2.1 # via -r requirements-tests.in, pytest-flask, pytest-mock python-dateutil==2.8.1 # via botocore, faker, freezegun, moto python-jose[cryptography]==3.1.0 # via moto pytz==2019.3 # via moto @@ -69,7 +69,7 @@ pyyaml==5.3.1 # via -r requirements-tests.in, bandit, cfn-lint, moto redis==3.5.3 # via fakeredis regex==2020.4.4 # via black requests-mock==1.8.0 # via -r requirements-tests.in -requests==2.25.0 # via docker, moto, requests-mock, responses +requests==2.25.1 # via docker, moto, requests-mock, responses responses==0.10.12 # via moto rsa==4.0 # via python-jose s3transfer==0.3.3 # via boto3 diff --git a/requirements.txt b/requirements.txt index 981c38b7..550a0279 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --no-index --output-file=requirements.txt requirements.in # -acme==1.10.1 # via -r requirements.in +acme==1.11.0 # via -r requirements.in alembic-autogenerate-enums==0.0.2 # via -r requirements.in alembic==1.4.2 # via flask-migrate amqp==2.5.2 # via kombu @@ -15,21 +15,21 @@ bcrypt==3.1.7 # via flask-bcrypt, paramiko beautifulsoup4==4.9.1 # via cloudflare billiard==3.6.3.0 # via celery blinker==1.4 # via flask-mail, flask-principal, raven -boto3==1.16.35 # via -r requirements.in -botocore==1.19.35 # via -r requirements.in, boto3, s3transfer +boto3==1.16.51 # via -r requirements.in +botocore==1.19.51 # via -r requirements.in, boto3, s3transfer celery[redis]==4.4.2 # via -r requirements.in certifi==2020.12.5 # via -r requirements.in, requests certsrv==2.1.1 # via -r requirements.in cffi==1.14.0 # via bcrypt, cryptography, pynacl chardet==3.0.4 # via requests -click==7.1.2 # flask -cloudflare==2.8.14 # via -r requirements.in +click==7.1.2 # via flask +cloudflare==2.8.15 # via -r requirements.in cryptography==3.3.1 # via -r requirements.in, acme, josepy, paramiko, pyopenssl, requests dnspython3==1.15.0 # via -r requirements.in dnspython==1.15.0 # via dnspython3 dyn==1.8.1 # via -r requirements.in flask-bcrypt==0.7.1 # via -r requirements.in -flask-cors==3.0.9 # via -r requirements.in +flask-cors==3.0.10 # via -r requirements.in flask-mail==0.9.1 # via -r requirements.in flask-migrate==2.5.3 # via -r requirements.in flask-principal==0.4.0 # via -r requirements.in @@ -40,7 +40,7 @@ flask-sqlalchemy==2.4.4 # via -r requirements.in, flask-migrate flask==1.1.2 # via -r requirements.in, flask-bcrypt, flask-cors, flask-mail, flask-migrate, flask-principal, flask-restful, flask-script, flask-sqlalchemy, raven future==0.18.2 # via -r requirements.in gunicorn==20.0.4 # via -r requirements.in -hvac==0.10.5 # via -r requirements.in +hvac==0.10.6 # via -r requirements.in idna==2.9 # via requests inflection==0.5.1 # via -r requirements.in itsdangerous==1.1.0 # via flask @@ -67,7 +67,7 @@ pycryptodomex==3.9.7 # via pyjks pyjks==20.0.0 # via -r requirements.in pyjwt==1.7.1 # via -r requirements.in pynacl==1.3.0 # via paramiko -pyopenssl==20.0.0 # via -r requirements.in, acme, josepy, ndg-httpsclient, requests +pyopenssl==20.0.1 # via -r requirements.in, acme, josepy, ndg-httpsclient, requests pyrfc3339==1.1 # via acme python-dateutil==2.8.1 # via alembic, arrow, botocore python-editor==1.0.4 # via alembic @@ -78,7 +78,7 @@ pyyaml==5.3.1 # via -r requirements.in, cloudflare raven[flask]==6.10.0 # via -r requirements.in redis==3.5.3 # via -r requirements.in, celery requests-toolbelt==0.9.1 # via acme -requests[security]==2.25.0 # via -r requirements.in, acme, certsrv, cloudflare, hvac, requests-toolbelt +requests[security]==2.25.1 # via -r requirements.in, acme, certsrv, cloudflare, hvac, requests-toolbelt retrying==1.3.3 # via -r requirements.in s3transfer==0.3.3 # via boto3 six==1.15.0 # via -r requirements.in, acme, bcrypt, cryptography, flask-cors, flask-restful, hvac, josepy, jsonlines, pynacl, pyopenssl, python-dateutil, retrying, sqlalchemy-utils