Exemple #1
0
class Endpoint(db.Model):
    __tablename__ = 'endpoints'
    id = Column(Integer, primary_key=True)
    owner = Column(String(128))
    name = Column(String(128))
    dnsname = Column(String(256))
    type = Column(String(128))
    active = Column(Boolean, default=True)
    port = Column(Integer)
    policy_id = Column(Integer, ForeignKey('policy.id'))
    policy = relationship('Policy', backref='endpoint')
    certificate_id = Column(Integer, ForeignKey('certificates.id'))
    source_id = Column(Integer, ForeignKey('sources.id'))
    sensitive = Column(Boolean, default=False)
    source = relationship('Source', back_populates='endpoints')
    last_updated = Column(DateTime,
                          PassiveDefault(func.now()),
                          onupdate=func.now(),
                          nullable=False)
    date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)

    @property
    def issues(self):
        issues = []

        for cipher in self.policy.ciphers:
            if cipher.deprecated:
                issues.append({
                    'name':
                    'deprecated cipher',
                    'value':
                    '{0} has been deprecated consider removing it.'.format(
                        cipher.name)
                })

        if self.certificate.expired:
            issues.append({
                'name':
                'expired certificate',
                'value':
                'There is an expired certificate attached to this endpoint consider replacing it.'
            })

        if self.certificate.revoked:
            issues.append({
                'name':
                'revoked',
                'value':
                'There is a revoked certificate attached to this endpoint consider replacing it.'
            })

        return issues

    def __repr__(self):
        return "Endpoint(name={name})".format(name=self.name)
