Merge branch 'castrapel-hackday'
This commit is contained in:
commit
38c33395c8
1
Makefile
1
Makefile
|
@ -123,5 +123,4 @@ endif
|
||||||
@echo "--> Done installing new dependencies"
|
@echo "--> Done installing new dependencies"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
||||||
|
|
||||||
.PHONY: develop dev-postgres dev-docs setup-git build clean update-submodules test testloop test-cli test-js test-python lint lint-python lint-js coverage publish release
|
.PHONY: develop dev-postgres dev-docs setup-git build clean update-submodules test testloop test-cli test-js test-python lint lint-python lint-js coverage publish release
|
||||||
|
|
|
@ -29,6 +29,7 @@ from lemur.endpoints.views import mod as endpoints_bp
|
||||||
from lemur.logs.views import mod as logs_bp
|
from lemur.logs.views import mod as logs_bp
|
||||||
from lemur.api_keys.views import mod as api_key_bp
|
from lemur.api_keys.views import mod as api_key_bp
|
||||||
from lemur.pending_certificates.views import mod as pending_certificates_bp
|
from lemur.pending_certificates.views import mod as pending_certificates_bp
|
||||||
|
from lemur.dns_providers.views import mod as dns_providers_bp
|
||||||
|
|
||||||
from lemur.__about__ import (
|
from lemur.__about__ import (
|
||||||
__author__, __copyright__, __email__, __license__, __summary__, __title__,
|
__author__, __copyright__, __email__, __license__, __summary__, __title__,
|
||||||
|
@ -57,6 +58,7 @@ LEMUR_BLUEPRINTS = (
|
||||||
logs_bp,
|
logs_bp,
|
||||||
api_key_bp,
|
api_key_bp,
|
||||||
pending_certificates_bp,
|
pending_certificates_bp,
|
||||||
|
dns_providers_bp,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Authority(db.Model):
|
||||||
self.description = kwargs.get('description')
|
self.description = kwargs.get('description')
|
||||||
self.authority_certificate = kwargs['authority_certificate']
|
self.authority_certificate = kwargs['authority_certificate']
|
||||||
self.plugin_name = kwargs['plugin']['slug']
|
self.plugin_name = kwargs['plugin']['slug']
|
||||||
|
self.options = kwargs.get('options')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def plugin(self):
|
def plugin(self):
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.common.utils import truthiness
|
from lemur.common.utils import truthiness
|
||||||
from lemur.extensions import metrics
|
from lemur.extensions import metrics
|
||||||
|
@ -107,6 +110,8 @@ def create(**kwargs):
|
||||||
|
|
||||||
cert = upload(**kwargs)
|
cert = upload(**kwargs)
|
||||||
kwargs['authority_certificate'] = cert
|
kwargs['authority_certificate'] = cert
|
||||||
|
if kwargs.get('plugin', {}).get('plugin_options', []):
|
||||||
|
kwargs['options'] = json.dumps(kwargs.get('plugin', {}).get('plugin_options', []))
|
||||||
|
|
||||||
authority = Authority(**kwargs)
|
authority = Authority(**kwargs)
|
||||||
authority = database.create(authority)
|
authority = database.create(authority)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.authorizations.models
|
||||||
|
:platform: unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
.. moduleauthor:: Netflix Secops <secops@netflix.com>
|
||||||
|
"""
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
from sqlalchemy_utils import JSONType
|
||||||
|
from lemur.database import db
|
||||||
|
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class Authorizations(db.Model):
|
||||||
|
__tablename__ = 'pending_dns_authorizations'
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
account_number = Column(String(128))
|
||||||
|
domains = Column(JSONType)
|
||||||
|
dns_provider_type = Column(String(128))
|
||||||
|
options = Column(JSONType)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin(self):
|
||||||
|
return plugins.get(self.plugin_name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Authorizations(id={id})".format(label=self.id)
|
||||||
|
|
||||||
|
def __init__(self, account_number, domains, dns_provider_type, options=None):
|
||||||
|
self.account_number = account_number
|
||||||
|
self.domains = domains
|
||||||
|
self.dns_provider_type = dns_provider_type
|
||||||
|
self.options = options
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.pending_certificates.service
|
||||||
|
Copyright (c) 2017 and onwards Instart Logic, Inc. All rights reserved.
|
||||||
|
.. moduleauthor:: Secops <secops@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur import database
|
||||||
|
|
||||||
|
from lemur.authorizations.models import Authorizations
|
||||||
|
|
||||||
|
|
||||||
|
def get(authorization_id):
|
||||||
|
"""
|
||||||
|
Retrieve dns authorization by ID
|
||||||
|
"""
|
||||||
|
return database.get(Authorizations, authorization_id)
|
||||||
|
|
||||||
|
|
||||||
|
def create(account_number, domains, dns_provider_type, options=None):
|
||||||
|
"""
|
||||||
|
Creates a new dns authorization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
authorization = Authorizations(account_number, domains, dns_provider_type, options)
|
||||||
|
return database.create(authorization)
|
|
@ -102,6 +102,7 @@ class Certificate(db.Model):
|
||||||
serial = Column(String(128))
|
serial = Column(String(128))
|
||||||
cn = Column(String(128))
|
cn = Column(String(128))
|
||||||
deleted = Column(Boolean, index=True)
|
deleted = Column(Boolean, index=True)
|
||||||
|
dns_provider_id = Column(Integer(), nullable=True)
|
||||||
|
|
||||||
not_before = Column(ArrowType)
|
not_before = Column(ArrowType)
|
||||||
not_after = Column(ArrowType)
|
not_after = Column(ArrowType)
|
||||||
|
|
|
@ -18,7 +18,8 @@ from lemur.schemas import (
|
||||||
ExtensionSchema,
|
ExtensionSchema,
|
||||||
AssociatedRoleSchema,
|
AssociatedRoleSchema,
|
||||||
EndpointNestedOutputSchema,
|
EndpointNestedOutputSchema,
|
||||||
AssociatedRotationPolicySchema
|
AssociatedRotationPolicySchema,
|
||||||
|
DnsProviderSchema
|
||||||
)
|
)
|
||||||
|
|
||||||
from lemur.authorities.schemas import AuthorityNestedOutputSchema
|
from lemur.authorities.schemas import AuthorityNestedOutputSchema
|
||||||
|
@ -70,6 +71,7 @@ class CertificateInputSchema(CertificateCreationSchema):
|
||||||
replaces = fields.Nested(AssociatedCertificateSchema, missing=[], many=True)
|
replaces = fields.Nested(AssociatedCertificateSchema, missing=[], many=True)
|
||||||
replacements = fields.Nested(AssociatedCertificateSchema, missing=[], many=True) # deprecated
|
replacements = fields.Nested(AssociatedCertificateSchema, missing=[], many=True) # deprecated
|
||||||
roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True)
|
roles = fields.Nested(AssociatedRoleSchema, missing=[], many=True)
|
||||||
|
dns_provider = fields.Nested(DnsProviderSchema, missing={}, required=False, allow_none=True)
|
||||||
|
|
||||||
csr = fields.String(validate=validators.csr)
|
csr = fields.String(validate=validators.csr)
|
||||||
key_type = fields.String(validate=validate.OneOf(['RSA2048', 'RSA4096']), missing='RSA2048')
|
key_type = fields.String(validate=validate.OneOf(['RSA2048', 'RSA4096']), missing='RSA2048')
|
||||||
|
|
|
@ -50,7 +50,8 @@ class LemurDefaults(AuthenticatedResource):
|
||||||
"state": "CA",
|
"state": "CA",
|
||||||
"location": "Los Gatos",
|
"location": "Los Gatos",
|
||||||
"organization": "Netflix",
|
"organization": "Netflix",
|
||||||
"organizationalUnit": "Operations"
|
"organizationalUnit": "Operations",
|
||||||
|
"dnsProviders": [{"name": "test", ...}, {...}],
|
||||||
}
|
}
|
||||||
|
|
||||||
:reqheader Authorization: OAuth token to authenticate
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
|
@ -67,7 +68,7 @@ class LemurDefaults(AuthenticatedResource):
|
||||||
organization=current_app.config.get('LEMUR_DEFAULT_ORGANIZATION'),
|
organization=current_app.config.get('LEMUR_DEFAULT_ORGANIZATION'),
|
||||||
organizational_unit=current_app.config.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT'),
|
organizational_unit=current_app.config.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT'),
|
||||||
issuer_plugin=current_app.config.get('LEMUR_DEFAULT_ISSUER_PLUGIN'),
|
issuer_plugin=current_app.config.get('LEMUR_DEFAULT_ISSUER_PLUGIN'),
|
||||||
authority=default_authority
|
authority=default_authority,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
from sqlalchemy import Column, Integer, String, text
|
||||||
|
from sqlalchemy.dialects.postgresql import JSON
|
||||||
|
from sqlalchemy_utils import ArrowType
|
||||||
|
|
||||||
|
from lemur.database import db
|
||||||
|
|
||||||
|
|
||||||
|
class DnsProviders(db.Model):
|
||||||
|
__tablename__ = 'dns_providers'
|
||||||
|
id = Column(Integer(), primary_key=True)
|
||||||
|
name = Column(String(length=256), unique=True, nullable=True)
|
||||||
|
description = Column(String(length=1024), nullable=True)
|
||||||
|
provider_type = Column(String(length=256), nullable=True)
|
||||||
|
credentials = Column(String(length=256), nullable=True)
|
||||||
|
api_endpoint = Column(String(length=256), nullable=True)
|
||||||
|
date_created = Column(ArrowType(), server_default=text('now()'), nullable=False)
|
||||||
|
status = Column(String(length=128), nullable=True)
|
||||||
|
options = Column(JSON, nullable=True)
|
||||||
|
domains = Column(JSON, nullable=True)
|
|
@ -0,0 +1,18 @@
|
||||||
|
from lemur.common.fields import ArrowDateTime
|
||||||
|
from lemur.common.schema import LemurOutputSchema
|
||||||
|
|
||||||
|
from marshmallow import fields
|
||||||
|
|
||||||
|
|
||||||
|
class DnsProvidersNestedOutputSchema(LemurOutputSchema):
|
||||||
|
__envelope__ = False
|
||||||
|
id = fields.Integer()
|
||||||
|
name = fields.String()
|
||||||
|
provider_type = fields.String()
|
||||||
|
description = fields.String()
|
||||||
|
credentials = fields.String()
|
||||||
|
api_endpoint = fields.String()
|
||||||
|
date_created = ArrowDateTime()
|
||||||
|
|
||||||
|
|
||||||
|
dns_provider_schema = DnsProvidersNestedOutputSchema()
|
|
@ -0,0 +1,33 @@
|
||||||
|
from lemur import database
|
||||||
|
from lemur.dns_providers.models import DnsProviders
|
||||||
|
|
||||||
|
|
||||||
|
def render(args):
|
||||||
|
"""
|
||||||
|
Helper that helps us render the REST Api responses.
|
||||||
|
:param args:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = database.session_query(DnsProviders)
|
||||||
|
|
||||||
|
return database.sort_and_page(query, DnsProviders, args)
|
||||||
|
|
||||||
|
|
||||||
|
def get(dns_provider_id):
|
||||||
|
"""
|
||||||
|
Retrieves a dns provider by its lemur assigned ID.
|
||||||
|
|
||||||
|
:param dns_provider_id: Lemur assigned ID
|
||||||
|
:rtype : DnsProvider
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return database.get(DnsProviders, dns_provider_id)
|
||||||
|
|
||||||
|
|
||||||
|
def delete(dns_provider_id):
|
||||||
|
"""
|
||||||
|
Deletes a DNS provider.
|
||||||
|
|
||||||
|
:param dns_provider_id: Lemur assigned ID
|
||||||
|
"""
|
||||||
|
database.delete(get(dns_provider_id))
|
|
@ -0,0 +1,87 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.dns)providers.views
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
.. moduleauthor:: Curtis Castrapel <ccastrapel@netflix.com>
|
||||||
|
"""
|
||||||
|
from flask import Blueprint, g
|
||||||
|
from flask_restful import reqparse, Api
|
||||||
|
|
||||||
|
from lemur.auth.permissions import admin_permission
|
||||||
|
from lemur.auth.service import AuthenticatedResource
|
||||||
|
from lemur.common.schema import validate_schema
|
||||||
|
from lemur.common.utils import paginated_parser
|
||||||
|
from lemur.dns_providers import service
|
||||||
|
from lemur.dns_providers.schemas import dns_provider_schema
|
||||||
|
|
||||||
|
mod = Blueprint('dns_providers', __name__)
|
||||||
|
api = Api(mod)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsProvidersList(AuthenticatedResource):
|
||||||
|
""" Defines the 'dns_providers' endpoint """
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(DnsProvidersList, self).__init__()
|
||||||
|
|
||||||
|
@validate_schema(None, dns_provider_schema)
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
.. http:get:: /dns_providers
|
||||||
|
|
||||||
|
The current list of DNS Providers
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /dns_providers 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
|
||||||
|
|
||||||
|
{
|
||||||
|
"items": [{
|
||||||
|
"id": 1,
|
||||||
|
"name": "test",
|
||||||
|
"description": "test",
|
||||||
|
"provider_type": "dyn",
|
||||||
|
"status": "active",
|
||||||
|
}],
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
:query sortBy: field to sort on
|
||||||
|
:query sortDir: asc or desc
|
||||||
|
:query page: int. default is 1
|
||||||
|
:query filter: key value pair format is k;v
|
||||||
|
:query count: count number. default is 10
|
||||||
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 403: unauthenticated
|
||||||
|
|
||||||
|
"""
|
||||||
|
parser = paginated_parser.copy()
|
||||||
|
parser.add_argument('id', type=int, location='args')
|
||||||
|
parser.add_argument('name', type=str, location='args')
|
||||||
|
parser.add_argument('type', type=str, location='args')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
args['user'] = g.user
|
||||||
|
return service.render(args)
|
||||||
|
|
||||||
|
@admin_permission.require(http_exception=403)
|
||||||
|
def delete(self, dns_provider_id):
|
||||||
|
service.delete(dns_provider_id)
|
||||||
|
return {'result': True}
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(DnsProvidersList, '/dns_providers', endpoint='dns_providers')
|
|
@ -0,0 +1,39 @@
|
||||||
|
"""Create dns_providers table
|
||||||
|
|
||||||
|
Revision ID: 3adfdd6598df
|
||||||
|
Revises: 556ceb3e3c3e
|
||||||
|
Create Date: 2018-04-10 13:25:47.007556
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '3adfdd6598df'
|
||||||
|
down_revision = '556ceb3e3c3e'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects.postgresql import JSON
|
||||||
|
|
||||||
|
from sqlalchemy_utils import ArrowType
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# create provider table
|
||||||
|
op.create_table(
|
||||||
|
'dns_providers',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=256), nullable=True),
|
||||||
|
sa.Column('description', sa.String(length=1024), nullable=True),
|
||||||
|
sa.Column('provider_type', sa.String(length=256), nullable=True),
|
||||||
|
sa.Column('credentials', sa.String(length=256), nullable=True),
|
||||||
|
sa.Column('api_endpoint', sa.String(length=256), nullable=True),
|
||||||
|
sa.Column('date_created', ArrowType(), server_default=sa.text('now()'), nullable=False),
|
||||||
|
sa.Column('status', sa.String(length=128), nullable=True),
|
||||||
|
sa.Column('options', JSON),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table('dns_providers')
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""Add dns_provider id column to certificates table
|
||||||
|
|
||||||
|
Revision ID: 4e78b9e4e1dd
|
||||||
|
Revises: 3adfdd6598df
|
||||||
|
Create Date: 2018-04-10 14:00:30.701669
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '4e78b9e4e1dd'
|
||||||
|
down_revision = '3adfdd6598df'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('certificates', sa.Column('dns_provider_id', sa.Integer(), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('certificates', 'dns_provider_id')
|
|
@ -29,7 +29,7 @@ def fetch(ids):
|
||||||
|
|
||||||
for cert in pending_certs:
|
for cert in pending_certs:
|
||||||
authority = plugins.get(cert.authority.plugin_name)
|
authority = plugins.get(cert.authority.plugin_name)
|
||||||
real_cert = authority.get_ordered_certificate(cert.external_id)
|
real_cert = authority.get_ordered_certificate(cert)
|
||||||
if real_cert:
|
if real_cert:
|
||||||
# If a real certificate was returned from issuer, then create it in Lemur and delete
|
# If a real certificate was returned from issuer, then create it in Lemur and delete
|
||||||
# the pending certificate
|
# the pending certificate
|
||||||
|
|
|
@ -8,6 +8,7 @@ from datetime import datetime as dt
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy import Integer, ForeignKey, String, PassiveDefault, func, Column, Text, Boolean
|
from sqlalchemy import Integer, ForeignKey, String, PassiveDefault, func, Column, Text, Boolean
|
||||||
from sqlalchemy_utils.types.arrow import ArrowType
|
from sqlalchemy_utils.types.arrow import ArrowType
|
||||||
|
from sqlalchemy_utils import JSONType
|
||||||
|
|
||||||
import lemur.common.utils
|
import lemur.common.utils
|
||||||
from lemur.certificates.models import get_or_increase_name
|
from lemur.certificates.models import get_or_increase_name
|
||||||
|
@ -37,6 +38,7 @@ class PendingCertificate(db.Model):
|
||||||
private_key = Column(Vault, nullable=True)
|
private_key = Column(Vault, nullable=True)
|
||||||
|
|
||||||
date_created = Column(ArrowType, PassiveDefault(func.now()), nullable=False)
|
date_created = Column(ArrowType, PassiveDefault(func.now()), nullable=False)
|
||||||
|
dns_provider_id = Column(Integer(), nullable=True)
|
||||||
|
|
||||||
status = Column(String(128))
|
status = Column(String(128))
|
||||||
|
|
||||||
|
@ -54,6 +56,7 @@ class PendingCertificate(db.Model):
|
||||||
secondary=pending_cert_replacement_associations,
|
secondary=pending_cert_replacement_associations,
|
||||||
backref='pending_cert',
|
backref='pending_cert',
|
||||||
passive_deletes=True)
|
passive_deletes=True)
|
||||||
|
options = Column(JSONType)
|
||||||
|
|
||||||
rotation_policy = relationship("RotationPolicy")
|
rotation_policy = relationship("RotationPolicy")
|
||||||
|
|
||||||
|
@ -93,3 +96,7 @@ class PendingCertificate(db.Model):
|
||||||
self.replaces = kwargs.get('replaces', [])
|
self.replaces = kwargs.get('replaces', [])
|
||||||
self.rotation = kwargs.get('rotation')
|
self.rotation = kwargs.get('rotation')
|
||||||
self.rotation_policy = kwargs.get('rotation_policy')
|
self.rotation_policy = kwargs.get('rotation_policy')
|
||||||
|
try:
|
||||||
|
self.dns_provider_id = kwargs.get('dns_provider', {}).get("id")
|
||||||
|
except AttributeError:
|
||||||
|
self.dns_provider_id = None
|
||||||
|
|
|
@ -25,7 +25,7 @@ class IssuerPlugin(Plugin):
|
||||||
def revoke_certificate(self, certificate, comments):
|
def revoke_certificate(self, certificate, comments):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_ordered_certificate(self, order_id):
|
def get_ordered_certificate(self, certificate):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def cancel_ordered_certificate(self, pending_cert, **kwargs):
|
def cancel_ordered_certificate(self, pending_cert, **kwargs):
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
.. moduleauthor:: Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>
|
.. moduleauthor:: Mikhail Khodorovskiy <mikhail.khodorovskiy@jivesoftware.com>
|
||||||
|
.. moduleauthor:: Curtis Castrapel <ccastrapel@netflix.com>
|
||||||
"""
|
"""
|
||||||
import josepy as jose
|
import josepy as jose
|
||||||
|
import json
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
|
@ -22,7 +24,8 @@ from lemur.common.utils import generate_private_key
|
||||||
|
|
||||||
import OpenSSL.crypto
|
import OpenSSL.crypto
|
||||||
|
|
||||||
from lemur.common.utils import validate_conf
|
from lemur.authorizations import service as authorization_service
|
||||||
|
from lemur.dns_providers import service as dns_provider_service
|
||||||
from lemur.plugins.bases import IssuerPlugin
|
from lemur.plugins.bases import IssuerPlugin
|
||||||
from lemur.plugins import lemur_acme as acme
|
from lemur.plugins import lemur_acme as acme
|
||||||
|
|
||||||
|
@ -96,19 +99,26 @@ def request_certificate(acme_client, authorizations, csr):
|
||||||
OpenSSL.crypto.FILETYPE_PEM, cert_response.body
|
OpenSSL.crypto.FILETYPE_PEM, cert_response.body
|
||||||
).decode('utf-8')
|
).decode('utf-8')
|
||||||
|
|
||||||
pem_certificate_chain = "\n".join(
|
full_chain = []
|
||||||
OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert.decode("utf-8"))
|
for cert in acme_client.fetch_chain(cert_response):
|
||||||
for cert in acme_client.fetch_chain(cert_response)
|
chain = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||||
).decode('utf-8')
|
full_chain.append(chain.decode("utf-8"))
|
||||||
|
pem_certificate_chain = "\n".join(full_chain)
|
||||||
|
|
||||||
current_app.logger.debug("{0} {1}".format(type(pem_certificate). type(pem_certificate_chain)))
|
current_app.logger.debug("{0} {1}".format(type(pem_certificate), type(pem_certificate_chain)))
|
||||||
return pem_certificate, pem_certificate_chain
|
return pem_certificate, pem_certificate_chain
|
||||||
|
|
||||||
|
|
||||||
def setup_acme_client():
|
def setup_acme_client(authority):
|
||||||
email = current_app.config.get('ACME_EMAIL')
|
if not authority.options:
|
||||||
tel = current_app.config.get('ACME_TEL')
|
raise Exception("Invalid authority. Options not set")
|
||||||
directory_url = current_app.config.get('ACME_DIRECTORY_URL')
|
options = {}
|
||||||
|
for o in json.loads(authority.options):
|
||||||
|
print(o)
|
||||||
|
options[o.get("name")] = o.get("value")
|
||||||
|
email = options.get('email', current_app.config.get('ACME_EMAIL'))
|
||||||
|
tel = options.get('telephone', current_app.config.get('ACME_TEL'))
|
||||||
|
directory_url = options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))
|
||||||
contact = ('mailto:{}'.format(email), 'tel:{}'.format(tel))
|
contact = ('mailto:{}'.format(email), 'tel:{}'.format(tel))
|
||||||
|
|
||||||
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
|
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
|
||||||
|
@ -145,11 +155,14 @@ def get_domains(options):
|
||||||
|
|
||||||
def get_authorizations(acme_client, account_number, domains, dns_provider):
|
def get_authorizations(acme_client, account_number, domains, dns_provider):
|
||||||
authorizations = []
|
authorizations = []
|
||||||
try:
|
for domain in domains:
|
||||||
for domain in domains:
|
authz_record = start_dns_challenge(acme_client, account_number, domain, dns_provider)
|
||||||
authz_record = start_dns_challenge(acme_client, account_number, domain, dns_provider)
|
authorizations.append(authz_record)
|
||||||
authorizations.append(authz_record)
|
return authorizations
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_authorizations(acme_client, account_number, dns_provider, authorizations):
|
||||||
|
try:
|
||||||
for authz_record in authorizations:
|
for authz_record in authorizations:
|
||||||
complete_dns_challenge(acme_client, account_number, authz_record, dns_provider)
|
complete_dns_challenge(acme_client, account_number, authz_record, dns_provider)
|
||||||
finally:
|
finally:
|
||||||
|
@ -171,24 +184,59 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||||
description = 'Enables the creation of certificates via ACME CAs (including Let\'s Encrypt)'
|
description = 'Enables the creation of certificates via ACME CAs (including Let\'s Encrypt)'
|
||||||
version = acme.VERSION
|
version = acme.VERSION
|
||||||
|
|
||||||
author = 'Kevin Glisson'
|
author = 'Netflix'
|
||||||
author_url = 'https://github.com/netflix/lemur.git'
|
author_url = 'https://github.com/netflix/lemur.git'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
options = [
|
||||||
required_vars = [
|
{
|
||||||
'ACME_DIRECTORY_URL',
|
'name': 'acme_url',
|
||||||
'ACME_TEL',
|
'type': 'str',
|
||||||
'ACME_EMAIL',
|
'required': True,
|
||||||
'ACME_AWS_ACCOUNT_NUMBER',
|
'validation': '/^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+$/',
|
||||||
'ACME_ROOT'
|
'helpMessage': 'Must be a valid web url starting with http[s]://',
|
||||||
]
|
},
|
||||||
|
{
|
||||||
|
'name': 'telephone',
|
||||||
|
'type': 'str',
|
||||||
|
'default': '',
|
||||||
|
'helpMessage': 'Telephone to use'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'email',
|
||||||
|
'type': 'str',
|
||||||
|
'default': '',
|
||||||
|
'validation': '/^?([-a-zA-Z0-9.`?{}]+@\w+\.\w+)$/',
|
||||||
|
'helpMessage': 'Email to use'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'certificate',
|
||||||
|
'type': 'textarea',
|
||||||
|
'default': '',
|
||||||
|
'validation': '/^-----BEGIN CERTIFICATE-----/',
|
||||||
|
'helpMessage': 'Certificate to use'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
validate_conf(current_app, required_vars)
|
def __init__(self, *args, **kwargs):
|
||||||
self.dns_provider_name = current_app.config.get('ACME_DNS_PROVIDER', 'route53')
|
|
||||||
current_app.logger.debug("Using DNS provider: {0}".format(self.dns_provider_name))
|
|
||||||
self.dns_provider = __import__(self.dns_provider_name, globals(), locals(), [], 1)
|
|
||||||
super(ACMEIssuerPlugin, self).__init__(*args, **kwargs)
|
super(ACMEIssuerPlugin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_ordered_certificate(self, pending_cert):
|
||||||
|
acme_client, registration = setup_acme_client(pending_cert.authority)
|
||||||
|
order_info = authorization_service.get(pending_cert.external_id)
|
||||||
|
dns_provider = dns_provider_service.get(pending_cert.dns_provider_id)
|
||||||
|
dns_provider_type = __import__(dns_provider.provider_type, globals(), locals(), [], 1)
|
||||||
|
authorizations = get_authorizations(
|
||||||
|
acme_client, order_info.account_number, order_info.domains, dns_provider_type)
|
||||||
|
|
||||||
|
finalize_authorizations(acme_client, order_info.account_number, dns_provider_type, authorizations)
|
||||||
|
pem_certificate, pem_certificate_chain = request_certificate(acme_client, authorizations, pending_cert.csr)
|
||||||
|
cert = {
|
||||||
|
'body': "\n".join(str(pem_certificate).splitlines()),
|
||||||
|
'chain': "\n".join(str(pem_certificate_chain).splitlines()),
|
||||||
|
'external_id': str(pending_cert.external_id)
|
||||||
|
}
|
||||||
|
return cert
|
||||||
|
|
||||||
def create_certificate(self, csr, issuer_options):
|
def create_certificate(self, csr, issuer_options):
|
||||||
"""
|
"""
|
||||||
Creates an ACME certificate.
|
Creates an ACME certificate.
|
||||||
|
@ -197,11 +245,38 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||||
:param issuer_options:
|
:param issuer_options:
|
||||||
:return: :raise Exception:
|
:return: :raise Exception:
|
||||||
"""
|
"""
|
||||||
current_app.logger.debug("Requesting a new acme certificate: {0}".format(issuer_options))
|
authority = issuer_options.get('authority')
|
||||||
acme_client, registration = setup_acme_client()
|
create_immediately = issuer_options.get('create_immediately', False)
|
||||||
account_number = current_app.config.get('ACME_AWS_ACCOUNT_NUMBER')
|
acme_client, registration = setup_acme_client(authority)
|
||||||
|
dns_provider_d = issuer_options.get('dns_provider')
|
||||||
|
if not dns_provider_d:
|
||||||
|
raise Exception("DNS Provider setting is required for ACME certificates.")
|
||||||
|
dns_provider = dns_provider_service.get(dns_provider_d.get("id"))
|
||||||
|
credentials = json.loads(dns_provider.credentials)
|
||||||
|
|
||||||
|
current_app.logger.debug("Using DNS provider: {0}".format(dns_provider.provider_type))
|
||||||
|
dns_provider_type = __import__(dns_provider.provider_type, globals(), locals(), [], 1)
|
||||||
|
account_number = credentials.get("account_number")
|
||||||
|
if dns_provider.provider_type == 'route53' and not account_number:
|
||||||
|
error = "DNS Provider {} does not have an account number configured.".format(dns_provider.name)
|
||||||
|
current_app.logger.error(error)
|
||||||
|
raise Exception(error)
|
||||||
domains = get_domains(issuer_options)
|
domains = get_domains(issuer_options)
|
||||||
authorizations = get_authorizations(acme_client, account_number, domains, self.dns_provider)
|
if not create_immediately:
|
||||||
|
# Create pending authorizations that we'll need to do the creation
|
||||||
|
authz_domains = []
|
||||||
|
for d in domains:
|
||||||
|
if type(d) == str:
|
||||||
|
authz_domains.append(d)
|
||||||
|
else:
|
||||||
|
authz_domains.append(d.value)
|
||||||
|
|
||||||
|
dns_authorization = authorization_service.create(account_number, authz_domains, dns_provider.provider_type)
|
||||||
|
# Return id of the DNS Authorization
|
||||||
|
return None, None, dns_authorization.id
|
||||||
|
|
||||||
|
authorizations = get_authorizations(acme_client, account_number, domains, dns_provider_type)
|
||||||
|
finalize_authorizations(acme_client, account_number, dns_provider_type, authorizations)
|
||||||
pem_certificate, pem_certificate_chain = request_certificate(acme_client, authorizations, csr)
|
pem_certificate, pem_certificate_chain = request_certificate(acme_client, authorizations, csr)
|
||||||
# TODO add external ID (if possible)
|
# TODO add external ID (if possible)
|
||||||
return pem_certificate, pem_certificate_chain, None
|
return pem_certificate, pem_certificate_chain, None
|
||||||
|
@ -216,4 +291,11 @@ class ACMEIssuerPlugin(IssuerPlugin):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
role = {'username': '', 'password': '', 'name': 'acme'}
|
role = {'username': '', 'password': '', 'name': 'acme'}
|
||||||
return current_app.config.get('ACME_ROOT'), "", [role]
|
plugin_options = options.get('plugin').get('plugin_options')
|
||||||
|
# Define static acme_root based off configuration variable by default. However, if user has passed a
|
||||||
|
# certificate, use this certificate as the root.
|
||||||
|
acme_root = current_app.config.get('ACME_ROOT')
|
||||||
|
for option in plugin_options:
|
||||||
|
if option.get('name') == 'certificate':
|
||||||
|
acme_root = option.get('value')
|
||||||
|
return acme_root, "", [role]
|
||||||
|
|
|
@ -58,7 +58,7 @@ def change_txt_record(action, zone_id, domain, value, client=None):
|
||||||
def create_txt_record(host, value, account_number):
|
def create_txt_record(host, value, account_number):
|
||||||
zone_id = find_zone_id(host, account_number=account_number)
|
zone_id = find_zone_id(host, account_number=account_number)
|
||||||
change_id = change_txt_record(
|
change_id = change_txt_record(
|
||||||
"CREATE",
|
"UPSERT",
|
||||||
zone_id,
|
zone_id,
|
||||||
host,
|
host,
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -325,8 +325,9 @@ class DigiCertIssuerPlugin(IssuerPlugin):
|
||||||
response = self.session.put(create_url, data=json.dumps({'comments': comments}))
|
response = self.session.put(create_url, data=json.dumps({'comments': comments}))
|
||||||
return handle_response(response)
|
return handle_response(response)
|
||||||
|
|
||||||
def get_ordered_certificate(self, order_id):
|
def get_ordered_certificate(self, pending_cert):
|
||||||
""" Retrieve a certificate via order id """
|
""" Retrieve a certificate via order id """
|
||||||
|
order_id = pending_cert.external_id
|
||||||
base_url = current_app.config.get('DIGICERT_URL')
|
base_url = current_app.config.get('DIGICERT_URL')
|
||||||
try:
|
try:
|
||||||
certificate_id = get_certificate_id(self.session, base_url, order_id)
|
certificate_id = get_certificate_id(self.session, base_url, order_id)
|
||||||
|
|
|
@ -150,11 +150,7 @@ def test_signature_hash(app):
|
||||||
signature_hash('sdfdsf')
|
signature_hash('sdfdsf')
|
||||||
|
|
||||||
|
|
||||||
def test_issuer_plugin_create_certificate():
|
def test_issuer_plugin_create_certificate(certificate_="""\
|
||||||
import requests_mock
|
|
||||||
from lemur.plugins.lemur_digicert.plugin import DigiCertIssuerPlugin
|
|
||||||
|
|
||||||
pem_fixture = """\
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
abc
|
abc
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
@ -164,7 +160,11 @@ def
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
ghi
|
ghi
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
"""
|
"""):
|
||||||
|
import requests_mock
|
||||||
|
from lemur.plugins.lemur_digicert.plugin import DigiCertIssuerPlugin
|
||||||
|
|
||||||
|
pem_fixture = certificate_
|
||||||
|
|
||||||
subject = DigiCertIssuerPlugin()
|
subject = DigiCertIssuerPlugin()
|
||||||
adapter = requests_mock.Adapter()
|
adapter = requests_mock.Adapter()
|
||||||
|
|
|
@ -21,6 +21,7 @@ from lemur.plugins.utils import get_plugin_option
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
from lemur.users.models import User
|
from lemur.users.models import User
|
||||||
from lemur.authorities.models import Authority
|
from lemur.authorities.models import Authority
|
||||||
|
from lemur.dns_providers.models import DnsProviders
|
||||||
from lemur.policies.models import RotationPolicy
|
from lemur.policies.models import RotationPolicy
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate
|
||||||
from lemur.destinations.models import Destination
|
from lemur.destinations.models import Destination
|
||||||
|
@ -159,6 +160,11 @@ class AssociatedRotationPolicySchema(LemurInputSchema):
|
||||||
return fetch_objects(RotationPolicy, data, many=many)
|
return fetch_objects(RotationPolicy, data, many=many)
|
||||||
|
|
||||||
|
|
||||||
|
class DnsProviderSchema(LemurInputSchema):
|
||||||
|
id = fields.Integer()
|
||||||
|
name = fields.String()
|
||||||
|
|
||||||
|
|
||||||
class PluginInputSchema(LemurInputSchema):
|
class PluginInputSchema(LemurInputSchema):
|
||||||
plugin_options = fields.List(fields.Dict(), validate=validate_options)
|
plugin_options = fields.List(fields.Dict(), validate=validate_options)
|
||||||
slug = fields.String(required=True)
|
slug = fields.String(required=True)
|
||||||
|
|
|
@ -109,6 +109,15 @@
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
lemur.service('DnsProviders', function (LemurRestangular) {
|
||||||
|
var DnsProviders = this;
|
||||||
|
DnsProviders.get = function () {
|
||||||
|
return LemurRestangular.all('dns_providers').customGET().then(function (dnsProviders) {
|
||||||
|
return dnsProviders;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
lemur.directive('lemurBadRequest', [function () {
|
lemur.directive('lemurBadRequest', [function () {
|
||||||
return {
|
return {
|
||||||
template: '<h4>{{ directiveData.message }}</h4>' +
|
template: '<h4>{{ directiveData.message }}</h4>' +
|
||||||
|
|
|
@ -52,8 +52,58 @@
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Plugin
|
Plugin
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="form-group col-sm-10">
|
||||||
<select class="form-control" ng-model="authority.plugin" ng-options="plugin as plugin.title for plugin in plugins" required></select>
|
<select class="form-control" ng-model="authority.plugin" ng-options="plugin as plugin.title for plugin in plugins" required></select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" ng-repeat="item in authority.plugin.pluginOptions">
|
||||||
|
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
{{ item.name | titleCase }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
|
class="form-control" ng-model="item.value"/>
|
||||||
|
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available"
|
||||||
|
ng-model="item.value"></select>
|
||||||
|
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox" ng-model="item.value">
|
||||||
|
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control" ng-model="item.value"/>
|
||||||
|
<textarea name="sub" ng-if="item.type == 'textarea'" class="form-control" ng-model="item.value"></textarea>
|
||||||
|
<div ng-if="item.type == 'export-plugin'">
|
||||||
|
<form name="exportForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<select class="form-control" ng-model="item.value"
|
||||||
|
ng-options="plugin.title for plugin in exportPlugins" required></select>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-group" ng-repeat="item in item.value.pluginOptions">
|
||||||
|
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
{{ item.name | titleCase }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
|
class="form-control" ng-model="item.value"/>
|
||||||
|
<select name="sub" ng-if="item.type == 'select'" class="form-control"
|
||||||
|
ng-options="i for i in item.available" ng-model="item.value"></select>
|
||||||
|
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox"
|
||||||
|
ng-model="item.value">
|
||||||
|
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control"
|
||||||
|
ng-model="item.value" ng-pattern="item.validation"/>
|
||||||
|
<textarea name="sub" ng-if="item.type == 'textarea'" class="form-control"
|
||||||
|
ng-model="item.value" ng-pattern="item.validation"></textarea>
|
||||||
|
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine"
|
||||||
|
class="help-block">{{ item.helpMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine" class="help-block">{{ item.helpMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -134,6 +134,11 @@ angular.module('lemur')
|
||||||
$scope.certificate.validityYears = null;
|
$scope.certificate.validityYears = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CertificateService.getDnsProviders().then(function (providers) {
|
||||||
|
$scope.dnsProviders = providers;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$scope.create = function (certificate) {
|
$scope.create = function (certificate) {
|
||||||
WizardHandler.wizard().context.loading = true;
|
WizardHandler.wizard().context.loading = true;
|
||||||
CertificateService.create(certificate).then(
|
CertificateService.create(certificate).then(
|
||||||
|
|
|
@ -234,6 +234,9 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
</div></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -107,6 +107,17 @@
|
||||||
</ui-select>
|
</ui-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" ng-show="certificate.authority.plugin.slug == 'acme-issuer'">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
DNS Provider:
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select class="form-control" ng-model="certificate.dnsProvider" ng-options="item as item.name for item in dnsProviders.items track by item.id">
|
||||||
|
<option value="">- choose an entry. Neded for ACME Providers -</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2"
|
<label class="control-label col-sm-2"
|
||||||
uib-tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
uib-tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
||||||
|
|
|
@ -149,7 +149,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
return LemurRestangular.all('certificates');
|
return LemurRestangular.all('certificates');
|
||||||
})
|
})
|
||||||
.service('CertificateService', function ($location, CertificateApi, AuthorityService, AuthorityApi, LemurRestangular, DefaultService) {
|
.service('CertificateService', function ($location, CertificateApi, AuthorityService, AuthorityApi, LemurRestangular, DefaultService, DnsProviders) {
|
||||||
var CertificateService = this;
|
var CertificateService = this;
|
||||||
CertificateService.findCertificatesByName = function (filterValue) {
|
CertificateService.findCertificatesByName = function (filterValue) {
|
||||||
return CertificateApi.getList({'filter[name]': filterValue})
|
return CertificateApi.getList({'filter[name]': filterValue})
|
||||||
|
@ -246,6 +246,10 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CertificateService.getDnsProviders = function () {
|
||||||
|
return DnsProviders.get();
|
||||||
|
};
|
||||||
|
|
||||||
CertificateService.loadPrivateKey = function (certificate) {
|
CertificateService.loadPrivateKey = function (certificate) {
|
||||||
return certificate.customGET('key');
|
return certificate.customGET('key');
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('lemur')
|
||||||
|
|
||||||
|
.controller('DnsProviderCreateController', function ($scope, $uibModalInstance, PluginService, DnsProviderService, LemurRestangular, toaster) {
|
||||||
|
$scope.dns_provider = LemurRestangular.restangularizeElement(null, {}, 'dns_providers');
|
||||||
|
|
||||||
|
PluginService.getByType('dns_provider').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
|
PluginService.getByType('export').then(function (plugins) {
|
||||||
|
$scope.exportPlugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.save = function (dns_provider) {
|
||||||
|
DnsProviderService.create(dns_provider.then(
|
||||||
|
function () {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'success',
|
||||||
|
title: dns_provider.label,
|
||||||
|
body: 'Successfully Created!'
|
||||||
|
});
|
||||||
|
$uibModalInstance.close();
|
||||||
|
}, function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: dns_provider.label,
|
||||||
|
body: 'lemur-bad-request',
|
||||||
|
bodyOutputType: 'directive',
|
||||||
|
directiveData: response.data,
|
||||||
|
timeout: 100000
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('DnsProviderEditController', function ($scope, $uibModalInstance, DnsProviderService, DnsProviderApi, PluginService, toaster, editId) {
|
||||||
|
|
||||||
|
|
||||||
|
DnsProviderApi.get(editId).then(function (dns_provider) {
|
||||||
|
$scope.dns_provider = dns_provider;
|
||||||
|
|
||||||
|
PluginService.getByType('dns_provider').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
|
||||||
|
_.each($scope.plugins, function (plugin) {
|
||||||
|
if (plugin.slug === $scope.dns_provider.plugin.slug) {
|
||||||
|
plugin.pluginOptions = $scope.dns_provider.plugin.pluginOptions;
|
||||||
|
$scope.dns_provider.plugin = plugin;
|
||||||
|
_.each($scope.dns_provider.plugin.pluginOptions, function (option) {
|
||||||
|
if (option.type === 'export-plugin') {
|
||||||
|
PluginService.getByType('export').then(function (plugins) {
|
||||||
|
$scope.exportPlugins = plugins;
|
||||||
|
|
||||||
|
_.each($scope.exportPlugins, function (plugin) {
|
||||||
|
if (plugin.slug === option.value.slug) {
|
||||||
|
plugin.pluginOptions = option.value.pluginOptions;
|
||||||
|
option.value = plugin;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.save = function (dns_provider) {
|
||||||
|
DnsProviderService.update(dns_provider).then(
|
||||||
|
function () {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'success',
|
||||||
|
title: dns_provider.label,
|
||||||
|
body: 'Successfully Updated!'
|
||||||
|
});
|
||||||
|
$uibModalInstance.close();
|
||||||
|
}, function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: dns_provider.label,
|
||||||
|
body: 'lemur-bad-request',
|
||||||
|
bodyOutputType: 'directive',
|
||||||
|
directiveData: response.data,
|
||||||
|
timeout: 100000
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$uibModalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,93 @@
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h3><span ng-show="!dns_provider.fromServer">Create</span><span ng-show="dns_provider.fromServer">Edit</span>
|
||||||
|
DnsProvider <span class="text-muted"><small>oh the places you will go!</small></span></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div class="form-group"
|
||||||
|
ng-class="{'has-error': createForm.label.$invalid, 'has-success': !createForm.label.$invalid&&createForm.label.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Label
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="label" ng-model="dns_provider.label" placeholder="Label" class="form-control" required/>
|
||||||
|
<p ng-show="createForm.label.$invalid && !createForm.label.$pristine" class="help-block">You must enter an
|
||||||
|
dns_provider label</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea name="comments" ng-model="dns_provider.description" placeholder="Something elegant"
|
||||||
|
class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Plugin
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select class="form-control" ng-model="dns_provider.plugin" ng-options="plugin.title for plugin in plugins"
|
||||||
|
required></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-repeat="item in dns_provider.plugin.pluginOptions">
|
||||||
|
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
{{ item.name | titleCase }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
|
class="form-control" ng-model="item.value"/>
|
||||||
|
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available"
|
||||||
|
ng-model="item.value"></select>
|
||||||
|
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox" ng-model="item.value">
|
||||||
|
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control" ng-model="item.value"/>
|
||||||
|
<div ng-if="item.type == 'export-plugin'">
|
||||||
|
<form name="exportForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<select class="form-control" ng-model="item.value"
|
||||||
|
ng-options="plugin.title for plugin in exportPlugins" required></select>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-group" ng-repeat="item in item.value.pluginOptions">
|
||||||
|
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div
|
||||||
|
ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
{{ item.name | titleCase }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
|
class="form-control" ng-model="item.value"/>
|
||||||
|
<select name="sub" ng-if="item.type == 'select'" class="form-control"
|
||||||
|
ng-options="i for i in item.available" ng-model="item.value"></select>
|
||||||
|
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox"
|
||||||
|
ng-model="item.value">
|
||||||
|
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control"
|
||||||
|
ng-model="item.value" ng-pattern="item.validation"/>
|
||||||
|
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine"
|
||||||
|
class="help-block">{{ item.helpMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine" class="help-block">{{ item.helpMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button ng-click="save(dns_provider)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save
|
||||||
|
</button>
|
||||||
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
angular.module('lemur')
|
||||||
|
.service('DnsProviderApi', function (LemurRestangular) {
|
||||||
|
return LemurRestangular.all('dns_providers');
|
||||||
|
})
|
||||||
|
.service('DnsProviderService', function ($location, DnsProviderApi, PluginService, DnsProviders) {
|
||||||
|
var DnsProviderService = this;
|
||||||
|
DnsProviderService.findDnsProvidersByName = function (filterValue) {
|
||||||
|
return DnsProviderApi.getList({'filter[label]': filterValue})
|
||||||
|
.then(function (dns_providers) {
|
||||||
|
return dns_providers;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsProviderService.getDnsProviders = function () {
|
||||||
|
return DnsProviders.get();
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsProviderService.create = function (dns_provider) {
|
||||||
|
return DnsProviderApi.post(dns_provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsProviderService.get = function () {
|
||||||
|
return DnsProviderApi.get();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DnsProviderService.update = function (dns_provider) {
|
||||||
|
return dns_provider.put();
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsProviderService.getPlugin = function (dns_provider) {
|
||||||
|
return PluginService.getByName(dns_provider.pluginName).then(function (plugin) {
|
||||||
|
dns_provider.plugin = plugin;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return DnsProviderService;
|
||||||
|
});
|
|
@ -0,0 +1,84 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('lemur')
|
||||||
|
|
||||||
|
.config(function config($stateProvider) {
|
||||||
|
$stateProvider.state('dns_providers', {
|
||||||
|
url: '/dns_providers',
|
||||||
|
templateUrl: '/angular/dns_providers/view/view.tpl.html',
|
||||||
|
controller: 'DnsProvidersViewController'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('DnsProvidersViewController', function ($scope, $uibModal, DnsProviderApi, DnsProviderService, ngTableParams, toaster) {
|
||||||
|
$scope.filter = {};
|
||||||
|
$scope.dnsProvidersTable = new ngTableParams({
|
||||||
|
page: 1, // show first page
|
||||||
|
count: 10, // count per page
|
||||||
|
sorting: {
|
||||||
|
id: 'desc' // initial sorting
|
||||||
|
},
|
||||||
|
filter: $scope.filter
|
||||||
|
}, {
|
||||||
|
total: 0, // length of data
|
||||||
|
getData: function ($defer, params) {
|
||||||
|
DnsProviderApi.getList(params.url()).then(
|
||||||
|
function (data) {
|
||||||
|
params.total(data.total);
|
||||||
|
$defer.resolve(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.remove = function (dns_provider) {
|
||||||
|
dns_provider.remove().then(
|
||||||
|
function () {
|
||||||
|
$scope.dnsProvidersTable.reload();
|
||||||
|
},
|
||||||
|
function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Opps',
|
||||||
|
body: 'I see what you did there: ' + response.data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.edit = function (dns_providerId) {
|
||||||
|
var uibModalInstance = $uibModal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
||||||
|
controller: 'DnsProvidersEditController',
|
||||||
|
size: 'lg',
|
||||||
|
backdrop: 'static',
|
||||||
|
resolve: {
|
||||||
|
editId: function () {
|
||||||
|
return dns_providerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
uibModalInstance.result.then(function () {
|
||||||
|
$scope.dnsProvidersTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var uibModalInstance = $uibModal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'DnsProvidersCreateController',
|
||||||
|
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
||||||
|
size: 'lg',
|
||||||
|
backdrop: 'static'
|
||||||
|
});
|
||||||
|
|
||||||
|
uibModalInstance.result.then(function () {
|
||||||
|
$scope.dnsProvidersTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,54 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h2 class="featurette-heading">DNS Providers
|
||||||
|
<span class="text-muted"><small>the root of all problems</small></span></h2>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button ng-click="create()" class="btn btn-primary">Create</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button ng-model="showFilter" class="btn btn-default" uib-btn-checkbox
|
||||||
|
btn-checkbox-true="1"
|
||||||
|
btn-checkbox-false="0">Filter</button>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table ng-table="dnsProvidersTable" class="table table-striped" show-filter="showFilter" template-pagination="angular/pager.html" >
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="dns_provider in $data track by $index">
|
||||||
|
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>{{ dns_provider.name }}</li>
|
||||||
|
<li><span class="text-muted">{{ dns_provider.description }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td data-title="'Type'" sortable="'type'" filter="{ 'type': 'text' }">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>{{ dns_provider.providerType }}</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td data-title="'Domains'" sortable="'domains'" filter="{ 'domains': 'text' }">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>{{ dns_provider.domains }}</li>
|
||||||
|
<li><span class="text-muted">{{ dns_provider.description }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td data-title="''">
|
||||||
|
<div class="btn-group-vertical pull-right">
|
||||||
|
<button uib-tooltip="Edit DNS Provider" ng-click="edit(dns_provider.id)" class="btn btn-sm btn-info">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button uib-tooltip="Delete DNS Provider" ng-click="remove(dns_provider)" type="button" class="btn btn-sm btn-danger pull-left">
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -65,6 +65,7 @@
|
||||||
<li><a ui-sref="domains">Domains</a></li>
|
<li><a ui-sref="domains">Domains</a></li>
|
||||||
<li><a ui-sref="logs">Logs</a></li>
|
<li><a ui-sref="logs">Logs</a></li>
|
||||||
<li><a ui-sref="keys">Api Keys</a></li>
|
<li><a ui-sref="keys">Api Keys</a></li>
|
||||||
|
<li><a ui-sref="dns_providers">DNS Providers</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -37,7 +37,8 @@ class TestAsyncIssuerPlugin(IssuerPlugin):
|
||||||
def create_certificate(self, csr, issuer_options):
|
def create_certificate(self, csr, issuer_options):
|
||||||
return "", "", 12345
|
return "", "", 12345
|
||||||
|
|
||||||
def get_ordered_certificate(self, order_id):
|
def get_ordered_certificate(self, pending_cert):
|
||||||
|
order_id = pending_cert.external_id
|
||||||
return INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, 54321
|
return INTERNAL_VALID_LONG_STR, INTERNAL_VALID_SAN_STR, 54321
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -63,7 +63,7 @@ def test_get_certificate_primitives(certificate):
|
||||||
|
|
||||||
with freeze_time(datetime.date(year=2016, month=10, day=30)):
|
with freeze_time(datetime.date(year=2016, month=10, day=30)):
|
||||||
primitives = get_certificate_primitives(certificate)
|
primitives = get_certificate_primitives(certificate)
|
||||||
assert len(primitives) == 23
|
assert len(primitives) == 24
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_edit_schema(session):
|
def test_certificate_edit_schema(session):
|
||||||
|
@ -152,7 +152,8 @@ def test_certificate_input_schema(client, authority):
|
||||||
'authority': {'id': authority.id},
|
'authority': {'id': authority.id},
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityEnd': arrow.get(2016, 11, 9).isoformat(),
|
'validityEnd': arrow.get(2016, 11, 9).isoformat(),
|
||||||
'validityStart': arrow.get(2015, 11, 9).isoformat()
|
'validityStart': arrow.get(2015, 11, 9).isoformat(),
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -165,7 +166,7 @@ def test_certificate_input_schema(client, authority):
|
||||||
assert data['country'] == 'US'
|
assert data['country'] == 'US'
|
||||||
assert data['location'] == 'Los Gatos'
|
assert data['location'] == 'Los Gatos'
|
||||||
|
|
||||||
assert len(data.keys()) == 18
|
assert len(data.keys()) == 19
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_input_with_extensions(client, authority):
|
def test_certificate_input_with_extensions(client, authority):
|
||||||
|
@ -192,7 +193,8 @@ def test_certificate_input_with_extensions(client, authority):
|
||||||
{'nameType': 'DNSName', 'value': 'test.example.com'}
|
{'nameType': 'DNSName', 'value': 'test.example.com'}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -206,7 +208,8 @@ def test_certificate_out_of_range_date(client, authority):
|
||||||
'owner': 'jim@example.com',
|
'owner': 'jim@example.com',
|
||||||
'authority': {'id': authority.id},
|
'authority': {'id': authority.id},
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityYears': 100
|
'validityYears': 100,
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -230,7 +233,8 @@ def test_certificate_valid_years(client, authority):
|
||||||
'owner': 'jim@example.com',
|
'owner': 'jim@example.com',
|
||||||
'authority': {'id': authority.id},
|
'authority': {'id': authority.id},
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityYears': 1
|
'validityYears': 1,
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -245,7 +249,8 @@ def test_certificate_valid_dates(client, authority):
|
||||||
'authority': {'id': authority.id},
|
'authority': {'id': authority.id},
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityStart': '2020-01-01T00:00:00',
|
'validityStart': '2020-01-01T00:00:00',
|
||||||
'validityEnd': '2020-01-01T00:00:01'
|
'validityEnd': '2020-01-01T00:00:01',
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -262,6 +267,7 @@ def test_certificate_cn_admin(client, authority, logged_in_admin):
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityStart': '2020-01-01T00:00:00',
|
'validityStart': '2020-01-01T00:00:00',
|
||||||
'validityEnd': '2020-01-01T00:00:01',
|
'validityEnd': '2020-01-01T00:00:01',
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -285,7 +291,8 @@ def test_certificate_allowed_names(client, authority, session, logged_in_user):
|
||||||
{'nameType': 'IPAddress', 'value': '127.0.0.1'},
|
{'nameType': 'IPAddress', 'value': '127.0.0.1'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -306,6 +313,7 @@ def test_certificate_incative_authority(client, authority, session, logged_in_us
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityStart': '2020-01-01T00:00:00',
|
'validityStart': '2020-01-01T00:00:00',
|
||||||
'validityEnd': '2020-01-01T00:00:01',
|
'validityEnd': '2020-01-01T00:00:01',
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -329,7 +337,8 @@ def test_certificate_disallowed_names(client, authority, session, logged_in_user
|
||||||
{'nameType': 'DNSName', 'value': 'evilhacker.org'},
|
{'nameType': 'DNSName', 'value': 'evilhacker.org'},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, errors = CertificateInputSchema().load(input_data)
|
data, errors = CertificateInputSchema().load(input_data)
|
||||||
|
@ -348,6 +357,7 @@ def test_certificate_sensitive_name(client, authority, session, logged_in_user):
|
||||||
'description': 'testtestest',
|
'description': 'testtestest',
|
||||||
'validityStart': '2020-01-01T00:00:00',
|
'validityStart': '2020-01-01T00:00:00',
|
||||||
'validityEnd': '2020-01-01T00:00:01',
|
'validityEnd': '2020-01-01T00:00:01',
|
||||||
|
'dns_provider': None,
|
||||||
}
|
}
|
||||||
session.add(Domain(name='sensitive.example.com', sensitive=True))
|
session.add(Domain(name='sensitive.example.com', sensitive=True))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue