Closes #122
This commit is contained in:
parent
ce1fe9321c
commit
d6b3f5af81
|
@ -22,7 +22,8 @@ from lemur.domains.models import Domain
|
||||||
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE
|
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE
|
||||||
|
|
||||||
from lemur.models import certificate_associations, certificate_source_associations, \
|
from lemur.models import certificate_associations, certificate_source_associations, \
|
||||||
certificate_destination_associations, certificate_notification_associations
|
certificate_destination_associations, certificate_notification_associations, \
|
||||||
|
certificate_replacement_associations
|
||||||
|
|
||||||
|
|
||||||
def create_name(issuer, not_before, not_after, subject, san):
|
def create_name(issuer, not_before, not_after, subject, san):
|
||||||
|
@ -32,6 +33,11 @@ def create_name(issuer, not_before, not_after, subject, san):
|
||||||
useful information such as Common Name, Validation dates,
|
useful information such as Common Name, Validation dates,
|
||||||
and Issuer.
|
and Issuer.
|
||||||
|
|
||||||
|
:param san:
|
||||||
|
:param subject:
|
||||||
|
:param not_after:
|
||||||
|
:param issuer:
|
||||||
|
:param not_before:
|
||||||
:rtype : str
|
:rtype : str
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
@ -231,6 +237,11 @@ class Certificate(db.Model):
|
||||||
authority_id = Column(Integer, ForeignKey('authorities.id'))
|
authority_id = Column(Integer, ForeignKey('authorities.id'))
|
||||||
notifications = relationship("Notification", secondary=certificate_notification_associations, backref='certificate')
|
notifications = relationship("Notification", secondary=certificate_notification_associations, backref='certificate')
|
||||||
destinations = relationship("Destination", secondary=certificate_destination_associations, backref='certificate')
|
destinations = relationship("Destination", secondary=certificate_destination_associations, backref='certificate')
|
||||||
|
replaces = relationship("Certificate",
|
||||||
|
secondary=certificate_replacement_associations,
|
||||||
|
primaryjoin=id == certificate_replacement_associations.c.certificate_id, # noqa
|
||||||
|
secondaryjoin=id == certificate_replacement_associations.c.replaced_certificate_id, # noqa
|
||||||
|
backref='replaced')
|
||||||
sources = relationship("Source", secondary=certificate_source_associations, backref='certificate')
|
sources = relationship("Source", secondary=certificate_source_associations, backref='certificate')
|
||||||
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
||||||
|
|
||||||
|
@ -280,11 +291,44 @@ class Certificate(db.Model):
|
||||||
"""
|
"""
|
||||||
return "arn:aws:iam::{}:server-certificate/{}".format(account_number, self.name)
|
return "arn:aws:iam::{}:server-certificate/{}".format(account_number, self.name)
|
||||||
|
|
||||||
def as_dict(self):
|
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
|
||||||
|
|
||||||
|
|
||||||
@event.listens_for(Certificate.destinations, 'append')
|
@event.listens_for(Certificate.destinations, 'append')
|
||||||
def update_destinations(target, value, initiator):
|
def update_destinations(target, value, initiator):
|
||||||
|
"""
|
||||||
|
Attempt to upload the new certificate to the new destination
|
||||||
|
|
||||||
|
:param target:
|
||||||
|
:param value:
|
||||||
|
:param initiator:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
destination_plugin = plugins.get(value.plugin_name)
|
destination_plugin = plugins.get(value.plugin_name)
|
||||||
destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options)
|
destination_plugin.upload(target.name, target.body, target.private_key, target.chain, value.options)
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(Certificate.replaces, 'append')
|
||||||
|
def update_replacement(target, value, initiator):
|
||||||
|
"""
|
||||||
|
When a certificate is marked as 'replaced' it is then marked as in-active
|
||||||
|
|
||||||
|
:param target:
|
||||||
|
:param value:
|
||||||
|
:param initiator:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
value.active = False
|
||||||
|
|
||||||
|
|
||||||
|
@event.listens_for(Certificate, 'before_update')
|
||||||
|
def protect_active(mapper, connection, target):
|
||||||
|
"""
|
||||||
|
When a certificate has a replacement do not allow it to be marked as 'active'
|
||||||
|
|
||||||
|
:param connection:
|
||||||
|
:param mapper:
|
||||||
|
:param target:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if target.active:
|
||||||
|
if target.replaced:
|
||||||
|
raise Exception("Cannot mark certificate as active, certificate has been marked as replaced.")
|
||||||
|
|
|
@ -76,13 +76,16 @@ def find_duplicates(cert_body):
|
||||||
return Certificate.query.filter_by(body=cert_body).all()
|
return Certificate.query.filter_by(body=cert_body).all()
|
||||||
|
|
||||||
|
|
||||||
def update(cert_id, owner, description, active, destinations, notifications):
|
def update(cert_id, owner, description, active, destinations, notifications, replaces):
|
||||||
"""
|
"""
|
||||||
Updates a certificate.
|
Updates a certificate
|
||||||
|
|
||||||
:param cert_id:
|
:param cert_id:
|
||||||
:param owner:
|
:param owner:
|
||||||
|
:param description:
|
||||||
:param active:
|
:param active:
|
||||||
|
:param destinations:
|
||||||
|
:param notifications:
|
||||||
|
:param replaces:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
from lemur.notifications import service as notification_service
|
from lemur.notifications import service as notification_service
|
||||||
|
@ -104,6 +107,7 @@ def update(cert_id, owner, description, active, destinations, notifications):
|
||||||
cert.notifications = new_notifications
|
cert.notifications = new_notifications
|
||||||
|
|
||||||
database.update_list(cert, 'destinations', Destination, destinations)
|
database.update_list(cert, 'destinations', Destination, destinations)
|
||||||
|
database.update_list(cert, 'replaces', Certificate, replaces)
|
||||||
|
|
||||||
cert.owner = owner
|
cert.owner = owner
|
||||||
|
|
||||||
|
@ -165,6 +169,7 @@ def import_certificate(**kwargs):
|
||||||
|
|
||||||
notification_name = 'DEFAULT_SECURITY'
|
notification_name = 'DEFAULT_SECURITY'
|
||||||
notifications = notification_service.create_default_expiration_notifications(notification_name, current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
|
notifications = notification_service.create_default_expiration_notifications(notification_name, current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
|
||||||
|
database.update_list(cert, 'replaces', Certificate, kwargs['replacements'])
|
||||||
cert.notifications = notifications
|
cert.notifications = notifications
|
||||||
|
|
||||||
cert = database.create(cert)
|
cert = database.create(cert)
|
||||||
|
@ -194,8 +199,8 @@ def upload(**kwargs):
|
||||||
g.user.certificates.append(cert)
|
g.user.certificates.append(cert)
|
||||||
|
|
||||||
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
|
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
|
||||||
|
|
||||||
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
|
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
|
||||||
|
database.update_list(cert, 'replaces', Certificate, kwargs['replacements'])
|
||||||
|
|
||||||
# create default notifications for this certificate if none are provided
|
# create default notifications for this certificate if none are provided
|
||||||
notifications = []
|
notifications = []
|
||||||
|
@ -228,7 +233,7 @@ def create(**kwargs):
|
||||||
# do this after the certificate has already been created because if it fails to upload to the third party
|
# do this after the certificate has already been created because if it fails to upload to the third party
|
||||||
# we do not want to lose the certificate information.
|
# we do not want to lose the certificate information.
|
||||||
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
|
database.update_list(cert, 'destinations', Destination, kwargs.get('destinations'))
|
||||||
|
database.update_list(cert, 'replaces', Certificate, kwargs['replacements'])
|
||||||
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
|
database.update_list(cert, 'notifications', Notification, kwargs.get('notifications'))
|
||||||
|
|
||||||
# create default notifications for this certificate if none are provided
|
# create default notifications for this certificate if none are provided
|
||||||
|
|
|
@ -269,7 +269,10 @@ class CertificatesList(AuthenticatedResource):
|
||||||
},
|
},
|
||||||
"commonName": "test",
|
"commonName": "test",
|
||||||
"validityStart": "2015-06-05T07:00:00.000Z",
|
"validityStart": "2015-06-05T07:00:00.000Z",
|
||||||
"validityEnd": "2015-06-16T07:00:00.000Z"
|
"validityEnd": "2015-06-16T07:00:00.000Z",
|
||||||
|
"replacements": [
|
||||||
|
{'id': 123}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
@ -317,6 +320,7 @@ class CertificatesList(AuthenticatedResource):
|
||||||
self.reqparse.add_argument('extensions', type=dict, location='json')
|
self.reqparse.add_argument('extensions', type=dict, location='json')
|
||||||
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('notifications', type=list, default=[], location='json')
|
self.reqparse.add_argument('notifications', type=list, default=[], location='json')
|
||||||
|
self.reqparse.add_argument('replacements', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('validityStart', type=str, location='json') # TODO validate
|
self.reqparse.add_argument('validityStart', type=str, location='json') # TODO validate
|
||||||
self.reqparse.add_argument('validityEnd', type=str, location='json') # TODO validate
|
self.reqparse.add_argument('validityEnd', type=str, location='json') # TODO validate
|
||||||
self.reqparse.add_argument('authority', type=valid_authority, location='json', required=True)
|
self.reqparse.add_argument('authority', type=valid_authority, location='json', required=True)
|
||||||
|
@ -375,6 +379,7 @@ class CertificatesUpload(AuthenticatedResource):
|
||||||
"privateKey": "---Begin Private..."
|
"privateKey": "---Begin Private..."
|
||||||
"destinations": [],
|
"destinations": [],
|
||||||
"notifications": [],
|
"notifications": [],
|
||||||
|
"replacements": [],
|
||||||
"name": "cert1"
|
"name": "cert1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,8 +424,9 @@ class CertificatesUpload(AuthenticatedResource):
|
||||||
self.reqparse.add_argument('owner', type=str, required=True, location='json')
|
self.reqparse.add_argument('owner', type=str, required=True, location='json')
|
||||||
self.reqparse.add_argument('name', type=str, location='json')
|
self.reqparse.add_argument('name', type=str, location='json')
|
||||||
self.reqparse.add_argument('publicCert', type=pem_str, required=True, dest='public_cert', location='json')
|
self.reqparse.add_argument('publicCert', type=pem_str, required=True, dest='public_cert', location='json')
|
||||||
self.reqparse.add_argument('destinations', type=list, default=[], dest='destinations', location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('notifications', type=list, default=[], dest='notifications', location='json')
|
self.reqparse.add_argument('notifications', type=list, default=[], location='json')
|
||||||
|
self.reqparse.add_argument('replacements', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('intermediateCert', type=pem_str, dest='intermediate_cert', location='json')
|
self.reqparse.add_argument('intermediateCert', type=pem_str, dest='intermediate_cert', location='json')
|
||||||
self.reqparse.add_argument('privateKey', type=private_key_str, dest='private_key', location='json')
|
self.reqparse.add_argument('privateKey', type=private_key_str, dest='private_key', location='json')
|
||||||
|
|
||||||
|
@ -575,7 +581,8 @@ class Certificates(AuthenticatedResource):
|
||||||
"owner": "jimbob@example.com",
|
"owner": "jimbob@example.com",
|
||||||
"active": false
|
"active": false
|
||||||
"notifications": [],
|
"notifications": [],
|
||||||
"destinations": []
|
"destinations": [],
|
||||||
|
"replacements": []
|
||||||
}
|
}
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
@ -614,6 +621,7 @@ class Certificates(AuthenticatedResource):
|
||||||
self.reqparse.add_argument('description', type=str, location='json')
|
self.reqparse.add_argument('description', type=str, location='json')
|
||||||
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('notifications', type=notification_list, default=[], location='json')
|
self.reqparse.add_argument('notifications', type=notification_list, default=[], location='json')
|
||||||
|
self.reqparse.add_argument('replacements', type=list, default=[], location='json')
|
||||||
args = self.reqparse.parse_args()
|
args = self.reqparse.parse_args()
|
||||||
|
|
||||||
cert = service.get(certificate_id)
|
cert = service.get(certificate_id)
|
||||||
|
@ -628,7 +636,8 @@ class Certificates(AuthenticatedResource):
|
||||||
args['description'],
|
args['description'],
|
||||||
args['active'],
|
args['active'],
|
||||||
args['destinations'],
|
args['destinations'],
|
||||||
args['notifications']
|
args['notifications'],
|
||||||
|
args['replacements']
|
||||||
)
|
)
|
||||||
|
|
||||||
return dict(message='You are not authorized to update this certificate'), 403
|
return dict(message='You are not authorized to update this certificate'), 403
|
||||||
|
@ -711,9 +720,65 @@ class NotificationCertificatesList(AuthenticatedResource):
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
|
|
||||||
|
class CertificatesReplacementsList(AuthenticatedResource):
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(CertificatesReplacementsList, self).__init__()
|
||||||
|
|
||||||
|
@marshal_items(FIELDS)
|
||||||
|
def get(self, certificate_id):
|
||||||
|
"""
|
||||||
|
.. http:get:: /certificates/1/replacements
|
||||||
|
|
||||||
|
One certificate
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /certificates/1/replacements HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
[{
|
||||||
|
"id": 1,
|
||||||
|
"name": "cert1",
|
||||||
|
"description": "this is cert1",
|
||||||
|
"bits": 2048,
|
||||||
|
"deleted": false,
|
||||||
|
"issuer": "ExampeInc.",
|
||||||
|
"serial": "123450",
|
||||||
|
"chain": "-----Begin ...",
|
||||||
|
"body": "-----Begin ...",
|
||||||
|
"san": true,
|
||||||
|
"owner": "bob@example.com",
|
||||||
|
"active": true,
|
||||||
|
"notBefore": "2015-06-05T17:09:39",
|
||||||
|
"notAfter": "2015-06-10T17:09:39",
|
||||||
|
"signingAlgorithm": "sha2",
|
||||||
|
"cn": "example.com",
|
||||||
|
"status": "unknown"
|
||||||
|
}]
|
||||||
|
|
||||||
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 403: unauthenticated
|
||||||
|
"""
|
||||||
|
return service.get(certificate_id).replaces
|
||||||
|
|
||||||
|
|
||||||
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(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates', endpoint='notificationCertificates')
|
api.add_resource(NotificationCertificatesList, '/notifications/<int:notification_id>/certificates', endpoint='notificationCertificates')
|
||||||
|
api.add_resource(CertificatesReplacementsList, '/certificates/<int:certificate_id>/replacements', endpoint='replacements')
|
||||||
|
|
|
@ -36,6 +36,14 @@ certificate_notification_associations = db.Table('certificate_notification_assoc
|
||||||
Column('certificate_id', Integer,
|
Column('certificate_id', Integer,
|
||||||
ForeignKey('certificates.id', ondelete='cascade'))
|
ForeignKey('certificates.id', ondelete='cascade'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
certificate_replacement_associations = db.Table('certificate_replacement_associations',
|
||||||
|
Column('replaced_certificate_id', Integer,
|
||||||
|
ForeignKey('certificates.id', ondelete='cascade')),
|
||||||
|
Column('certificate_id', Integer,
|
||||||
|
ForeignKey('certificates.id', ondelete='cascade'))
|
||||||
|
)
|
||||||
|
|
||||||
roles_users = db.Table('roles_users',
|
roles_users = db.Table('roles_users',
|
||||||
Column('user_id', Integer, ForeignKey('users.id')),
|
Column('user_id', Integer, ForeignKey('users.id')),
|
||||||
Column('role_id', Integer, ForeignKey('roles.id'))
|
Column('role_id', Integer, ForeignKey('roles.id'))
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
<div class="btn-group-vertical pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a class="btn btn-sm btn-default" ui-sref="authority({name: authority.name})">Permalink</a>
|
<a class="btn btn-sm btn-default" ui-sref="authority({name: authority.name})">Permalink</a>
|
||||||
<button tooltip="Edit Authority" ng-click="edit(authority.id)" class="btn btn-sm btn-info">
|
<button tooltip="Edit Authority" ng-click="edit(authority.id)" class="btn btn-sm btn-info">
|
||||||
Edit
|
Edit
|
||||||
|
|
|
@ -5,6 +5,7 @@ angular.module('lemur')
|
||||||
CertificateApi.get(editId).then(function (certificate) {
|
CertificateApi.get(editId).then(function (certificate) {
|
||||||
CertificateService.getNotifications(certificate);
|
CertificateService.getNotifications(certificate);
|
||||||
CertificateService.getDestinations(certificate);
|
CertificateService.getDestinations(certificate);
|
||||||
|
CertificateService.getReplacements(certificate);
|
||||||
$scope.certificate = certificate;
|
$scope.certificate = certificate;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.certificateService = CertificateService;
|
||||||
$scope.destinationService = DestinationService;
|
$scope.destinationService = DestinationService;
|
||||||
$scope.notificationService = NotificationService;
|
$scope.notificationService = NotificationService;
|
||||||
})
|
})
|
||||||
|
@ -123,6 +125,7 @@ angular.module('lemur')
|
||||||
$scope.plugins = plugins;
|
$scope.plugins = plugins;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.certificateService = CertificateService;
|
||||||
$scope.authorityService = AuthorityService;
|
$scope.authorityService = AuthorityService;
|
||||||
$scope.destinationService = DestinationService;
|
$scope.destinationService = DestinationService;
|
||||||
$scope.notificationService = NotificationService;
|
$scope.notificationService = NotificationService;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
<p ng-show="editForm.description.$invalid && !editForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-include="'angular/certificates/certificate/replaces.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -5,9 +5,14 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Owner
|
Owner
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" name="ownerEmail" ng-model="certificate.owner" placeholder="TeamDL@example.com" tooltip="This is the certificates team distribution list or main point of contact" class="form-control" required/>
|
<input type="email" name="ownerEmail" ng-model="certificate.owner" placeholder="TeamDL@example.com"
|
||||||
<p ng-show="trackingForm.ownerEmail.$invalid && !trackingForm.ownerEmail.$pristine" class="help-block">You must enter an Certificate owner</p>
|
tooltip="This is the certificates team distribution list or main point of contact" class="form-control"
|
||||||
|
required/>
|
||||||
|
|
||||||
|
<p ng-show="trackingForm.ownerEmail.$invalid && !trackingForm.ownerEmail.$pristine" class="help-block">You must
|
||||||
|
enter an Certificate owner</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
|
@ -15,9 +20,13 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea name="description" ng-model="certificate.description" placeholder="Something elegant" class="form-control" required></textarea>
|
<textarea name="description" ng-model="certificate.description" placeholder="Something elegant"
|
||||||
<p ng-show="trackingForm.description.$invalid && !trackingForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for.</p>
|
class="form-control" required></textarea>
|
||||||
|
|
||||||
|
<p ng-show="trackingForm.description.$invalid && !trackingForm.description.$pristine" class="help-block">You
|
||||||
|
must give a short description about this authority will be used for.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
|
@ -25,11 +34,17 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Certificate Authority
|
Certificate Authority
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group col-sm-12">
|
<div class="input-group col-sm-12">
|
||||||
<input name="selectedAuthority" tooltip="If you are unsure which authority you need; you most likely want to use 'verisign'" type="text" ng-model="certificate.selectedAuthority" placeholder="Authority Name" typeahead-on-select="certificate.attachAuthority($item)"
|
<input name="selectedAuthority"
|
||||||
typeahead="authority.name for authority in authorityService.findActiveAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
tooltip="If you are unsure which authority you need; you most likely want to use 'verisign'"
|
||||||
class="form-control" typeahead-wait-ms="1000" typeahead-template-url="angular/authorities/authority/select.tpl.html" required>
|
type="text" ng-model="certificate.selectedAuthority" placeholder="Authority Name"
|
||||||
|
typeahead-on-select="certificate.attachAuthority($item)"
|
||||||
|
typeahead="authority.name for authority in authorityService.findActiveAuthorityByName($viewValue)"
|
||||||
|
typeahead-loading="loadingAuthorities"
|
||||||
|
class="form-control" typeahead-wait-ms="1000"
|
||||||
|
typeahead-template-url="angular/authorities/authority/select.tpl.html" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,8 +52,10 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Certificate Template
|
Certificate Template
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" ng-change="certificate.useTemplate()" name="certificateTemplate" ng-model="certificate.template" ng-options="template.name for template in templates"></select>
|
<select class="form-control" ng-change="certificate.useTemplate()" name="certificateTemplate"
|
||||||
|
ng-model="certificate.template" ng-options="template.name for template in templates"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
|
@ -46,37 +63,54 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Common Name
|
Common Name
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="commonName" tooltip="If you need a certificate with multiple domains enter your primary domain here and the rest under 'Subject Alternate Names' in the next few panels" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" ng-maxlength="64" required/>
|
<input name="commonName"
|
||||||
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name and it must be less than 64 characters</p>
|
tooltip="If you need a certificate with multiple domains enter your primary domain here and the rest under 'Subject Alternate Names' in the next few panels"
|
||||||
|
ng-model="certificate.commonName" placeholder="Common Name" class="form-control" ng-maxlength="64"
|
||||||
|
required/>
|
||||||
|
|
||||||
|
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must
|
||||||
|
enter a common name and it must be less than 64 characters</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
<label class="control-label col-sm-2"
|
||||||
|
tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
||||||
Validity Range <span class="glyphicon glyphicon-question-sign"></span>
|
Validity Range <span class="glyphicon glyphicon-question-sign"></span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input tooltip="Starting Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="$parent.openNotBefore.isOpen" min-date="certificate.authority.notBefore" max-date="certificate.authority.maxDate" ng-model="certificate.validityStart" />
|
<input tooltip="Starting Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd"
|
||||||
|
is-open="$parent.openNotBefore.isOpen" min-date="certificate.authority.notBefore"
|
||||||
|
max-date="certificate.authority.maxDate" ng-model="certificate.validityStart"/>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-default" ng-click="openNotBefore($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
<button class="btn btn-default" ng-click="openNotBefore($event)"><i
|
||||||
|
class="glyphicon glyphicon-calendar"></i></button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span style="padding-top: 15px" class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
<span style="padding-top: 15px" class="text-center col-sm-2"><label><span
|
||||||
|
class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
||||||
|
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input tooltip="Ending Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="$parent.openNotAfter.isOpen" min-date="certificate.authority.notBefore" max-date="certificate.authority.maxDate" ng-model="certificate.validityEnd" />
|
<input tooltip="Ending Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd"
|
||||||
|
is-open="$parent.openNotAfter.isOpen" min-date="certificate.authority.notBefore"
|
||||||
|
max-date="certificate.authority.maxDate" ng-model="certificate.validityEnd"/>
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-default" ng-click="openNotAfter($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
<button class="btn btn-default" ng-click="openNotAfter($event)"><i
|
||||||
|
class="glyphicon glyphicon-calendar"></i></button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-include="'angular/certificates/certificate/replaces.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@ angular.module('lemur')
|
||||||
|
|
||||||
$scope.destinationService = DestinationService;
|
$scope.destinationService = DestinationService;
|
||||||
$scope.notificationService = NotificationService;
|
$scope.notificationService = NotificationService;
|
||||||
|
$scope.certificateService = CertificateService;
|
||||||
|
|
||||||
PluginService.getByType('destination').then(function (plugins) {
|
PluginService.getByType('destination').then(function (plugins) {
|
||||||
$scope.plugins = plugins;
|
$scope.plugins = plugins;
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
class="help-block">Enter a valid certificate.</p>
|
class="help-block">Enter a valid certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-include="'angular/certificates/certificate/replaces.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/notifications.tpl.html'"></div>
|
||||||
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -67,6 +67,16 @@ angular.module('lemur')
|
||||||
removeDestination: function (index) {
|
removeDestination: function (index) {
|
||||||
this.destinations.splice(index, 1);
|
this.destinations.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
attachReplacement: function (replacement) {
|
||||||
|
this.selectedReplacement = null;
|
||||||
|
if (this.replacements === undefined) {
|
||||||
|
this.replacements = [];
|
||||||
|
}
|
||||||
|
this.replacements.push(replacement);
|
||||||
|
},
|
||||||
|
removeReplacement: function (index) {
|
||||||
|
this.replacements.splice(index, 1);
|
||||||
|
},
|
||||||
attachNotification: function (notification) {
|
attachNotification: function (notification) {
|
||||||
this.selectedNotification = null;
|
this.selectedNotification = null;
|
||||||
if (this.notifications === undefined) {
|
if (this.notifications === undefined) {
|
||||||
|
@ -149,6 +159,12 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CertificateService.getReplacements = function (certificate) {
|
||||||
|
return certificate.getList('replacements').then(function (replacements) {
|
||||||
|
certificate.replacements = replacements;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
CertificateService.getDefaults = function (certificate) {
|
CertificateService.getDefaults = function (certificate) {
|
||||||
return DefaultService.get().then(function (defaults) {
|
return DefaultService.get().then(function (defaults) {
|
||||||
certificate.country = defaults.country;
|
certificate.country = defaults.country;
|
||||||
|
|
|
@ -36,6 +36,7 @@ angular.module('lemur')
|
||||||
CertificateService.getDomains(certificate);
|
CertificateService.getDomains(certificate);
|
||||||
CertificateService.getDestinations(certificate);
|
CertificateService.getDestinations(certificate);
|
||||||
CertificateService.getNotifications(certificate);
|
CertificateService.getNotifications(certificate);
|
||||||
|
CertificateService.getReplacements(certificate);
|
||||||
CertificateService.getAuthority(certificate);
|
CertificateService.getAuthority(certificate);
|
||||||
CertificateService.getCreator(certificate);
|
CertificateService.getCreator(certificate);
|
||||||
});
|
});
|
||||||
|
@ -101,6 +102,7 @@ angular.module('lemur')
|
||||||
body: 'Unable to update! ' + response.data.message,
|
body: 'Unable to update! ' + response.data.message,
|
||||||
timeout: 100000
|
timeout: 100000
|
||||||
});
|
});
|
||||||
|
certificate.active = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$scope.getCertificateStatus = function () {
|
$scope.getCertificateStatus = function () {
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
<tr ng-class="{'even-row': $even }" ng-repeat-start="certificate in $data track by $index">
|
<tr ng-class="{'even-row': $even }" ng-repeat-start="certificate in $data track by $index">
|
||||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>{{ certificate.name }}</li>
|
<li>{{ ::certificate.name }}</li>
|
||||||
<li><span class="text-muted">{{ certificate.owner }}</span></li>
|
<li><span class="text-muted">{{ ::certificate.owner }}</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getCertificateStatus()">
|
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getCertificateStatus()">
|
||||||
|
@ -37,10 +37,10 @@
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Issuer'" sortable="'issuer'" filter="{ 'issuer': 'text' }">
|
<td data-title="'Issuer'" sortable="'issuer'" filter="{ 'issuer': 'text' }">
|
||||||
{{ certificate.authority.name || certificate.issuer }}
|
{{ ::certificate.authority.name || certificate.issuer }}
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Common Name'" filter="{ 'cn': 'text'}">
|
<td data-title="'Common Name'" filter="{ 'cn': 'text'}">
|
||||||
{{ certificate.cn }}
|
{{ ::certificate.cn }}
|
||||||
</td>
|
</td>
|
||||||
<td class="col-md-2" data-title="''">
|
<td class="col-md-2" data-title="''">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
|
@ -61,19 +61,19 @@
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Creator</strong>
|
<strong>Creator</strong>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
{{ certificate.creator.email }}
|
{{ ::certificate.creator.email }}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Not Before</strong>
|
<strong>Not Before</strong>
|
||||||
<span class="pull-right" tooltip="{{ certificate.notBefore }}">
|
<span class="pull-right" tooltip="{{ ::certificate.notBefore }}">
|
||||||
{{ momentService.createMoment(certificate.notBefore) }}
|
{{ ::momentService.createMoment(certificate.notBefore) }}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Not After</strong>
|
<strong>Not After</strong>
|
||||||
<span class="pull-right" tooltip="{{ certificate.notAfter }}">
|
<span class="pull-right" tooltip="{{ ::certificate.notAfter }}">
|
||||||
{{ momentService.createMoment(certificate.notAfter) }}
|
{{ ::momentService.createMoment(certificate.notAfter) }}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
|
@ -85,15 +85,15 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Bits</strong>
|
<strong>Bits</strong>
|
||||||
<span class="pull-right">{{ certificate.bits }}</span>
|
<span class="pull-right">{{ ::certificate.bits }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Signing Algorithm</strong>
|
<strong>Signing Algorithm</strong>
|
||||||
<span class="pull-right">{{ certificate.signingAlgorithm }}</span>
|
<span class="pull-right">{{ ::certificate.signingAlgorithm }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Serial</strong>
|
<strong>Serial</strong>
|
||||||
<span class="pull-right">{{ certificate.serial }}</span>
|
<span class="pull-right">{{ ::certificate.serial }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
tooltip="Lemur will attempt to check a certificates validity, this is used to track whether a certificate as been revoked"
|
tooltip="Lemur will attempt to check a certificates validity, this is used to track whether a certificate as been revoked"
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>Description</strong>
|
<strong>Description</strong>
|
||||||
<p>{{ certificate.description }}</p>
|
<p>{{ ::certificate.description }}</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</tab>
|
</tab>
|
||||||
|
@ -115,8 +115,8 @@
|
||||||
<tab-heading>Notifications</tab-heading>
|
<tab-heading>Notifications</tab-heading>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item" ng-repeat="notification in certificate.notifications">
|
<li class="list-group-item" ng-repeat="notification in certificate.notifications">
|
||||||
<strong>{{ notification.label }}</strong>
|
<strong>{{ ::notification.label }}</strong>
|
||||||
<span class="pull-right">{{ notification.description}}</span>
|
<span class="pull-right">{{ ::notification.description}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</tab>
|
</tab>
|
||||||
|
@ -124,18 +124,27 @@
|
||||||
<tab-heading>Destinations</tab-heading>
|
<tab-heading>Destinations</tab-heading>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item" ng-repeat="destination in certificate.destinations">
|
<li class="list-group-item" ng-repeat="destination in certificate.destinations">
|
||||||
<strong>{{ destination.label }}</strong>
|
<strong>{{ ::destination.label }}</strong>
|
||||||
<span class="pull-right">{{ destination.description }}</span>
|
<span class="pull-right">{{ ::destination.description }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</tab>
|
</tab>
|
||||||
<tab>
|
<tab>
|
||||||
<tab-heading>Domains</tab-heading>
|
<tab-heading>Domains</tab-heading>
|
||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
<a href="#/domains/{{ domain.id }}" class="list-group-item"
|
<a href="#/domains/{{ ::domain.id }}" class="list-group-item"
|
||||||
ng-repeat="domain in certificate.domains">{{ domain.name }}</a>
|
ng-repeat="domain in certificate.domains">{{ ::domain.name }}</a>
|
||||||
</div>
|
</div>
|
||||||
</tab>
|
</tab>
|
||||||
|
<tab>
|
||||||
|
<tab-heading>Replaces</tab-heading>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item" ng-repeat="replacement in certificate.replacements">
|
||||||
|
<strong>{{ ::replacement.name }}</strong>
|
||||||
|
<p>{{ ::replacement.description}}</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</tab>
|
||||||
</tabset>
|
</tabset>
|
||||||
<tabset justified="true" class="col-md-6">
|
<tabset justified="true" class="col-md-6">
|
||||||
<tab>
|
<tab>
|
||||||
|
@ -145,7 +154,7 @@
|
||||||
tooltip="Copy chain to clipboard" tooltip-trigger="mouseenter" clipboard
|
tooltip="Copy chain to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||||
text="certificate.chain"></button>
|
text="certificate.chain"></button>
|
||||||
</tab-heading>
|
</tab-heading>
|
||||||
<pre style="width: 100%">{{ certificate.chain }}</pre>
|
<pre style="width: 100%">{{ ::certificate.chain }}</pre>
|
||||||
</tab>
|
</tab>
|
||||||
<tab>
|
<tab>
|
||||||
<tab-heading>
|
<tab-heading>
|
||||||
|
@ -154,7 +163,7 @@
|
||||||
tooltip="Copy certificate to clipboard" tooltip-trigger="mouseenter" clipboard
|
tooltip="Copy certificate to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||||
text="certificate.body"></button>
|
text="certificate.body"></button>
|
||||||
</tab-heading>
|
</tab-heading>
|
||||||
<pre style="width: 100%">{{ certificate.body }}</pre>
|
<pre style="width: 100%">{{ ::certificate.body }}</pre>
|
||||||
</tab>
|
</tab>
|
||||||
<tab ng-click="loadPrivateKey(certificate)">
|
<tab ng-click="loadPrivateKey(certificate)">
|
||||||
<tab-heading>
|
<tab-heading>
|
||||||
|
@ -163,7 +172,7 @@
|
||||||
tooltip="Copy key to clipboard" tooltip-trigger="mouseenter" clipboard
|
tooltip="Copy key to clipboard" tooltip-trigger="mouseenter" clipboard
|
||||||
text="certificate.privateKey"></button>
|
text="certificate.privateKey"></button>
|
||||||
</tab-heading>
|
</tab-heading>
|
||||||
<pre style="width: 100%">{{ certificate.privateKey }}</pre>
|
<pre style="width: 100%">{{ ::certificate.privateKey }}</pre>
|
||||||
</tab>
|
</tab>
|
||||||
</tabset>
|
</tabset>
|
||||||
</td>
|
</td>
|
||||||
|
|
Loading…
Reference in New Issue