Exemple #2
0
class Log(db.Model):
    __tablename__ = 'logs'
    id = Column(Integer, primary_key=True)
    certificate_id = Column(Integer, ForeignKey('certificates.id'))
    log_type = Column(Enum('key_view', 'create_cert', 'update_cert', 'revoke_cert', 'delete_cert', name='log_type'), nullable=False)
    logged_at = Column(ArrowType(), PassiveDefault(func.now()), nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
Exemple #3
0
class Authority(db.Model):
    __tablename__ = "authorities"
    id = Column(Integer, primary_key=True)
    owner = Column(String(128), nullable=False)
    name = Column(String(128), unique=True)
    body = Column(Text())
    chain = Column(Text())
    active = Column(Boolean, default=True)
    plugin_name = Column(String(64))
    description = Column(Text)
    options = Column(JSON)
    date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
    roles = relationship(
        "Role",
        secondary=roles_authorities,
        passive_deletes=True,
        backref=db.backref("authority"),
        lazy="dynamic",
    )
    user_id = Column(Integer, ForeignKey("users.id"))
    authority_certificate = relationship(
        "Certificate",
        backref="root_authority",
        uselist=False,
        foreign_keys="Certificate.root_authority_id",
    )
    certificates = relationship("Certificate",
                                backref="authority",
                                foreign_keys="Certificate.authority_id")

    authority_pending_certificate = relationship(
        "PendingCertificate",
        backref="root_authority",
        uselist=False,
        foreign_keys="PendingCertificate.root_authority_id",
    )
    pending_certificates = relationship(
        "PendingCertificate",
        backref="authority",
        foreign_keys="PendingCertificate.authority_id",
    )

    def __init__(self, **kwargs):
        self.owner = kwargs["owner"]
        self.roles = kwargs.get("roles", [])
        self.name = kwargs.get("name")
        self.description = kwargs.get("description")
        self.authority_certificate = kwargs["authority_certificate"]
        self.plugin_name = kwargs["plugin"]["slug"]
        self.options = kwargs.get("options")

    @property
    def plugin(self):
        return plugins.get(self.plugin_name)

    def __repr__(self):
        return "Authority(name={name})".format(name=self.name)
Exemple #4
0
class Authority(db.Model):
    __tablename__ = 'authorities'
    id = Column(Integer, primary_key=True)
    owner = Column(String(128), nullable=False)
    name = Column(String(128), unique=True)
    body = Column(Text())
    chain = Column(Text())
    active = Column(Boolean, default=True)
    plugin_name = Column(String(64))
    description = Column(Text)
    options = Column(JSON)
    date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
    roles = relationship('Role',
                         secondary=roles_authorities,
                         passive_deletes=True,
                         backref=db.backref('authority'),
                         lazy='dynamic')
    user_id = Column(Integer, ForeignKey('users.id'))
    authority_certificate = relationship(
        "Certificate",
        backref='root_authority',
        uselist=False,
        foreign_keys='Certificate.root_authority_id')
    certificates = relationship("Certificate",
                                backref='authority',
                                foreign_keys='Certificate.authority_id')

    authority_pending_certificate = relationship(
        "PendingCertificate",
        backref='root_authority',
        uselist=False,
        foreign_keys='PendingCertificate.root_authority_id')
    pending_certificates = relationship(
        'PendingCertificate',
        backref='authority',
        foreign_keys='PendingCertificate.authority_id')

    def __init__(self, **kwargs):
        self.owner = kwargs['owner']
        self.roles = kwargs.get('roles', [])
        self.name = kwargs.get('name')
        self.description = kwargs.get('description')
        self.authority_certificate = kwargs['authority_certificate']
        self.plugin_name = kwargs['plugin']['slug']
        self.options = kwargs.get('options')

    @property
    def plugin(self):
        return plugins.get(self.plugin_name)

    def __repr__(self):
        return "Authority(name={name})".format(name=self.name)
Exemple #5
0
class Authority(db.Model):
    __tablename__ = 'authorities'
    id = Column(Integer, primary_key=True)
    owner = Column(String(128))
    name = Column(String(128), unique=True)
    body = Column(Text())
    chain = Column(Text())
    bits = Column(Integer())
    cn = Column(String(128))
    not_before = Column(DateTime)
    not_after = Column(DateTime)
    active = Column(Boolean, default=True)
    date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
    plugin_name = Column(String(64))
    description = Column(Text)
    options = Column(JSON)
    roles = relationship('Role',
                         backref=db.backref('authority'),
                         lazy='dynamic')
    user_id = Column(Integer, ForeignKey('users.id'))
    certificates = relationship("Certificate", backref='authority')

    def __init__(self,
                 name,
                 owner,
                 plugin_name,
                 body,
                 roles=None,
                 chain=None,
                 description=None):
        self.name = name
        self.body = body
        self.chain = chain
        self.owner = owner
        self.plugin_name = plugin_name
        cert = x509.load_pem_x509_certificate(str(body), default_backend())
        self.cn = get_cn(cert)
        self.not_before = get_not_before(cert)
        self.not_after = get_not_after(cert)
        self.roles = roles
        self.description = description

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def serialize(self):
        blob = self.as_dict()
        return blob
Exemple #6
0
class Log(db.Model):
    __tablename__ = "logs"
    id = Column(Integer, primary_key=True)
    certificate_id = Column(Integer, ForeignKey("certificates.id"))
    log_type = Column(
        Enum(
            "key_view",
            "create_cert",
            "update_cert",
            "revoke_cert",
            "delete_cert",
            name="log_type",
        ),
        nullable=False,
    )
    logged_at = Column(ArrowType(), PassiveDefault(func.now()), nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
Exemple #7
0
class Certificate(db.Model):
    __tablename__ = 'certificates'
    id = Column(Integer, primary_key=True)
    external_id = Column(String(128))
    owner = Column(String(128), nullable=False)
    name = Column(String(256), unique=True)
    description = Column(String(1024))
    notify = Column(Boolean, default=True)

    body = Column(Text(), nullable=False)
    chain = Column(Text())
    private_key = Column(Vault)

    issuer = Column(String(128))
    serial = Column(String(128))
    cn = Column(String(128))
    deleted = Column(Boolean, index=True)

    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))
    bits = Column(Integer())
    san = Column(String(1024))  # TODO this should be migrated to boolean

    rotation = Column(Boolean, default=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
    root_authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
    rotation_policy_id = Column(Integer, ForeignKey('rotation_policies.id'))

    notifications = relationship('Notification', secondary=certificate_notification_associations, backref='certificate')
    destinations = relationship('Destination', secondary=certificate_destination_associations, backref='certificate')
    sources = relationship('Source', secondary=certificate_source_associations, backref='certificate')
    domains = relationship('Domain', secondary=certificate_associations, backref='certificate')
    roles = relationship('Role', secondary=roles_certificates, 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')

    logs = relationship('Log', backref='certificate')
    endpoints = relationship('Endpoint', backref='certificate')
    rotation_policy = relationship("RotationPolicy")

    sensitive_fields = ('private_key',)

    def __init__(self, **kwargs):
        cert = lemur.common.utils.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(defaults.text_to_slug(kwargs['name']), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer, self.not_before, self.not_after, self.san), self.serial)

        self.owner = kwargs['owner']
        self.body = kwargs['body'].strip()

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get('external_id')

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

    @property
    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)

    @property
    def key_type(self):
        cert = lemur.common.utils.parse_certificate(self.body)
        if isinstance(cert.public_key(), rsa.RSAPublicKey):
            return 'RSA{key_size}'.format(key_size=cert.public_key().key_size)

    @property
    def validity_remaining(self):
        return abs(self.not_after - arrow.utcnow())

    @property
    def validity_range(self):
        return self.not_after - self.not_before

    @property
    def subject(self):
        cert = lemur.common.utils.parse_certificate(self.body)
        return cert.subject

    @property
    def public_key(self):
        cert = lemur.common.utils.parse_certificate(self.body)
        return cert.public_key()

    @hybrid_property
    def expired(self):
        if self.not_after <= arrow.utcnow():
            return True

    @expired.expression
    def expired(cls):
        return case(
            [
                (cls.not_after <= arrow.utcnow(), True)
            ],
            else_=False
        )

    @hybrid_property
    def revoked(self):
        if 'revoked' == self.status:
            return True

    @revoked.expression
    def revoked(cls):
        return case(
            [
                (cls.status == 'revoked', True)
            ],
            else_=False
        )

    @hybrid_property
    def in_rotation_window(self):
        """
        Determines if a certificate is available for rotation based
        on the rotation policy associated.
        :return:
        """
        now = arrow.utcnow()
        end = now + timedelta(days=self.rotation_policy.days)

        if self.not_after <= end:
            return True

    @in_rotation_window.expression
    def in_rotation_window(cls):
        """
        Determines if a certificate is available for rotation based
        on the rotation policy associated.
        :return:
        """
        return case(
            [
                (extract('day', cls.not_after - func.now()) <= RotationPolicy.days, True)
            ],
            else_=False
        )

    @property
    def extensions(self):
        # setup default values
        return_extensions = {
            'sub_alt_names': {'names': []}
        }

        try:
            cert = lemur.common.utils.parse_certificate(self.body)
            for extension in cert.extensions:
                value = extension.value
                if isinstance(value, x509.BasicConstraints):
                    return_extensions['basic_constraints'] = value

                elif isinstance(value, x509.SubjectAlternativeName):
                    return_extensions['sub_alt_names']['names'] = value

                elif isinstance(value, x509.ExtendedKeyUsage):
                    return_extensions['extended_key_usage'] = value

                elif isinstance(value, x509.KeyUsage):
                    return_extensions['key_usage'] = value

                elif isinstance(value, x509.SubjectKeyIdentifier):
                    return_extensions['subject_key_identifier'] = {'include_ski': True}

                elif isinstance(value, x509.AuthorityInformationAccess):
                    return_extensions['certificate_info_access'] = {'include_aia': True}

                elif isinstance(value, x509.AuthorityKeyIdentifier):
                    aki = {
                        'use_key_identifier': False,
                        'use_authority_cert': False
                    }

                    if value.key_identifier:
                        aki['use_key_identifier'] = True

                    if value.authority_cert_issuer:
                        aki['use_authority_cert'] = True

                    return_extensions['authority_key_identifier'] = aki

                # TODO: Don't support CRLDistributionPoints yet https://github.com/Netflix/lemur/issues/662
                elif isinstance(value, x509.CRLDistributionPoints):
                    current_app.logger.warning('CRLDistributionPoints not yet supported for clone operation.')

                # TODO: Not supporting custom OIDs yet. https://github.com/Netflix/lemur/issues/665
                else:
                    current_app.logger.warning('Custom OIDs not yet supported for clone operation.')
        except InvalidCodepoint as e:
            sentry.captureException()
            current_app.logger.warning('Unable to parse extensions due to underscore in dns name')
        except ValueError as e:
            sentry.captureException()
            current_app.logger.warning('Unable to parse')
            current_app.logger.exception(e)

        return return_extensions

    def __repr__(self):
        return "Certificate(name={name})".format(name=self.name)
