Merge pull request #158 from kevgliss/fix

Fix
This commit is contained in:
kevgliss 2015-11-30 14:27:23 -08:00
commit 8066d540e0
3 changed files with 115 additions and 11 deletions

View File

@ -7,31 +7,22 @@
""" """
import base64 import base64
from builtins import str from builtins import str
from flask import Blueprint, make_response, jsonify from flask import Blueprint, make_response, jsonify
from flask.ext.restful import reqparse, Api, fields from flask.ext.restful import reqparse, Api, fields
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from lemur.certificates import service from lemur.certificates import service
from lemur.authorities.models import Authority from lemur.authorities.models import Authority
from lemur.auth.service import AuthenticatedResource from lemur.auth.service import AuthenticatedResource
from lemur.auth.permissions import ViewKeyPermission, AuthorityPermission, UpdateCertificatePermission from lemur.auth.permissions import ViewKeyPermission, AuthorityPermission, UpdateCertificatePermission
from lemur.roles import service as role_service from lemur.roles import service as role_service
from lemur.common.utils import marshal_items, paginated_parser from lemur.common.utils import marshal_items, paginated_parser
from lemur.notifications.views import notification_list from lemur.notifications.views import notification_list
mod = Blueprint('certificates', __name__) mod = Blueprint('certificates', __name__)
api = Api(mod) api = Api(mod)
FIELDS = { FIELDS = {
'name': fields.String, 'name': fields.String,
'id': fields.Integer, 'id': fields.Integer,
@ -104,6 +95,7 @@ def private_key_str(value, name):
class CertificatesList(AuthenticatedResource): class CertificatesList(AuthenticatedResource):
""" Defines the 'certificates' endpoint """ """ Defines the 'certificates' endpoint """
def __init__(self): def __init__(self):
self.reqparse = reqparse.RequestParser() self.reqparse = reqparse.RequestParser()
super(CertificatesList, self).__init__() super(CertificatesList, self).__init__()
@ -354,6 +346,7 @@ class CertificatesList(AuthenticatedResource):
class CertificatesUpload(AuthenticatedResource): class CertificatesUpload(AuthenticatedResource):
""" Defines the 'certificates' upload endpoint """ """ Defines the 'certificates' upload endpoint """
def __init__(self): def __init__(self):
self.reqparse = reqparse.RequestParser() self.reqparse = reqparse.RequestParser()
super(CertificatesUpload, self).__init__() super(CertificatesUpload, self).__init__()
@ -442,6 +435,7 @@ class CertificatesUpload(AuthenticatedResource):
class CertificatesStats(AuthenticatedResource): class CertificatesStats(AuthenticatedResource):
""" Defines the 'certificates' stats endpoint """ """ Defines the 'certificates' stats endpoint """
def __init__(self): def __init__(self):
self.reqparse = reqparse.RequestParser() self.reqparse = reqparse.RequestParser()
super(CertificatesStats, self).__init__() super(CertificatesStats, self).__init__()
@ -646,6 +640,7 @@ class Certificates(AuthenticatedResource):
class NotificationCertificatesList(AuthenticatedResource): class NotificationCertificatesList(AuthenticatedResource):
""" Defines the 'certificates' endpoint """ """ Defines the 'certificates' endpoint """
def __init__(self): def __init__(self):
self.reqparse = reqparse.RequestParser() self.reqparse = reqparse.RequestParser()
super(NotificationCertificatesList, self).__init__() super(NotificationCertificatesList, self).__init__()
@ -795,6 +790,38 @@ class CertificateExport(AuthenticatedResource):
Host: example.com Host: example.com
Accept: application/json, text/javascript Accept: application/json, text/javascript
{
"export": {
"plugin": {
"pluginOptions": [{
"available": ["Java Key Store (JKS)"],
"required": true,
"type": "select",
"name": "type",
"helpMessage": "Choose the format you wish to export",
"value": "Java Key Store (JKS)"
}, {
"required": false,
"type": "str",
"name": "passphrase",
"validation": "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%*#?&])[A-Za-z\\d$@$!%*#?&]{8,}$",
"helpMessage": "If no passphrase is given one will be generated for you, we highly recommend this. Minimum length is 8."
}, {
"required": false,
"type": "str",
"name": "alias",
"helpMessage": "Enter the alias you wish to use for the keystore."
}],
"version": "unknown",
"description": "Attempts to generate a JKS keystore or truststore",
"title": "Java",
"author": "Kevin Glisson",
"type": "export",
"slug": "java-export"
}
}
}
**Example response**: **Example response**:
@ -804,6 +831,11 @@ class CertificateExport(AuthenticatedResource):
Vary: Accept Vary: Accept
Content-Type: text/javascript Content-Type: text/javascript
{
"data": "base64encodedstring",
"passphrase": "UAWOHW#&@_%!tnwmxh832025",
"extension": "jks"
}
:reqheader Authorization: OAuth token to authenticate :reqheader Authorization: OAuth token to authenticate
:statuscode 200: no error :statuscode 200: no error
@ -831,5 +863,7 @@ api.add_resource(CertificatesStats, '/certificates/stats', endpoint='certificate
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(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates', endpoint='notificationCertificates') api.add_resource(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates',
api.add_resource(CertificatesReplacementsList, '/certificates/<int:certificate_id>/replacements', endpoint='replacements') endpoint='notificationCertificates')
api.add_resource(CertificatesReplacementsList, '/certificates/<int:certificate_id>/replacements',
endpoint='replacements')

View File

@ -0,0 +1,42 @@
<div class="modal-header">
<div class="modal-title">
<h3 class="modal-header">Export <span class="text-muted"><small>{{ certificate.name }}</small></span></h3>
</div>
<div class="modal-body">
<form ng-show="!passphrase" name="exportForm" class="form-horizontal" role="form" novalidate>
<div class="form-group">
<label class="control-label col-sm-2">
Plugin
</label>
<div class="col-sm-10">
<select class="form-control" ng-model="certificate.export.plugin" ng-options="plugin.title for plugin in plugins" required></select>
</div>
</div>
<div class="form-group" ng-repeat="item in certificate.export.plugin.pluginOptions">
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
<div ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
<label class="control-label col-sm-2">
{{ ::item.name | titleCase }}
</label>
<div class="col-sm-10">
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="/^[0-9]{12,12}$/" class="form-control" ng-model="item.value"/>
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available" ng-model="item.value"></select>
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox" ng-model="item.value">
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control" ng-model="item.value" ng-pattern="{{ ::item.validation }}"/>
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine" class="help-block">{{ ::item.helpMessage }}</p>
</div>
</div>
</ng-form>
</div>
</form>
<div ng-show="passphrase">
<h3>Successfully exported!</h3>
<h4>You're passphrase is: <strong>{{ passphrase }}</strong></h4>
<p ng-show="additional">{{ additional }}</p>
</div>
</div>
<div class="modal-footer">
<button type="submit" ng-show="!passphrase" ng-click="save(certificate)" ng-disabled="exportForm.$invalid" class="btn btn-success">Export</button>
<button ng-click="cancel()" class="btn btn-danger">{{ passphrase ? "Close" : "Cancel" }}</button>
</div>
</div>

View File

@ -0,0 +1,28 @@
<div class="form-group">
<label class="control-label col-sm-2">
Replaces
</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" ng-model="certificate.selectedReplacement" placeholder="Certificate123..."
typeahead="certificate.name for certificate in certificateService.findCertificatesByName($viewValue)" typeahead-loading="loadingCertificates"
class="form-control input-md" typeahead-on-select="certificate.attachReplacement($item)" typeahead-min-wait="100"
tooltip="Lemur will mark any certificates being replaced as 'inactive'"
tooltip-trigger="focus" tooltip-placement="top">
<span class="input-group-btn">
<button ng-model="replacements.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
<span class="badge">{{ certificate.replacements.length || 0 }}</span>
</button>
</span>
</div>
<table class="table">
<tr ng-repeat="replacement in certificate.replacements track by $index">
<td><a class="btn btn-sm btn-info">{{ replacement.name }}</a></td>
<td><span class="text-muted">{{ replacement.description }}</span></td>
<td>
<button type="button" ng-click="certificate.removeReplacement($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
</td>
</tr>
</table>
</div>
</div>