diff --git a/bower.json b/bower.json index f2c6973d..b4dba71d 100644 --- a/bower.json +++ b/bower.json @@ -32,7 +32,8 @@ "angularjs-toaster": "~0.4.14", "ngletteravatar": "~3.0.1", "angular-ui-router": "~0.2.15", - "angular-clipboard": "~1.1.1" + "angular-clipboard": "~1.1.1", + "angular-file-saver": "~1.0.1" }, "devDependencies": { "angular-mocks": "~1.3", diff --git a/lemur/certificates/views.py b/lemur/certificates/views.py index 4cba53ae..6a58e93b 100644 --- a/lemur/certificates/views.py +++ b/lemur/certificates/views.py @@ -5,6 +5,7 @@ :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson """ +import base64 from builtins import str from flask import Blueprint, make_response, jsonify @@ -817,10 +818,9 @@ class CertificateExport(AuthenticatedResource): permission = UpdateCertificatePermission(certificate_id, getattr(role, 'name', None)) if permission.can(): - passphrase, data = service.export(cert, args['export']['plugin']) - response = make_response(data) - response.headers['content-type'] = 'application/octet-stream' - return response + extension, passphrase, data = service.export(cert, args['export']['plugin']) + # we take a hit in message size when b64 encoding + return dict(extension=extension, passphrase=passphrase, data=base64.b64encode(data)) return dict(message='You are not authorized to export this certificate'), 403 diff --git a/lemur/plugins/lemur_java/plugin.py b/lemur/plugins/lemur_java/plugin.py index 72aabf61..6e839dc4 100644 --- a/lemur/plugins/lemur_java/plugin.py +++ b/lemur/plugins/lemur_java/plugin.py @@ -74,7 +74,7 @@ class JavaExportPlugin(ExportPlugin): 'type': 'str', 'required': False, 'helpMessage': 'If no passphrase is given one will be generated for you, we highly recommend this. Minimum length is 8.', - 'validation': '^.{8}$' + 'validation': '^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$' }, { 'name': 'alias', @@ -105,9 +105,13 @@ class JavaExportPlugin(ExportPlugin): else: alias = "blah" + if not key: + raise Exception("Unable to export, no private key found.") + with mktempfile() as cert_tmp: with open(cert_tmp, 'w') as f: f.write(body) + with mktempfile() as key_tmp: with open(key_tmp, 'w') as f: f.write(key) @@ -168,4 +172,4 @@ class JavaExportPlugin(ExportPlugin): with open(jks_tmp, 'rb') as f: raw = f.read() - return passphrase, raw + return "jks", passphrase, raw diff --git a/lemur/static/app/angular/app.js b/lemur/static/app/angular/app.js index a941814d..5f395a68 100644 --- a/lemur/static/app/angular/app.js +++ b/lemur/static/app/angular/app.js @@ -15,7 +15,8 @@ var lemur = angular 'mgo-angular-wizard', 'satellizer', 'ngLetterAvatar', - 'angular-clipboard' + 'angular-clipboard', + 'ngFileSaver' ]) .config(function ($stateProvider, $urlRouterProvider, $authProvider) { $urlRouterProvider.otherwise('/welcome'); diff --git a/lemur/static/app/angular/certificates/certificate/certificate.js b/lemur/static/app/angular/certificates/certificate/certificate.js index f9a63af2..8ffec997 100644 --- a/lemur/static/app/angular/certificates/certificate/certificate.js +++ b/lemur/static/app/angular/certificates/certificate/certificate.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('lemur') - .controller('CertificateExportController', function ($scope, $modalInstance, CertificateApi, CertificateService, PluginService, toaster, editId) { + .controller('CertificateExportController', function ($scope, $modalInstance, CertificateApi, CertificateService, PluginService, FileSaver, Blob, toaster, editId) { CertificateApi.get(editId).then(function (certificate) { $scope.certificate = certificate; }); @@ -16,13 +16,26 @@ angular.module('lemur') $scope.save = function (certificate) { CertificateService.export(certificate).then( - function () { - toaster.pop({ - type: 'success', - title: certificate.name, - body: 'Successfully exported!' - }); - $modalInstance.close(); + function (response) { + var byteCharacters = atob(response.data); + var byteArrays = []; + + for (var offset = 0; offset < byteCharacters.length; offset += 512) { + var slice = byteCharacters.slice(offset, offset + 512); + + var byteNumbers = new Array(slice.length); + for (var i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + } + + var blob = new Blob(byteArrays, {type: 'application/octet-stream'}); + saveAs(blob, certificate.name + "." + response.extension); + $scope.passphrase = response.passphrase; }, function (response) { toaster.pop({