Exemple #8
0
class PendingCertificate(db.Model):
    __tablename__ = 'pending_certs'
    id = Column(Integer, primary_key=True)
    external_id = Column(String(128))
    owner = Column(String(128), nullable=False)
    name = Column(String(256), unique=True)
    description = Column(String(1024))
    notify = Column(Boolean, default=True)
    number_attempts = Column(Integer)
    rename = Column(Boolean, default=True)
    resolved = Column(Boolean, default=False)
    resolved_cert_id = Column(Integer, nullable=True)

    cn = Column(String(128))
    csr = Column(Text(), nullable=False)
    chain = Column(Text())
    private_key = Column(Vault, nullable=True)

    date_created = Column(ArrowType, PassiveDefault(func.now()), nullable=False)
    dns_provider_id = Column(Integer, ForeignKey('dns_providers.id', ondelete="CASCADE"))

    status = Column(Text(), nullable=True)
    last_updated = Column(ArrowType, PassiveDefault(func.now()), onupdate=func.now(), nullable=False)

    rotation = Column(Boolean, default=False)
    user_id = Column(Integer, ForeignKey('users.id'))
    authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
    root_authority_id = Column(Integer, ForeignKey('authorities.id', ondelete="CASCADE"))
    rotation_policy_id = Column(Integer, ForeignKey('rotation_policies.id'))

    notifications = relationship('Notification', secondary=pending_cert_notification_associations,
                                 backref='pending_cert', passive_deletes=True)
    destinations = relationship('Destination', secondary=pending_cert_destination_associations, backref='pending_cert',
                                passive_deletes=True)
    sources = relationship('Source', secondary=pending_cert_source_associations, backref='pending_cert',
                           passive_deletes=True)
    roles = relationship('Role', secondary=pending_cert_role_associations, backref='pending_cert', passive_deletes=True)
    replaces = relationship('Certificate',
                            secondary=pending_cert_replacement_associations,
                            backref='pending_cert',
                            passive_deletes=True)
    options = Column(JSONType)

    rotation_policy = relationship("RotationPolicy")

    sensitive_fields = ('private_key',)

    def __init__(self, **kwargs):
        self.csr = kwargs.get('csr')
        self.private_key = kwargs.get('private_key', "")
        if self.private_key:
            # If the request does not send private key, the key exists but the value is None
            self.private_key = self.private_key.strip()
        self.external_id = kwargs.get('external_id')

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(defaults.text_to_slug(kwargs['name']), 0)
            self.rename = False
        else:
            # TODO: Fix auto-generated name, it should be renamed on creation
            self.name = get_or_increase_name(
                defaults.certificate_name(kwargs['common_name'], kwargs['authority'].name,
                                          dt.now(), dt.now(), False), self.external_id)
            self.rename = True

        self.cn = defaults.common_name(utils.parse_csr(self.csr))
        self.owner = kwargs['owner']
        self.number_attempts = 0

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replaces', [])
        self.rotation = kwargs.get('rotation')
        self.rotation_policy = kwargs.get('rotation_policy')
        try:
            self.dns_provider_id = kwargs.get('dns_provider').id
        except (AttributeError, KeyError, TypeError, Exception):
            pass
