[WIP] - 422 elb rotate (#493)
* Initial work on certificate rotation. * Adding ability to get additional certificate info. * - Adding endpoint rotation. - Removes the g requirement from all services to enable easier testing.
This commit is contained in:
@ -5,16 +5,16 @@
|
||||
:license: Apache, see LICENSE for more details.
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
import datetime
|
||||
import arrow
|
||||
|
||||
import lemur.common.utils
|
||||
from flask import current_app
|
||||
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql.expression import case
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy import event, Integer, ForeignKey, String, DateTime, PassiveDefault, func, Column, Text, Boolean
|
||||
from sqlalchemy import event, Integer, ForeignKey, String, PassiveDefault, func, Column, Text, Boolean
|
||||
|
||||
import lemur.common.utils
|
||||
from lemur.database import db
|
||||
from lemur.models import certificate_associations, certificate_source_associations, \
|
||||
certificate_destination_associations, certificate_notification_associations, \
|
||||
@ -22,6 +22,8 @@ from lemur.models import certificate_associations, certificate_source_associatio
|
||||
from lemur.plugins.base import plugins
|
||||
from lemur.utils import Vault
|
||||
|
||||
from sqlalchemy_utils.types.arrow import ArrowType
|
||||
|
||||
from lemur.common import defaults
|
||||
from lemur.domains.models import Domain
|
||||
|
||||
@ -53,9 +55,9 @@ class Certificate(db.Model):
|
||||
cn = Column(String(128))
|
||||
deleted = Column(Boolean, index=True)
|
||||
|
||||
not_before = Column(DateTime)
|
||||
not_after = Column(DateTime)
|
||||
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
|
||||
not_before = Column(ArrowType)
|
||||
not_after = Column(ArrowType)
|
||||
date_created = Column(ArrowType, PassiveDefault(func.now()), nullable=False)
|
||||
|
||||
signing_algorithm = Column(String(128))
|
||||
status = Column(String(128))
|
||||
@ -120,16 +122,41 @@ class Certificate(db.Model):
|
||||
def active(self):
|
||||
return self.notify
|
||||
|
||||
@property
|
||||
def organization(self):
|
||||
cert = lemur.common.utils.parse_certificate(self.body)
|
||||
return defaults.organization(cert)
|
||||
|
||||
@property
|
||||
def organizational_unit(self):
|
||||
cert = lemur.common.utils.parse_certificate(self.body)
|
||||
return defaults.organizational_unit(cert)
|
||||
|
||||
@property
|
||||
def country(self):
|
||||
cert = lemur.common.utils.parse_certificate(self.body)
|
||||
return defaults.country(cert)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
cert = lemur.common.utils.parse_certificate(self.body)
|
||||
return defaults.state(cert)
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
cert = lemur.common.utils.parse_certificate(self.body)
|
||||
return defaults.location(cert)
|
||||
|
||||
@hybrid_property
|
||||
def expired(self):
|
||||
if self.not_after <= datetime.datetime.now():
|
||||
if self.not_after <= arrow.utcnow():
|
||||
return True
|
||||
|
||||
@expired.expression
|
||||
def expired(cls):
|
||||
return case(
|
||||
[
|
||||
(cls.now_after <= datetime.datetime.now(), True)
|
||||
(cls.now_after <= arrow.utcnow(), True)
|
||||
],
|
||||
else_=False
|
||||
)
|
||||
|
@ -8,7 +8,7 @@
|
||||
import arrow
|
||||
|
||||
from sqlalchemy import func, or_
|
||||
from flask import g, current_app
|
||||
from flask import current_app
|
||||
|
||||
from lemur import database
|
||||
from lemur.extensions import metrics
|
||||
@ -201,11 +201,7 @@ def upload(**kwargs):
|
||||
|
||||
cert = database.create(cert)
|
||||
|
||||
try:
|
||||
g.user.certificates.append(cert)
|
||||
except AttributeError:
|
||||
current_app.logger.debug("No user to associate uploaded certificate to.")
|
||||
|
||||
kwargs['creator'].certificates.append(cert)
|
||||
return database.update(cert)
|
||||
|
||||
|
||||
@ -213,7 +209,6 @@ def create(**kwargs):
|
||||
"""
|
||||
Creates a new certificate.
|
||||
"""
|
||||
kwargs['creator'] = g.user.email
|
||||
cert_body, private_key, cert_chain = mint(**kwargs)
|
||||
kwargs['body'] = cert_body
|
||||
kwargs['private_key'] = private_key
|
||||
@ -228,7 +223,7 @@ def create(**kwargs):
|
||||
|
||||
cert = Certificate(**kwargs)
|
||||
|
||||
g.user.certificates.append(cert)
|
||||
kwargs['creator'].certificates.append(cert)
|
||||
cert.authority = kwargs['authority']
|
||||
database.commit()
|
||||
|
||||
@ -286,10 +281,10 @@ def render(args):
|
||||
query = database.filter(query, Certificate, terms)
|
||||
|
||||
if show:
|
||||
sub_query = database.session_query(Role.name).filter(Role.user_id == g.user.id).subquery()
|
||||
sub_query = database.session_query(Role.name).filter(Role.user_id == args['user'].id).subquery()
|
||||
query = query.filter(
|
||||
or_(
|
||||
Certificate.user_id == g.user.id,
|
||||
Certificate.user_id == args['user'].id,
|
||||
Certificate.owner.in_(sub_query)
|
||||
)
|
||||
)
|
||||
@ -476,10 +471,9 @@ def calculate_reissue_range(start, end):
|
||||
new_start = arrow.utcnow().date()
|
||||
new_end = new_start + span
|
||||
|
||||
return new_start, new_end
|
||||
return new_start, arrow.get(new_end)
|
||||
|
||||
|
||||
# TODO pull the OU, O, CN, etc + other extensions.
|
||||
def get_certificate_primitives(certificate):
|
||||
"""
|
||||
Retrieve key primitive from a certificate such that the certificate
|
||||
@ -491,6 +485,7 @@ def get_certificate_primitives(certificate):
|
||||
start, end = calculate_reissue_range(certificate.not_before, certificate.not_after)
|
||||
names = [{'name_type': 'DNSName', 'value': x.name} for x in certificate.domains]
|
||||
|
||||
# TODO pull additional extensions
|
||||
extensions = {
|
||||
'sub_alt_names': {
|
||||
'names': names
|
||||
@ -506,5 +501,31 @@ def get_certificate_primitives(certificate):
|
||||
destinations=certificate.destinations,
|
||||
roles=certificate.roles,
|
||||
extensions=extensions,
|
||||
owner=certificate.owner
|
||||
owner=certificate.owner,
|
||||
organization=certificate.organization,
|
||||
organizational_unit=certificate.organizational_unit,
|
||||
country=certificate.country,
|
||||
state=certificate.state,
|
||||
location=certificate.location
|
||||
)
|
||||
|
||||
|
||||
def reissue_certificate(certificate, replace=None, user=None):
|
||||
"""
|
||||
Reissue certificate with the same properties of the given certificate.
|
||||
:param certificate:
|
||||
:return:
|
||||
"""
|
||||
primitives = get_certificate_primitives(certificate)
|
||||
|
||||
if not user:
|
||||
primitives['creator'] = certificate.user
|
||||
else:
|
||||
primitives['creator'] = user
|
||||
|
||||
new_cert = create(**primitives)
|
||||
|
||||
if replace:
|
||||
certificate.notify = False
|
||||
|
||||
return new_cert
|
||||
|
@ -8,7 +8,7 @@
|
||||
import base64
|
||||
from builtins import str
|
||||
|
||||
from flask import Blueprint, make_response, jsonify
|
||||
from flask import Blueprint, make_response, jsonify, g
|
||||
from flask.ext.restful import reqparse, Api
|
||||
|
||||
from lemur.common.schema import validate_schema
|
||||
@ -129,6 +129,7 @@ class CertificatesList(AuthenticatedResource):
|
||||
parser.add_argument('show', type=str, location='args')
|
||||
|
||||
args = parser.parse_args()
|
||||
args['user'] = g.user
|
||||
return service.render(args)
|
||||
|
||||
@validate_schema(certificate_input_schema, certificate_output_schema)
|
||||
@ -265,6 +266,7 @@ class CertificatesList(AuthenticatedResource):
|
||||
authority_permission = AuthorityPermission(data['authority'].id, roles)
|
||||
|
||||
if authority_permission.can():
|
||||
data['creator'] = g.user
|
||||
return service.create(**data)
|
||||
|
||||
return dict(message="You are not authorized to use {0}".format(data['authority'].name)), 403
|
||||
@ -371,6 +373,7 @@ class CertificatesUpload(AuthenticatedResource):
|
||||
"""
|
||||
if data.get('destinations'):
|
||||
if data.get('private_key'):
|
||||
data['creator'] = g.user
|
||||
return service.upload(**data)
|
||||
else:
|
||||
raise Exception("Private key must be provided in order to upload certificate to AWS")
|
||||
@ -740,6 +743,7 @@ class NotificationCertificatesList(AuthenticatedResource):
|
||||
|
||||
args = parser.parse_args()
|
||||
args['notification_id'] = notification_id
|
||||
args['user'] = g.current_user
|
||||
return service.render(args)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user