Exemple #9
0
    def reflecttable(self, connection, table, include_columns):
        preparer = self.identifier_preparer
        if table.schema is None:
            pragma = "PRAGMA "
        else:
            pragma = "PRAGMA %s." % preparer.quote_identifier(table.schema)
        qtable = preparer.format_table(table, False)

        c = connection.execute("%stable_info(%s)" % (pragma, qtable))
        found_table = False
        while True:
            row = c.fetchone()
            if row is None:
                break

            found_table = True
            (name, type_, nullable, has_default,
             primary_key) = (row[1], row[2].upper(), not row[3], row[4]
                             is not None, row[5])
            name = re.sub(r'^\"|\"$', '', name)
            if include_columns and name not in include_columns:
                continue
            match = re.match(r'(\w+)(\(.*?\))?', type_)
            if match:
                coltype = match.group(1)
                args = match.group(2)
            else:
                coltype = "VARCHAR"
                args = ''

            try:
                coltype = ischema_names[coltype]
            except KeyError:
                util.warn("Did not recognize type '%s' of column '%s'" %
                          (coltype, name))
                coltype = sqltypes.NullType

            if args is not None:
                args = re.findall(r'(\d+)', args)
                coltype = coltype(*[int(a) for a in args])

            colargs = []
            if has_default:
                colargs.append(PassiveDefault('?'))
            table.append_column(
                schema.Column(name,
                              coltype,
                              primary_key=primary_key,
                              nullable=nullable,
                              *colargs))

        if not found_table:
            raise exceptions.NoSuchTableError(table.name)

        c = connection.execute("%sforeign_key_list(%s)" % (pragma, qtable))
        fks = {}
        while True:
            row = c.fetchone()
            if row is None:
                break
            (constraint_name, tablename, localcol,
             remotecol) = (row[0], row[2], row[3], row[4])
            tablename = re.sub(r'^\"|\"$', '', tablename)
            localcol = re.sub(r'^\"|\"$', '', localcol)
            remotecol = re.sub(r'^\"|\"$', '', remotecol)
            try:
                fk = fks[constraint_name]
            except KeyError:
                fk = ([], [])
                fks[constraint_name] = fk

            # look up the table based on the given table's engine, not 'self',
            # since it could be a ProxyEngine
            remotetable = schema.Table(tablename,
                                       table.metadata,
                                       autoload=True,
                                       autoload_with=connection)
            constrained_column = table.c[localcol].name
            refspec = ".".join([tablename, remotecol])
            if constrained_column not in fk[0]:
                fk[0].append(constrained_column)
            if refspec not in fk[1]:
                fk[1].append(refspec)
        for name, value in fks.iteritems():
            table.append_constraint(
                schema.ForeignKeyConstraint(value[0], value[1]))
        # check for UNIQUE indexes
        c = connection.execute("%sindex_list(%s)" % (pragma, qtable))
        unique_indexes = []
        while True:
            row = c.fetchone()
            if row is None:
                break
            if (row[2] == 1):
                unique_indexes.append(row[1])
        # loop thru unique indexes for one that includes the primary key
        for idx in unique_indexes:
            c = connection.execute("%sindex_info(%s)" % (pragma, idx))
            cols = []
            while True:
                row = c.fetchone()
                if row is None:
                    break
                cols.append(row[2])
Exemple #10
0
class Certificate(db.Model):
    __tablename__ = 'certificates'
    id = Column(Integer, primary_key=True)
    owner = Column(String(128), nullable=False)
    name = Column(String(128), unique=True)
    description = Column(String(1024))
    notify = Column(Boolean, default=True)

    body = Column(Text(), nullable=False)
    chain = Column(Text())
    private_key = Column(Vault)

    issuer = Column(String(128))
    serial = Column(String(128))
    cn = Column(String(128))
    deleted = Column(Boolean, index=True)

    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))
    bits = Column(Integer())
    san = Column(String(1024))  # TODO this should be migrated to boolean

    user_id = Column(Integer, ForeignKey('users.id'))
    authority_id = Column(Integer,
                          ForeignKey('authorities.id', ondelete="CASCADE"))
    root_authority_id = Column(
        Integer, ForeignKey('authorities.id', ondelete="CASCADE"))

    notifications = relationship(
        'Notification',
        secondary=certificate_notification_associations,
        backref='certificate')
    destinations = relationship('Destination',
                                secondary=certificate_destination_associations,
                                backref='certificate')
    sources = relationship('Source',
                           secondary=certificate_source_associations,
                           backref='certificate')
    domains = relationship('Domain',
                           secondary=certificate_associations,
                           backref='certificate')
    roles = relationship('Role',
                         secondary=roles_certificates,
                         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')

    logs = relationship('Log', backref='certificate')
    endpoints = relationship('Endpoint', backref='certificate')

    def __init__(self, **kwargs):
        cert = lemur.common.utils.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(kwargs['name'])
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san))

        self.owner = kwargs['owner']
        self.body = kwargs['body'].strip()

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.notify = kwargs.get('notify', True)
        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replacements', [])
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.serial = defaults.serial(cert)

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

    @property
    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)

    @property
    def key_type(self):
        cert = lemur.common.utils.parse_certificate(self.body)
        if isinstance(cert.public_key(), rsa.RSAPublicKey):
            return 'RSA{key_size}'.format(key_size=cert.public_key().key_size)

    @hybrid_property
    def expired(self):
        if self.not_after <= arrow.utcnow():
            return True

    @expired.expression
    def expired(cls):
        return case([(cls.now_after <= arrow.utcnow(), True)], else_=False)

    @hybrid_property
    def revoked(self):
        if 'revoked' == self.status:
            return True

    @revoked.expression
    def revoked(cls):
        return case([(cls.status == 'revoked', True)], else_=False)

    @property
    def extensions(self):
        # TODO pull the OU, O, CN, etc + other extensions.
        names = [{
            'name_type': 'DNSName',
            'value': x.name
        } for x in self.domains]

        extensions = {'sub_alt_names': {'names': names}}

        return extensions

    def get_arn(self, account_number):
        """
        Generate a valid AWS IAM arn

        :rtype : str
        :param account_number:
        :return:
        """
        return "arn:aws:iam::{}:server-certificate/{}".format(
            account_number, self.name)

    def __repr__(self):
        return "Certificate(name={name})".format(name=self.name)
Exemple #11
0
class Certificate(db.Model):
    __tablename__ = 'certificates'
    id = Column(Integer, primary_key=True)
    owner = Column(String(128), nullable=False)
    name = Column(String(128), unique=True)
    description = Column(String(1024))
    active = Column(Boolean, default=True)

    body = Column(Text(), nullable=False)
    chain = Column(Text())
    private_key = Column(Vault)

    issuer = Column(String(128))
    serial = Column(String(128))
    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)

    signing_algorithm = Column(String(128))
    status = Column(String(128))
    bits = Column(Integer())
    san = Column(String(1024))  # TODO this should be migrated to boolean

    user_id = Column(Integer, ForeignKey('users.id'))
    authority_id = Column(Integer,
                          ForeignKey('authorities.id', ondelete="CASCADE"))
    root_authority_id = Column(
        Integer, ForeignKey('authorities.id', ondelete="CASCADE"))

    notifications = relationship(
        "Notification",
        secondary=certificate_notification_associations,
        backref='certificate')
    destinations = relationship("Destination",
                                secondary=certificate_destination_associations,
                                backref='certificate')
    sources = relationship("Source",
                           secondary=certificate_source_associations,
                           backref='certificate')
    domains = relationship("Domain",
                           secondary=certificate_associations,
                           backref="certificate")
    roles = relationship("Role",
                         secondary=roles_certificates,
                         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')

    endpoints = relationship("Endpoint", backref='certificate')

    def __init__(self, **kwargs):
        cert = lemur.common.utils.parse_certificate(kwargs['body'])

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get('name'):
            self.name = get_or_increase_name(kwargs['name'])
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san))

        self.owner = kwargs['owner']
        self.body = kwargs['body'].strip()

        if kwargs.get('private_key'):
            self.private_key = kwargs['private_key'].strip()

        if kwargs.get('chain'):
            self.chain = kwargs['chain'].strip()

        self.destinations = kwargs.get('destinations', [])
        self.notifications = kwargs.get('notifications', [])
        self.description = kwargs.get('description')
        self.roles = list(set(kwargs.get('roles', [])))
        self.replaces = kwargs.get('replacements', [])
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.serial = defaults.serial(cert)

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

    @hybrid_property
    def expired(self):
        if self.not_after <= datetime.datetime.now():
            return True

    @expired.expression
    def expired(cls):
        return case([(cls.now_after <= datetime.datetime.now(), True)],
                    else_=False)

    @hybrid_property
    def revoked(self):
        if 'revoked' == self.status:
            return True

    @revoked.expression
    def revoked(cls):
        return case([(cls.status == 'revoked', True)], else_=False)

    def get_arn(self, account_number):
        """
        Generate a valid AWS IAM arn

        :rtype : str
        :param account_number:
        :return:
        """
        return "arn:aws:iam::{}:server-certificate/{}".format(
            account_number, self.name)
Exemple #12
0
class Certificate(db.Model):
    __tablename__ = "certificates"
    __table_args__ = (
        Index(
            "ix_certificates_cn",
            "cn",
            postgresql_ops={"cn": "gin_trgm_ops"},
            postgresql_using="gin",
        ),
        Index(
            "ix_certificates_name",
            "name",
            postgresql_ops={"name": "gin_trgm_ops"},
            postgresql_using="gin",
        ),
    )
    id = Column(Integer, primary_key=True)
    ix = Index("ix_certificates_id_desc",
               id.desc(),
               postgresql_using="btree",
               unique=True)
    external_id = Column(String(128))
    owner = Column(String(128), nullable=False)
    name = Column(String(256), unique=True)
    description = Column(String(1024))
    notify = Column(Boolean, default=True)

    body = Column(Text(), nullable=False)
    chain = Column(Text())
    csr = Column(Text())
    private_key = Column(Vault)

    issuer = Column(String(128))
    serial = Column(String(128))
    cn = Column(String(128))
    deleted = Column(Boolean, index=True, default=False)
    dns_provider_id = Column(Integer(),
                             ForeignKey("dns_providers.id",
                                        ondelete="CASCADE"),
                             nullable=True)

    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))
    bits = Column(Integer())
    san = Column(String(1024))  # TODO this should be migrated to boolean

    rotation = Column(Boolean, default=False)
    user_id = Column(Integer, ForeignKey("users.id"))
    authority_id = Column(Integer,
                          ForeignKey("authorities.id", ondelete="CASCADE"))
    root_authority_id = Column(
        Integer, ForeignKey("authorities.id", ondelete="CASCADE"))
    rotation_policy_id = Column(Integer, ForeignKey("rotation_policies.id"))

    notifications = relationship(
        "Notification",
        secondary=certificate_notification_associations,
        backref="certificate",
    )
    destinations = relationship(
        "Destination",
        secondary=certificate_destination_associations,
        backref="certificate",
    )
    sources = relationship("Source",
                           secondary=certificate_source_associations,
                           backref="certificate")
    domains = relationship("Domain",
                           secondary=certificate_associations,
                           backref="certificate")
    roles = relationship("Role",
                         secondary=roles_certificates,
                         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",
    )

    replaced_by_pending = relationship(
        "PendingCertificate",
        secondary=pending_cert_replacement_associations,
        backref="pending_replace",
        viewonly=True,
    )

    logs = relationship("Log", backref="certificate")
    endpoints = relationship("Endpoint", backref="certificate")
    rotation_policy = relationship("RotationPolicy")
    sensitive_fields = ("private_key", )

    def __init__(self, **kwargs):
        self.body = kwargs["body"].strip()
        cert = self.parsed_cert

        self.issuer = defaults.issuer(cert)
        self.cn = defaults.common_name(cert)
        self.san = defaults.san(cert)
        self.not_before = defaults.not_before(cert)
        self.not_after = defaults.not_after(cert)
        self.serial = defaults.serial(cert)

        # when destinations are appended they require a valid name.
        if kwargs.get("name"):
            self.name = get_or_increase_name(
                defaults.text_to_slug(kwargs["name"]), self.serial)
        else:
            self.name = get_or_increase_name(
                defaults.certificate_name(self.cn, self.issuer,
                                          self.not_before, self.not_after,
                                          self.san),
                self.serial,
            )

        self.owner = kwargs["owner"]

        if kwargs.get("private_key"):
            self.private_key = kwargs["private_key"].strip()

        if kwargs.get("chain"):
            self.chain = kwargs["chain"].strip()

        if kwargs.get("csr"):
            self.csr = kwargs["csr"].strip()

        self.notify = kwargs.get("notify", True)
        self.destinations = kwargs.get("destinations", [])
        self.notifications = kwargs.get("notifications", [])
        self.description = kwargs.get("description")
        self.roles = list(set(kwargs.get("roles", [])))
        self.replaces = kwargs.get("replaces", [])
        self.rotation = kwargs.get("rotation")
        self.rotation_policy = kwargs.get("rotation_policy")
        self.signing_algorithm = defaults.signing_algorithm(cert)
        self.bits = defaults.bitstrength(cert)
        self.external_id = kwargs.get("external_id")
        self.authority_id = kwargs.get("authority_id")
        self.dns_provider_id = kwargs.get("dns_provider_id")

        for domain in defaults.domains(cert):
            self.domains.append(Domain(name=domain))

        # Check integrity before saving anything into the database.
        # For user-facing API calls, validation should also be done in schema validators.
        self.check_integrity()

    def check_integrity(self):
        """
        Integrity checks: Does the cert have a valid chain and matching private key?
        """
        if self.private_key:
            validators.verify_private_key_match(
                utils.parse_private_key(self.private_key),
                self.parsed_cert,
                error_class=AssertionError,
            )

        if self.chain:
            chain = [self.parsed_cert] + utils.parse_cert_chain(self.chain)
            validators.verify_cert_chain(chain, error_class=AssertionError)

    @cached_property
    def parsed_cert(self):
        assert self.body, "Certificate body not set"
        return utils.parse_certificate(self.body)

    @property
    def active(self):
        return self.notify

    @property
    def organization(self):
        return defaults.organization(self.parsed_cert)

    @property
    def organizational_unit(self):
        return defaults.organizational_unit(self.parsed_cert)

    @property
    def country(self):
        return defaults.country(self.parsed_cert)

    @property
    def state(self):
        return defaults.state(self.parsed_cert)

    @property
    def location(self):
        return defaults.location(self.parsed_cert)

    @property
    def distinguished_name(self):
        return self.parsed_cert.subject.rfc4514_string()

    @property
    def key_type(self):
        if isinstance(self.parsed_cert.public_key(), rsa.RSAPublicKey):
            return "RSA{key_size}".format(
                key_size=self.parsed_cert.public_key().key_size)

    @property
    def validity_remaining(self):
        return abs(self.not_after - arrow.utcnow())

    @property
    def validity_range(self):
        return self.not_after - self.not_before

    @property
    def subject(self):
        return self.parsed_cert.subject

    @property
    def public_key(self):
        return self.parsed_cert.public_key()

    @hybrid_property
    def expired(self):
        if self.not_after <= arrow.utcnow():
            return True

    @expired.expression
    def expired(cls):
        return case([(cls.not_after <= arrow.utcnow(), True)], else_=False)

    @hybrid_property
    def revoked(self):
        if "revoked" == self.status:
            return True

    @revoked.expression
    def revoked(cls):
        return case([(cls.status == "revoked", True)], else_=False)

    @hybrid_property
    def in_rotation_window(self):
        """
        Determines if a certificate is available for rotation based
        on the rotation policy associated.
        :return:
        """
        now = arrow.utcnow()
        end = now + timedelta(days=self.rotation_policy.days)

        if self.not_after <= end:
            return True

    @in_rotation_window.expression
    def in_rotation_window(cls):
        """
        Determines if a certificate is available for rotation based
        on the rotation policy associated.
        :return:
        """
        return case(
            [(extract("day", cls.not_after - func.now()) <=
              RotationPolicy.days, True)],
            else_=False,
        )

    @property
    def extensions(self):
        # setup default values
        return_extensions = {"sub_alt_names": {"names": []}}

        try:
            for extension in self.parsed_cert.extensions:
                value = extension.value
                if isinstance(value, x509.BasicConstraints):
                    return_extensions["basic_constraints"] = value

                elif isinstance(value, x509.SubjectAlternativeName):
                    return_extensions["sub_alt_names"]["names"] = value

                elif isinstance(value, x509.ExtendedKeyUsage):
                    return_extensions["extended_key_usage"] = value

                elif isinstance(value, x509.KeyUsage):
                    return_extensions["key_usage"] = value

                elif isinstance(value, x509.SubjectKeyIdentifier):
                    return_extensions["subject_key_identifier"] = {
                        "include_ski": True
                    }

                elif isinstance(value, x509.AuthorityInformationAccess):
                    return_extensions["certificate_info_access"] = {
                        "include_aia": True
                    }

                elif isinstance(value, x509.AuthorityKeyIdentifier):
                    aki = {
                        "use_key_identifier": False,
                        "use_authority_cert": False
                    }

                    if value.key_identifier:
                        aki["use_key_identifier"] = True

                    if value.authority_cert_issuer:
                        aki["use_authority_cert"] = True

                    return_extensions["authority_key_identifier"] = aki

                elif isinstance(value, x509.CRLDistributionPoints):
                    return_extensions["crl_distribution_points"] = {
                        "include_crl_dp": value
                    }

                # TODO: Not supporting custom OIDs yet. https://github.com/Netflix/lemur/issues/665
                else:
                    current_app.logger.warning(
                        "Custom OIDs not yet supported for clone operation.")
        except InvalidCodepoint as e:
            sentry.captureException()
            current_app.logger.warning(
                "Unable to parse extensions due to underscore in dns name")
        except ValueError as e:
            sentry.captureException()
            current_app.logger.warning("Unable to parse")
            current_app.logger.exception(e)

        return return_extensions

    def __repr__(self):
        return "Certificate(name={name})".format(name=self.name)
Exemple #13
0
class Authority(db.Model):
    __tablename__ = "authorities"
    id = Column(Integer, primary_key=True)
    owner = Column(String(128), nullable=False)
    name = Column(String(128), unique=True)
    body = Column(Text())
    chain = Column(Text())
    active = Column(Boolean, default=True)
    plugin_name = Column(String(64))
    description = Column(Text)
    options = Column(JSON)
    date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
    roles = relationship(
        "Role",
        secondary=roles_authorities,
        passive_deletes=True,
        backref=db.backref("authority"),
        lazy="dynamic",
    )
    user_id = Column(Integer, ForeignKey("users.id"))
    authority_certificate = relationship(
        "Certificate",
        backref="root_authority",
        uselist=False,
        foreign_keys="Certificate.root_authority_id",
    )
    certificates = relationship(
        "Certificate", backref="authority", foreign_keys="Certificate.authority_id"
    )

    authority_pending_certificate = relationship(
        "PendingCertificate",
        backref="root_authority",
        uselist=False,
        foreign_keys="PendingCertificate.root_authority_id",
    )
    pending_certificates = relationship(
        "PendingCertificate",
        backref="authority",
        foreign_keys="PendingCertificate.authority_id",
    )

    def __init__(self, **kwargs):
        self.owner = kwargs["owner"]
        self.roles = kwargs.get("roles", [])
        self.name = kwargs.get("name")
        self.description = kwargs.get("description")
        self.authority_certificate = kwargs["authority_certificate"]
        self.plugin_name = kwargs["plugin"]["slug"]
        self.options = kwargs.get("options")

    @property
    def plugin(self):
        return plugins.get(self.plugin_name)

    @property
    def is_cab_compliant(self):
        """
        Parse the options to find whether authority is CAB Forum Compliant,
        i.e., adhering to the CA/Browser Forum Baseline Requirements.
        Returns None if option is not available
        """
        if not self.options:
            return None

        for option in json.loads(self.options):
            if "name" in option and option["name"] == 'cab_compliant':
                return option["value"]

        return None

    @property
    def max_issuance_days(self):
        if self.is_cab_compliant:
            return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397)

    @property
    def default_validity_days(self):
        if self.is_cab_compliant:
            return current_app.config.get("PUBLIC_CA_MAX_VALIDITY_DAYS", 397)

        return current_app.config.get("DEFAULT_VALIDITY_DAYS", 365)  # 1 year default

    def __repr__(self):
        return "Authority(name={name})".format(name=self.name)
Exemple #14
0
    def reflecttable(self, connection, table):
        c = connection.execute("PRAGMA table_info(" + table.name + ")", {})
        found_table = False
        while True:
            row = c.fetchone()
            if row is None:
                break
            #print "row! " + repr(row)
            found_table = True
            (name, type, nullable, has_default,
             primary_key) = (row[1], row[2].upper(), not row[3], row[4]
                             is not None, row[5])
            name = re.sub(r'^\"|\"$', '', name)
            match = re.match(r'(\w+)(\(.*?\))?', type)
            if match:
                coltype = match.group(1)
                args = match.group(2)
            else:
                coltype = "VARCHAR"
                args = ''

            #print "coltype: " + repr(coltype) + " args: " + repr(args)
            coltype = pragma_names.get(coltype, SLString)
            if args is not None:
                args = re.findall(r'(\d+)', args)
                #print "args! " +repr(args)
                coltype = coltype(*[int(a) for a in args])

            colargs = []
            if has_default:
                colargs.append(PassiveDefault('?'))
            table.append_column(
                schema.Column(name,
                              coltype,
                              primary_key=primary_key,
                              nullable=nullable,
                              *colargs))

        if not found_table:
            raise exceptions.NoSuchTableError(table.name)

        c = connection.execute("PRAGMA foreign_key_list(" + table.name + ")",
                               {})
        fks = {}
        while True:
            row = c.fetchone()
            if row is None:
                break
            (constraint_name, tablename, localcol,
             remotecol) = (row[0], row[2], row[3], row[4])
            tablename = re.sub(r'^\"|\"$', '', tablename)
            localcol = re.sub(r'^\"|\"$', '', localcol)
            remotecol = re.sub(r'^\"|\"$', '', remotecol)
            try:
                fk = fks[constraint_name]
            except KeyError:
                fk = ([], [])
                fks[constraint_name] = fk

            #print "row! " + repr([key for key in row.keys()]), repr(row)
            # look up the table based on the given table's engine, not 'self',
            # since it could be a ProxyEngine
            remotetable = schema.Table(tablename,
                                       table.metadata,
                                       autoload=True,
                                       autoload_with=connection)
            constrained_column = table.c[localcol].name
            refspec = ".".join([tablename, remotecol])
            if constrained_column not in fk[0]:
                fk[0].append(constrained_column)
            if refspec not in fk[1]:
                fk[1].append(refspec)
        for name, value in fks.iteritems():
            table.append_constraint(
                schema.ForeignKeyConstraint(value[0], value[1]))
        # check for UNIQUE indexes
        c = connection.execute("PRAGMA index_list(" + table.name + ")", {})
        unique_indexes = []
        while True:
            row = c.fetchone()
            if row is None:
                break
            if (row[2] == 1):
                unique_indexes.append(row[1])
        # loop thru unique indexes for one that includes the primary key
        for idx in unique_indexes:
            c = connection.execute("PRAGMA index_info(" + idx + ")", {})
            cols = []
            while True:
                row = c.fetchone()
                if row is None:
                    break
                cols.append(row[2])
                col = table.columns[row[2]]
            # unique index that includes the pk is considered a multiple primary key
            for col in cols:
                table.primary_key.add(table.columns[col])