commit
389ad1981b
|
@ -1,11 +1,11 @@
|
||||||
Writing a Plugin
|
Writing a Plugin
|
||||||
================
|
================
|
||||||
|
|
||||||
**The plugin interface is a work in progress.**
|
|
||||||
|
|
||||||
Several interfaces exist for extending Lemur:
|
Several interfaces exist for extending Lemur:
|
||||||
|
|
||||||
* Issuers (lemur.issuers)
|
* Issuer (lemur.plugins.base.issuer)
|
||||||
|
* Destination (lemur.plugins.base.destination)
|
||||||
|
* Source (lemur.plugins.base.source)
|
||||||
|
|
||||||
Structure
|
Structure
|
||||||
---------
|
---------
|
||||||
|
@ -29,9 +29,9 @@ if you want to pull the version using pkg_resources (which is what we recommend)
|
||||||
Inside of ``plugin.py``, you'll declare your Plugin class::
|
Inside of ``plugin.py``, you'll declare your Plugin class::
|
||||||
|
|
||||||
import lemur_pluginname
|
import lemur_pluginname
|
||||||
from lemur.common.services.issuers.plugins import Issuer
|
from lemur.plugins.base.issuer import IssuerPlugin
|
||||||
|
|
||||||
class PluginName(Plugin):
|
class PluginName(IssuerPlugin):
|
||||||
title = 'Plugin Name'
|
title = 'Plugin Name'
|
||||||
slug = 'pluginname'
|
slug = 'pluginname'
|
||||||
description = 'My awesome plugin!'
|
description = 'My awesome plugin!'
|
||||||
|
@ -55,27 +55,43 @@ And you'll register it via ``entry_points`` in your ``setup.py``::
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
That's it! Users will be able to install your plugin via ``pip install <package name>`` and configure it
|
That's it! Users will be able to install your plugin via ``pip install <package name>``.
|
||||||
via the web interface based on the hooks you enabled.
|
|
||||||
|
Interfaces
|
||||||
|
==========
|
||||||
|
|
||||||
|
Lemur has several different plugin interfaces that are used to extend Lemur, each of them require
|
||||||
|
that you subclass and override their functions in order for your plugin to function.
|
||||||
|
|
||||||
|
|
||||||
Permissions
|
Issuer
|
||||||
===========
|
------
|
||||||
|
|
||||||
As described in the plugin interface, Lemur provides a suite of permissions.
|
Issuer plugins are to be used when you want to allow Lemur to use external services to create certificates.
|
||||||
|
In the simple case this means that you have one Certificate Authority and you ask it for certificates given a
|
||||||
|
few parameters. In a more advanced case this could mean that this third party not only allows you to create certifcates
|
||||||
|
but also allows you to create Certificate Authorities and Sub Certificate Authorities.
|
||||||
|
|
||||||
In most cases, a admin (that is, if User.is_admin is ``True``), will be granted implicit permissions
|
The `IssuerPlugin` interface only required that you implement one function::
|
||||||
on everything.
|
|
||||||
|
|
||||||
This page attempts to describe those permissions, and the contextual objects along with them.
|
def create_certificate(self, options):
|
||||||
|
# requests.get('a third party')
|
||||||
|
|
||||||
.. data:: add_project
|
|
||||||
|
|
||||||
Controls whether a user can create a new project.
|
Lemur will pass a dictionary of all possible options for certificate creation.
|
||||||
|
|
||||||
::
|
Optionally the `IssuerPlugin` exposes another function for authority create::
|
||||||
|
|
||||||
>>> has_perm('add_project', user)
|
def create_authority(self, options):
|
||||||
|
# request.get('a third party')
|
||||||
|
|
||||||
|
|
||||||
|
If implemented this function will be used to allow users to create external Certificate Authorities. From this function
|
||||||
|
you are expected to return the ROOT certificate authority, any intermediates that Authority might provide and any roles
|
||||||
|
you wish to be associated with this authority.
|
||||||
|
|
||||||
|
.. Note:: You do not need to associate roles to the authority at creation time as they can always be associated after the
|
||||||
|
fact.
|
||||||
|
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
|
@ -149,3 +165,5 @@ Running tests follows the py.test standard. As long as your test files and metho
|
||||||
=========================== 1 passed in 0.35 seconds ============================
|
=========================== 1 passed in 0.35 seconds ============================
|
||||||
|
|
||||||
|
|
||||||
|
.. SeeAlso:: Lemur bundles several plugins that use the same interfaces mentioned above. View the source: #TODO
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ function browserSyncInit(baseDir, files, browser) {
|
||||||
gulp.task('serve', ['watch'], function () {
|
gulp.task('serve', ['watch'], function () {
|
||||||
browserSyncInit([
|
browserSyncInit([
|
||||||
'.tmp',
|
'.tmp',
|
||||||
'app'
|
'lemur/static/app'
|
||||||
], [
|
], [
|
||||||
'.tmp/*.html',
|
'.tmp/*.html',
|
||||||
'.tmp/styles/**/*.css',
|
'.tmp/styles/**/*.css',
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
var gulp = require('gulp');
|
var gulp = require('gulp');
|
||||||
|
|
||||||
|
|
||||||
gulp.task('watch', ['dev:styles', 'dev:scripts', 'dev:inject'] ,function () {
|
gulp.task('watch', ['dev:styles', 'dev:scripts', 'dev:inject', 'dev:fonts'] ,function () {
|
||||||
gulp.watch('app/styles/**/*.less', ['dev:styles']);
|
gulp.watch('app/styles/**/*.less', ['dev:styles']);
|
||||||
gulp.watch('app/styles/**/*.css', ['dev:styles']);
|
gulp.watch('app/styles/**/*.css', ['dev:styles']);
|
||||||
gulp.watch('app/**/*.js', ['dev:scripts']);
|
gulp.watch('app/**/*.js', ['dev:scripts']);
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from flask import jsonify
|
|
||||||
|
|
||||||
from lemur import factory
|
from lemur import factory
|
||||||
|
|
||||||
from lemur.users.views import mod as users_bp
|
from lemur.users.views import mod as users_bp
|
||||||
|
|
|
@ -72,13 +72,13 @@ def create_token(user):
|
||||||
:param user:
|
:param user:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
expiration_delta = timedelta(days=int(current_app.config.get('TOKEN_EXPIRATION', 1)))
|
expiration_delta = timedelta(days=int(current_app.config.get('LEMUR_TOKEN_EXPIRATION', 1)))
|
||||||
payload = {
|
payload = {
|
||||||
'sub': user.id,
|
'sub': user.id,
|
||||||
'iat': datetime.now(),
|
'iat': datetime.now(),
|
||||||
'exp': datetime.now() + expiration_delta
|
'exp': datetime.now() + expiration_delta
|
||||||
}
|
}
|
||||||
token = jwt.encode(payload, current_app.config['TOKEN_SECRET'])
|
token = jwt.encode(payload, current_app.config['LEMUR_TOKEN_SECRET'])
|
||||||
return token.decode('unicode_escape')
|
return token.decode('unicode_escape')
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ def login_required(f):
|
||||||
return dict(message='Token is invalid'), 403
|
return dict(message='Token is invalid'), 403
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, current_app.config['TOKEN_SECRET'])
|
payload = jwt.decode(token, current_app.config['LEMUR_TOKEN_SECRET'])
|
||||||
except jwt.DecodeError:
|
except jwt.DecodeError:
|
||||||
return dict(message='Token is invalid'), 403
|
return dict(message='Token is invalid'), 403
|
||||||
except jwt.ExpiredSignatureError:
|
except jwt.ExpiredSignatureError:
|
||||||
|
|
|
@ -14,8 +14,6 @@ from flask import g, Blueprint, current_app, abort
|
||||||
from flask.ext.restful import reqparse, Resource, Api
|
from flask.ext.restful import reqparse, Resource, Api
|
||||||
from flask.ext.principal import Identity, identity_changed
|
from flask.ext.principal import Identity, identity_changed
|
||||||
|
|
||||||
from lemur.common.crypto import unlock
|
|
||||||
|
|
||||||
from lemur.auth.permissions import admin_permission
|
from lemur.auth.permissions import admin_permission
|
||||||
from lemur.users import service as user_service
|
from lemur.users import service as user_service
|
||||||
from lemur.roles import service as role_service
|
from lemur.roles import service as role_service
|
||||||
|
@ -234,24 +232,7 @@ class Ping(Resource):
|
||||||
return dict(token=create_token(user))
|
return dict(token=create_token(user))
|
||||||
|
|
||||||
|
|
||||||
class Unlock(AuthenticatedResource):
|
|
||||||
def __init__(self):
|
|
||||||
self.reqparse = reqparse.RequestParser()
|
|
||||||
super(Unlock, self).__init__()
|
|
||||||
|
|
||||||
@admin_permission.require(http_exception=403)
|
|
||||||
def post(self):
|
|
||||||
self.reqparse.add_argument('password', type=str, required=True, location='json')
|
|
||||||
args = self.reqparse.parse_args()
|
|
||||||
unlock(args['password'])
|
|
||||||
return {
|
|
||||||
"message": "You have successfully unlocked this Lemur instance",
|
|
||||||
"type": "success"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(Login, '/auth/login', endpoint='login')
|
api.add_resource(Login, '/auth/login', endpoint='login')
|
||||||
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
||||||
api.add_resource(Unlock, '/auth/unlock', endpoint='unlock')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ from lemur.roles import service as role_service
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
import lemur.certificates.service as cert_service
|
import lemur.certificates.service as cert_service
|
||||||
|
|
||||||
from lemur.common.services.issuers.manager import get_plugin_by_name
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
def update(authority_id, active=None, roles=None):
|
def update(authority_id, active=None, roles=None):
|
||||||
"""
|
"""
|
||||||
|
@ -49,12 +49,12 @@ def create(kwargs):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
issuer = get_plugin_by_name(kwargs.get('pluginName'))
|
issuer = plugins.get(kwargs.get('pluginName'))
|
||||||
|
|
||||||
kwargs['creator'] = g.current_user.email
|
kwargs['creator'] = g.current_user.email
|
||||||
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
|
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
|
||||||
|
|
||||||
cert = cert_service.save_cert(cert_body, None, intermediate, None, None, None)
|
cert = cert_service.save_cert(cert_body, None, intermediate, None)
|
||||||
cert.user = g.current_user
|
cert.user = g.current_user
|
||||||
|
|
||||||
# we create and attach any roles that the issuer gives us
|
# we create and attach any roles that the issuer gives us
|
||||||
|
@ -65,9 +65,11 @@ def create(kwargs):
|
||||||
password=r['password'],
|
password=r['password'],
|
||||||
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
||||||
username=r['username'])
|
username=r['username'])
|
||||||
|
|
||||||
# the user creating the authority should be able to administer it
|
# the user creating the authority should be able to administer it
|
||||||
if role.username == 'admin':
|
if role.username == 'admin':
|
||||||
g.current_user.roles.append(role)
|
g.current_user.roles.append(role)
|
||||||
|
|
||||||
role_objs.append(role)
|
role_objs.append(role)
|
||||||
|
|
||||||
authority = Authority(
|
authority = Authority(
|
||||||
|
@ -80,7 +82,6 @@ def create(kwargs):
|
||||||
roles=role_objs
|
roles=role_objs
|
||||||
)
|
)
|
||||||
|
|
||||||
# do this last encase we need to roll back/abort
|
|
||||||
database.update(cert)
|
database.update(cert)
|
||||||
authority = database.create(authority)
|
authority = database.create(authority)
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,12 @@ from sqlalchemy_utils import EncryptedType
|
||||||
from lemur.database import db
|
from lemur.database import db
|
||||||
|
|
||||||
from lemur.domains.models import Domain
|
from lemur.domains.models import Domain
|
||||||
from lemur.users import service as user_service
|
|
||||||
|
|
||||||
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE, NONSTANDARD_NAMING_TEMPLATE
|
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE, NONSTANDARD_NAMING_TEMPLATE
|
||||||
from lemur.models import certificate_associations, certificate_account_associations
|
from lemur.models import certificate_associations, certificate_account_associations
|
||||||
|
|
||||||
|
|
||||||
def create_name(issuer, not_before, not_after, common_name, san):
|
def create_name(issuer, not_before, not_after, subject, san):
|
||||||
"""
|
"""
|
||||||
Create a name for our certificate. A naming standard
|
Create a name for our certificate. A naming standard
|
||||||
is based on a series of templates. The name includes
|
is based on a series of templates. The name includes
|
||||||
|
@ -36,11 +35,6 @@ def create_name(issuer, not_before, not_after, common_name, san):
|
||||||
:rtype : str
|
:rtype : str
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
delchars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
|
|
||||||
# aws doesn't allow special chars
|
|
||||||
subject = common_name.replace('*', "WILDCARD")
|
|
||||||
issuer = issuer.translate(None, delchars)
|
|
||||||
|
|
||||||
if san:
|
if san:
|
||||||
t = SAN_NAMING_TEMPLATE
|
t = SAN_NAMING_TEMPLATE
|
||||||
else:
|
else:
|
||||||
|
@ -53,7 +47,14 @@ def create_name(issuer, not_before, not_after, common_name, san):
|
||||||
not_after=not_after.strftime('%Y%m%d')
|
not_after=not_after.strftime('%Y%m%d')
|
||||||
)
|
)
|
||||||
|
|
||||||
return temp
|
# NOTE we may want to give more control over naming
|
||||||
|
# aws doesn't allow special chars except '-'
|
||||||
|
disallowed_chars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
|
||||||
|
disallowed_chars = disallowed_chars.replace("-", "")
|
||||||
|
temp = temp.replace('*', "WILDCARD")
|
||||||
|
temp = temp.translate(None, disallowed_chars)
|
||||||
|
# white space is silly too
|
||||||
|
return temp.replace(" ", "-")
|
||||||
|
|
||||||
|
|
||||||
def cert_get_cn(cert):
|
def cert_get_cn(cert):
|
||||||
|
@ -85,11 +86,6 @@ def cert_get_domains(cert):
|
||||||
domains.append(entry)
|
domains.append(entry)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
|
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
|
||||||
|
|
||||||
# do a simple check to make sure it's a real domain
|
|
||||||
common_name = cert_get_cn(cert)
|
|
||||||
if '.' in common_name:
|
|
||||||
domains.append(common_name)
|
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,11 +107,8 @@ def cert_is_san(cert):
|
||||||
:param cert:
|
:param cert:
|
||||||
:return: Bool
|
:return: Bool
|
||||||
"""
|
"""
|
||||||
domains = cert_get_domains(cert)
|
if len(cert_get_domains(cert)) > 1:
|
||||||
if len(domains) > 1:
|
|
||||||
return True
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def cert_is_wildcard(cert):
|
def cert_is_wildcard(cert):
|
||||||
"""
|
"""
|
||||||
|
@ -127,7 +120,6 @@ def cert_is_wildcard(cert):
|
||||||
domains = cert_get_domains(cert)
|
domains = cert_get_domains(cert)
|
||||||
if len(domains) == 1 and domains[0][0:1] == "*":
|
if len(domains) == 1 and domains[0][0:1] == "*":
|
||||||
return True
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def cert_get_bitstrength(cert):
|
def cert_get_bitstrength(cert):
|
||||||
|
@ -205,8 +197,8 @@ class Certificate(db.Model):
|
||||||
owner = Column(String(128))
|
owner = Column(String(128))
|
||||||
body = Column(Text())
|
body = Column(Text())
|
||||||
private_key = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
private_key = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
||||||
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY'))) # TODO deprecate
|
||||||
csr_config = Column(Text())
|
csr_config = Column(Text()) # TODO deprecate
|
||||||
status = Column(String(128))
|
status = Column(String(128))
|
||||||
deleted = Column(Boolean, index=True)
|
deleted = Column(Boolean, index=True)
|
||||||
name = Column(String(128))
|
name = Column(String(128))
|
||||||
|
@ -227,13 +219,11 @@ class Certificate(db.Model):
|
||||||
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
||||||
elb_listeners = relationship("Listener", lazy='dynamic', backref='certificate')
|
elb_listeners = relationship("Listener", lazy='dynamic', backref='certificate')
|
||||||
|
|
||||||
def __init__(self, body, private_key=None, challenge=None, chain=None, csr_config=None):
|
def __init__(self, body, private_key=None, chain=None):
|
||||||
self.body = body
|
self.body = body
|
||||||
# We encrypt the private_key on creation
|
# We encrypt the private_key on creation
|
||||||
self.private_key = private_key
|
self.private_key = private_key
|
||||||
self.chain = chain
|
self.chain = chain
|
||||||
self.csr_config = csr_config
|
|
||||||
self.challenge = challenge
|
|
||||||
cert = x509.load_pem_x509_certificate(str(self.body), default_backend())
|
cert = x509.load_pem_x509_certificate(str(self.body), default_backend())
|
||||||
self.bits = cert_get_bitstrength(cert)
|
self.bits = cert_get_bitstrength(cert)
|
||||||
self.issuer = cert_get_issuer(cert)
|
self.issuer = cert_get_issuer(cert)
|
||||||
|
|
|
@ -5,24 +5,17 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import arrow
|
import arrow
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
import hashlib
|
|
||||||
import datetime
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from sqlalchemy import func, or_
|
from sqlalchemy import func, or_
|
||||||
from flask import g, current_app
|
from flask import g, current_app
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.common.services.aws import iam
|
from lemur.common.services.aws import iam
|
||||||
from lemur.common.services.issuers.manager import get_plugin_by_name
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate
|
||||||
from lemur.certificates.exceptions import UnableToCreateCSR, \
|
|
||||||
UnableToCreatePrivateKey, MissingFiles
|
|
||||||
|
|
||||||
from lemur.accounts.models import Account
|
from lemur.accounts.models import Account
|
||||||
from lemur.accounts import service as account_service
|
from lemur.accounts import service as account_service
|
||||||
|
@ -30,6 +23,12 @@ from lemur.authorities.models import Authority
|
||||||
|
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
|
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get(cert_id):
|
def get(cert_id):
|
||||||
"""
|
"""
|
||||||
|
@ -127,24 +126,18 @@ def mint(issuer_options):
|
||||||
"""
|
"""
|
||||||
authority = issuer_options['authority']
|
authority = issuer_options['authority']
|
||||||
|
|
||||||
issuer = get_plugin_by_name(authority.plugin_name)
|
issuer = plugins.get(authority.plugin_name)
|
||||||
# NOTE if we wanted to support more issuers it might make sense to
|
|
||||||
# push CSR creation down to the plugin
|
|
||||||
path = create_csr(issuer.get_csr_config(issuer_options))
|
|
||||||
challenge, csr, csr_config, private_key = load_ssl_pack(path)
|
|
||||||
|
|
||||||
issuer_options['challenge'] = challenge
|
csr, private_key = create_csr(issuer_options)
|
||||||
|
|
||||||
|
issuer_options['challenge'] = create_challenge()
|
||||||
issuer_options['creator'] = g.user.email
|
issuer_options['creator'] = g.user.email
|
||||||
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
||||||
|
|
||||||
cert = save_cert(cert_body, private_key, cert_chain, challenge, csr_config, issuer_options.get('accounts'))
|
cert = save_cert(cert_body, private_key, cert_chain, issuer_options.get('accounts'))
|
||||||
cert.user = g.user
|
cert.user = g.user
|
||||||
cert.authority = authority
|
cert.authority = authority
|
||||||
database.update(cert)
|
database.update(cert)
|
||||||
|
|
||||||
# securely delete pack after saving it to RDS and IAM (if applicable)
|
|
||||||
delete_ssl_pack(path)
|
|
||||||
|
|
||||||
return cert, private_key, cert_chain,
|
return cert, private_key, cert_chain,
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,7 +145,7 @@ def import_certificate(**kwargs):
|
||||||
"""
|
"""
|
||||||
Uploads already minted certificates and pulls the required information into Lemur.
|
Uploads already minted certificates and pulls the required information into Lemur.
|
||||||
|
|
||||||
This is to be used for certificates that are reated outside of Lemur but
|
This is to be used for certificates that are created outside of Lemur but
|
||||||
should still be tracked.
|
should still be tracked.
|
||||||
|
|
||||||
Internally this is used to bootstrap Lemur with external
|
Internally this is used to bootstrap Lemur with external
|
||||||
|
@ -180,7 +173,7 @@ def import_certificate(**kwargs):
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
def save_cert(cert_body, private_key, cert_chain, challenge, csr_config, accounts):
|
def save_cert(cert_body, private_key, cert_chain, accounts):
|
||||||
"""
|
"""
|
||||||
Determines if the certificate needs to be uploaded to AWS or other services.
|
Determines if the certificate needs to be uploaded to AWS or other services.
|
||||||
|
|
||||||
|
@ -189,9 +182,9 @@ def save_cert(cert_body, private_key, cert_chain, challenge, csr_config, account
|
||||||
:param cert_chain:
|
:param cert_chain:
|
||||||
:param challenge:
|
:param challenge:
|
||||||
:param csr_config:
|
:param csr_config:
|
||||||
:param account_ids:
|
:param accounts:
|
||||||
"""
|
"""
|
||||||
cert = Certificate(cert_body, private_key, challenge, cert_chain, csr_config)
|
cert = Certificate(cert_body, private_key, cert_chain)
|
||||||
# if we have an AWS accounts lets upload them
|
# if we have an AWS accounts lets upload them
|
||||||
if accounts:
|
if accounts:
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
|
@ -211,8 +204,6 @@ def upload(**kwargs):
|
||||||
kwargs.get('public_cert'),
|
kwargs.get('public_cert'),
|
||||||
kwargs.get('private_key'),
|
kwargs.get('private_key'),
|
||||||
kwargs.get('intermediate_cert'),
|
kwargs.get('intermediate_cert'),
|
||||||
None,
|
|
||||||
None,
|
|
||||||
kwargs.get('accounts')
|
kwargs.get('accounts')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -230,6 +221,7 @@ def create(**kwargs):
|
||||||
|
|
||||||
cert.owner = kwargs['owner']
|
cert.owner = kwargs['owner']
|
||||||
database.create(cert)
|
database.create(cert)
|
||||||
|
cert.description = kwargs['description']
|
||||||
g.user.certificates.append(cert)
|
g.user.certificates.append(cert)
|
||||||
database.update(g.user)
|
database.update(g.user)
|
||||||
return cert
|
return cert
|
||||||
|
@ -302,94 +294,92 @@ def create_csr(csr_config):
|
||||||
|
|
||||||
:param csr_config:
|
:param csr_config:
|
||||||
"""
|
"""
|
||||||
|
private_key = rsa.generate_private_key(
|
||||||
|
public_exponent=65537,
|
||||||
|
key_size=2048,
|
||||||
|
backend=default_backend()
|
||||||
|
)
|
||||||
|
|
||||||
# we create a no colliding file name
|
# TODO When we figure out a better way to validate these options they should be parsed as unicode
|
||||||
path = create_path(hashlib.md5(csr_config).hexdigest())
|
builder = x509.CertificateSigningRequestBuilder()
|
||||||
|
builder = builder.subject_name(x509.Name([
|
||||||
|
x509.NameAttribute(x509.OID_COMMON_NAME, unicode(csr_config['commonName'])),
|
||||||
|
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, unicode(csr_config['organization'])),
|
||||||
|
x509.NameAttribute(x509.OID_ORGANIZATIONAL_UNIT_NAME, unicode(csr_config['organizationalUnit'])),
|
||||||
|
x509.NameAttribute(x509.OID_COUNTRY_NAME, unicode(csr_config['country'])),
|
||||||
|
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, unicode(csr_config['state'])),
|
||||||
|
x509.NameAttribute(x509.OID_LOCALITY_NAME, unicode(csr_config['location'])),
|
||||||
|
]))
|
||||||
|
|
||||||
challenge = create_challenge()
|
builder = builder.add_extension(
|
||||||
challenge_path = os.path.join(path, 'challenge.txt')
|
x509.BasicConstraints(ca=False, path_length=None), critical=True,
|
||||||
|
)
|
||||||
|
|
||||||
with open(challenge_path, 'w') as c:
|
#for k, v in csr_config.get('extensions', {}).items():
|
||||||
c.write(challenge)
|
# if k == 'subAltNames':
|
||||||
|
# builder = builder.add_extension(
|
||||||
|
# x509.SubjectAlternativeName([x509.DNSName(n) for n in v]), critical=True,
|
||||||
|
# )
|
||||||
|
|
||||||
csr_path = os.path.join(path, 'csr_config.txt')
|
# TODO support more CSR options, none of the authorities support these atm
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.KeyUsage(
|
||||||
|
# digital_signature=digital_signature,
|
||||||
|
# content_commitment=content_commitment,
|
||||||
|
# key_encipherment=key_enipherment,
|
||||||
|
# data_encipherment=data_encipherment,
|
||||||
|
# key_agreement=key_agreement,
|
||||||
|
# key_cert_sign=key_cert_sign,
|
||||||
|
# crl_sign=crl_sign,
|
||||||
|
# encipher_only=enchipher_only,
|
||||||
|
# decipher_only=decipher_only
|
||||||
|
# ), critical=True
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# # we must maintain our own list of OIDs here
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.ExtendedKeyUsage(
|
||||||
|
# server_authentication=server_authentication,
|
||||||
|
# email=
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.AuthorityInformationAccess()
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.AuthorityKeyIdentifier()
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.SubjectKeyIdentifier()
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.CRLDistributionPoints()
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# builder.add_extension(
|
||||||
|
# x509.ObjectIdentifier(oid)
|
||||||
|
# )
|
||||||
|
|
||||||
with open(csr_path, 'w') as f:
|
request = builder.sign(
|
||||||
f.write(csr_config)
|
private_key, hashes.SHA256(), default_backend()
|
||||||
|
)
|
||||||
|
|
||||||
#TODO use cloudCA to seed a -rand file for each call
|
# serialize our private key and CSR
|
||||||
#TODO replace openssl shell calls with cryptograph
|
pem = private_key.private_bytes(
|
||||||
with open('/dev/null', 'w') as devnull:
|
encoding=serialization.Encoding.PEM,
|
||||||
code = subprocess.call(['openssl', 'genrsa',
|
format=serialization.PrivateFormat.TraditionalOpenSSL, # would like to use PKCS8 but AWS ELBs don't like it
|
||||||
'-out', os.path.join(path, 'private.key'), '2048'],
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
stdout=devnull, stderr=devnull)
|
)
|
||||||
|
|
||||||
if code != 0:
|
csr = request.public_bytes(
|
||||||
raise UnableToCreatePrivateKey(code)
|
encoding=serialization.Encoding.PEM
|
||||||
|
)
|
||||||
with open('/dev/null', 'w') as devnull:
|
|
||||||
code = subprocess.call(['openssl', 'req', '-new', '-sha256', '-nodes',
|
|
||||||
'-config', csr_path, "-key", os.path.join(path, 'private.key'),
|
|
||||||
"-out", os.path.join(path, 'request.csr')], stdout=devnull, stderr=devnull)
|
|
||||||
|
|
||||||
if code != 0:
|
|
||||||
raise UnableToCreateCSR(code)
|
|
||||||
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def create_path(domain_hash):
|
|
||||||
"""
|
|
||||||
|
|
||||||
:param domain_hash:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
path = os.path.join('/tmp', domain_hash)
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.mkdir(path)
|
|
||||||
except OSError as e:
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
path = os.path.join('/tmp', "{}.{}".format(domain_hash, now.strftime('%s')))
|
|
||||||
os.mkdir(path)
|
|
||||||
current_app.logger.warning(e)
|
|
||||||
|
|
||||||
current_app.logger.debug("Writing ssl files to: {}".format(path))
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def load_ssl_pack(path):
|
|
||||||
"""
|
|
||||||
Loads the information created by openssl to be used by other functions.
|
|
||||||
|
|
||||||
:param path:
|
|
||||||
"""
|
|
||||||
if len(os.listdir(path)) != 4:
|
|
||||||
raise MissingFiles(path)
|
|
||||||
|
|
||||||
with open(os.path.join(path, 'challenge.txt')) as c:
|
|
||||||
challenge = c.read()
|
|
||||||
|
|
||||||
with open(os.path.join(path, 'request.csr')) as r:
|
|
||||||
csr = r.read()
|
|
||||||
|
|
||||||
with open(os.path.join(path, 'csr_config.txt')) as config:
|
|
||||||
csr_config = config.read()
|
|
||||||
|
|
||||||
with open(os.path.join(path, 'private.key')) as key:
|
|
||||||
private_key = key.read()
|
|
||||||
|
|
||||||
return (challenge, csr, csr_config, private_key,)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_ssl_pack(path):
|
|
||||||
"""
|
|
||||||
Removes the temporary files associated with CSR creation.
|
|
||||||
|
|
||||||
:param path:
|
|
||||||
"""
|
|
||||||
subprocess.check_call(['srm', '-r', path])
|
|
||||||
|
|
||||||
|
return csr, pem
|
||||||
|
|
||||||
def create_challenge():
|
def create_challenge():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -30,8 +30,7 @@ from lemur.certificates.models import Certificate, get_name_from_arn
|
||||||
from lemur.common.services.aws.iam import get_all_server_certs
|
from lemur.common.services.aws.iam import get_all_server_certs
|
||||||
from lemur.common.services.aws.iam import get_cert_from_arn
|
from lemur.common.services.aws.iam import get_cert_from_arn
|
||||||
|
|
||||||
from lemur.common.services.issuers.manager import get_plugin_by_name
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
|
||||||
def aws():
|
def aws():
|
||||||
"""
|
"""
|
||||||
|
@ -101,7 +100,7 @@ def cloudca():
|
||||||
"""
|
"""
|
||||||
user = user_service.get_by_email('lemur@nobody')
|
user = user_service.get_by_email('lemur@nobody')
|
||||||
# sync all new certificates/authorities not created through lemur
|
# sync all new certificates/authorities not created through lemur
|
||||||
issuer = get_plugin_by_name('cloudca')
|
issuer = plugins.get('cloudca')
|
||||||
authorities = issuer.get_authorities()
|
authorities = issuer.get_authorities()
|
||||||
total = 0
|
total = 0
|
||||||
new = 1
|
new = 1
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
"""
|
|
||||||
.. module: lemur.common.crypto
|
|
||||||
:platform: Unix
|
|
||||||
:synopsis: This module contains all cryptographic function's in Lemur
|
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
|
||||||
:license: Apache, see LICENSE for more details.
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import ssl
|
|
||||||
import StringIO
|
|
||||||
import functools
|
|
||||||
from Crypto import Random
|
|
||||||
from Crypto.Cipher import AES
|
|
||||||
from hashlib import sha512
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from lemur.factory import create_app
|
|
||||||
|
|
||||||
|
|
||||||
old_init = ssl.SSLSocket.__init__
|
|
||||||
|
|
||||||
@functools.wraps(old_init)
|
|
||||||
def ssl_bug(self, *args, **kwargs):
|
|
||||||
kwargs['ssl_version'] = ssl.PROTOCOL_TLSv1
|
|
||||||
old_init(self, *args, **kwargs)
|
|
||||||
|
|
||||||
ssl.SSLSocket.__init__ = ssl_bug
|
|
||||||
|
|
||||||
|
|
||||||
def derive_key_and_iv(password, salt, key_length, iv_length):
|
|
||||||
"""
|
|
||||||
Derives the key and iv from the password and salt.
|
|
||||||
|
|
||||||
:param password:
|
|
||||||
:param salt:
|
|
||||||
:param key_length:
|
|
||||||
:param iv_length:
|
|
||||||
:return: key, iv
|
|
||||||
"""
|
|
||||||
d = d_i = ''
|
|
||||||
|
|
||||||
while len(d) < key_length + iv_length:
|
|
||||||
d_i = sha512(d_i + password + salt).digest()
|
|
||||||
d += d_i
|
|
||||||
|
|
||||||
return d[:key_length], d[key_length:key_length+iv_length]
|
|
||||||
|
|
||||||
|
|
||||||
def encrypt(in_file, out_file, password, key_length=32):
|
|
||||||
"""
|
|
||||||
Encrypts a file.
|
|
||||||
|
|
||||||
:param in_file:
|
|
||||||
:param out_file:
|
|
||||||
:param password:
|
|
||||||
:param key_length:
|
|
||||||
"""
|
|
||||||
bs = AES.block_size
|
|
||||||
salt = Random.new().read(bs - len('Salted__'))
|
|
||||||
key, iv = derive_key_and_iv(password, salt, key_length, bs)
|
|
||||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
|
||||||
out_file.write('Salted__' + salt)
|
|
||||||
finished = False
|
|
||||||
while not finished:
|
|
||||||
chunk = in_file.read(1024 * bs)
|
|
||||||
if len(chunk) == 0 or len(chunk) % bs != 0:
|
|
||||||
padding_length = bs - (len(chunk) % bs)
|
|
||||||
chunk += padding_length * chr(padding_length)
|
|
||||||
finished = True
|
|
||||||
out_file.write(cipher.encrypt(chunk))
|
|
||||||
|
|
||||||
|
|
||||||
def decrypt(in_file, out_file, password, key_length=32):
|
|
||||||
"""
|
|
||||||
Decrypts a file.
|
|
||||||
|
|
||||||
:param in_file:
|
|
||||||
:param out_file:
|
|
||||||
:param password:
|
|
||||||
:param key_length:
|
|
||||||
:raise ValueError:
|
|
||||||
"""
|
|
||||||
bs = AES.block_size
|
|
||||||
salt = in_file.read(bs)[len('Salted__'):]
|
|
||||||
key, iv = derive_key_and_iv(password, salt, key_length, bs)
|
|
||||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
|
||||||
next_chunk = ''
|
|
||||||
finished = False
|
|
||||||
while not finished:
|
|
||||||
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
|
|
||||||
if len(next_chunk) == 0:
|
|
||||||
padding_length = ord(chunk[-1])
|
|
||||||
if padding_length < 1 or padding_length > bs:
|
|
||||||
raise ValueError("bad decrypt pad (%d)" % padding_length)
|
|
||||||
# all the pad-bytes must be the same
|
|
||||||
if chunk[-padding_length:] != (padding_length * chr(padding_length)):
|
|
||||||
# this is similar to the bad decrypt:evp_enc.c from openssl program
|
|
||||||
raise ValueError("bad decrypt")
|
|
||||||
chunk = chunk[:-padding_length]
|
|
||||||
finished = True
|
|
||||||
out_file.write(chunk)
|
|
||||||
|
|
||||||
|
|
||||||
def encrypt_string(string, password):
|
|
||||||
"""
|
|
||||||
Encrypts a string.
|
|
||||||
|
|
||||||
:param string:
|
|
||||||
:param password:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
in_file = StringIO.StringIO(string)
|
|
||||||
enc_file = StringIO.StringIO()
|
|
||||||
encrypt(in_file, enc_file, password)
|
|
||||||
enc_file.seek(0)
|
|
||||||
return enc_file.read()
|
|
||||||
|
|
||||||
|
|
||||||
def decrypt_string(string, password):
|
|
||||||
"""
|
|
||||||
Decrypts a string.
|
|
||||||
|
|
||||||
:param string:
|
|
||||||
:param password:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
in_file = StringIO.StringIO(string)
|
|
||||||
out_file = StringIO.StringIO()
|
|
||||||
decrypt(in_file, out_file, password)
|
|
||||||
out_file.seek(0)
|
|
||||||
return out_file.read()
|
|
||||||
|
|
||||||
|
|
||||||
def lock(password):
|
|
||||||
"""
|
|
||||||
Encrypts Lemur's KEY_PATH. This directory can be used to store secrets needed for normal
|
|
||||||
Lemur operation. This is especially useful for storing secrets needed for communication
|
|
||||||
with third parties (e.g. external certificate authorities).
|
|
||||||
|
|
||||||
Lemur does not assume anything about the contents of the directory and will attempt to
|
|
||||||
encrypt all files contained within. Currently this has only been tested against plain
|
|
||||||
text files.
|
|
||||||
|
|
||||||
:param password:
|
|
||||||
"""
|
|
||||||
dest_dir = os.path.join(current_app.config.get("KEY_PATH"), "encrypted")
|
|
||||||
|
|
||||||
if not os.path.exists(dest_dir):
|
|
||||||
current_app.logger.debug("Creating encryption directory: {0}".format(dest_dir))
|
|
||||||
os.makedirs(dest_dir)
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(os.path.join(current_app.config.get("KEY_PATH"), 'decrypted')):
|
|
||||||
for f in files:
|
|
||||||
source = os.path.join(root, f)
|
|
||||||
dest = os.path.join(dest_dir, f + ".enc")
|
|
||||||
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
|
||||||
encrypt(in_file, out_file, password)
|
|
||||||
|
|
||||||
|
|
||||||
def unlock(password):
|
|
||||||
"""
|
|
||||||
Decrypts Lemur's KEY_PATH, allowing lemur to use the secrets within.
|
|
||||||
|
|
||||||
This reverses the :func:`lock` function.
|
|
||||||
|
|
||||||
:param password:
|
|
||||||
"""
|
|
||||||
dest_dir = os.path.join(current_app.config.get("KEY_PATH"), "decrypted")
|
|
||||||
source_dir = os.path.join(current_app.config.get("KEY_PATH"), "encrypted")
|
|
||||||
|
|
||||||
if not os.path.exists(dest_dir):
|
|
||||||
current_app.logger.debug("Creating decryption directory: {0}".format(dest_dir))
|
|
||||||
os.makedirs(dest_dir)
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(source_dir):
|
|
||||||
for f in files:
|
|
||||||
source = os.path.join(source_dir, f)
|
|
||||||
dest = os.path.join(dest_dir, ".".join(f.split(".")[:-1]))
|
|
||||||
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
|
||||||
current_app.logger.debug("Writing file: {0} Source: {1}".format(dest, source))
|
|
||||||
decrypt(in_file, out_file, password)
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.common.managers
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
# inspired by https://github.com/getsentry/sentry
|
||||||
|
class InstanceManager(object):
|
||||||
|
def __init__(self, class_list=None, instances=True):
|
||||||
|
if class_list is None:
|
||||||
|
class_list = []
|
||||||
|
self.instances = instances
|
||||||
|
self.update(class_list)
|
||||||
|
|
||||||
|
def get_class_list(self):
|
||||||
|
return self.class_list
|
||||||
|
|
||||||
|
def add(self, class_path):
|
||||||
|
self.cache = None
|
||||||
|
self.class_list.append(class_path)
|
||||||
|
|
||||||
|
def remove(self, class_path):
|
||||||
|
self.cache = None
|
||||||
|
self.class_list.remove(class_path)
|
||||||
|
|
||||||
|
def update(self, class_list):
|
||||||
|
"""
|
||||||
|
Updates the class list and wipes the cache.
|
||||||
|
"""
|
||||||
|
self.cache = None
|
||||||
|
self.class_list = class_list
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
"""
|
||||||
|
Returns a list of cached instances.
|
||||||
|
"""
|
||||||
|
class_list = list(self.get_class_list())
|
||||||
|
if not class_list:
|
||||||
|
self.cache = []
|
||||||
|
return []
|
||||||
|
|
||||||
|
if self.cache is not None:
|
||||||
|
return self.cache
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for cls_path in class_list:
|
||||||
|
module_name, class_name = cls_path.rsplit('.', 1)
|
||||||
|
try:
|
||||||
|
module = __import__(module_name, {}, {}, class_name)
|
||||||
|
cls = getattr(module, class_name)
|
||||||
|
if self.instances:
|
||||||
|
results.append(cls())
|
||||||
|
else:
|
||||||
|
results.append(cls)
|
||||||
|
except Exception:
|
||||||
|
current_app.logger.exception('Unable to import %s', cls_path)
|
||||||
|
continue
|
||||||
|
self.cache = results
|
||||||
|
|
||||||
|
return results
|
|
@ -1,37 +0,0 @@
|
||||||
"""
|
|
||||||
.. module: lemur.common.services.issuers.manager
|
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
|
||||||
:license: Apache, see LICENSE for more details.
|
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson (kglisson@netflix.com)
|
|
||||||
"""
|
|
||||||
import pkgutil
|
|
||||||
from importlib import import_module
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from lemur.common.services.issuers import plugins
|
|
||||||
|
|
||||||
# TODO make the plugin dir configurable
|
|
||||||
def get_plugin_by_name(plugin_name):
|
|
||||||
"""
|
|
||||||
Fetches a given plugin by it's name. We use a known location for issuer plugins and attempt
|
|
||||||
to load it such that it can be used for issuing certificates.
|
|
||||||
|
|
||||||
:param plugin_name:
|
|
||||||
:return: a plugin `class` :raise Exception: Generic error whenever the plugin specified can not be found.
|
|
||||||
"""
|
|
||||||
for importer, modname, ispkg in pkgutil.iter_modules(plugins.__path__):
|
|
||||||
try:
|
|
||||||
issuer = import_module('lemur.common.services.issuers.plugins.{0}.{0}'.format(modname))
|
|
||||||
if issuer.__name__ == plugin_name:
|
|
||||||
# we shouldn't return bad issuers
|
|
||||||
issuer_obj = issuer.init()
|
|
||||||
return issuer_obj
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.warn("Issuer {0} was unable to be imported: {1}".format(modname, e))
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise Exception("Could not find the specified plugin: {0}".format(plugin_name))
|
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
CSR_CONFIG = """
|
|
||||||
# Configuration for standard CSR generation for Netflix
|
|
||||||
# Used for procuring CloudCA certificates
|
|
||||||
# Author: kglisson
|
|
||||||
# Contact: secops@netflix.com
|
|
||||||
|
|
||||||
[ req ]
|
|
||||||
# Use a 2048 bit private key
|
|
||||||
default_bits = 2048
|
|
||||||
default_keyfile = key.pem
|
|
||||||
prompt = no
|
|
||||||
encrypt_key = no
|
|
||||||
|
|
||||||
# base request
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
|
|
||||||
# distinguished_name
|
|
||||||
[ req_distinguished_name ]
|
|
||||||
countryName = "{country}" # C=
|
|
||||||
stateOrProvinceName = "{state}" # ST=
|
|
||||||
localityName = "{location}" # L=
|
|
||||||
organizationName = "{organization}" # O=
|
|
||||||
organizationalUnitName = "{organizationalUnit}" # OU=
|
|
||||||
# This is the hostname/subject name on the certificate
|
|
||||||
commonName = "{commonName}" # CN=
|
|
||||||
"""
|
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
CSR_CONFIG = """
|
|
||||||
# Configuration for standard CSR generation for Netflix
|
|
||||||
# Used for procuring VeriSign certificates
|
|
||||||
# Author: jachan
|
|
||||||
# Contact: cloudsecurity@netflix.com
|
|
||||||
|
|
||||||
[ req ]
|
|
||||||
# Use a 2048 bit private key
|
|
||||||
default_bits = 2048
|
|
||||||
default_keyfile = key.pem
|
|
||||||
prompt = no
|
|
||||||
encrypt_key = no
|
|
||||||
|
|
||||||
# base request
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
|
|
||||||
# extensions
|
|
||||||
# Uncomment the following line if you are requesting a SAN cert
|
|
||||||
{is_san_comment}req_extensions = req_ext
|
|
||||||
|
|
||||||
# distinguished_name
|
|
||||||
[ req_distinguished_name ]
|
|
||||||
countryName = "US" # C=
|
|
||||||
stateOrProvinceName = "CALIFORNIA" # ST=
|
|
||||||
localityName = "Los Gatos" # L=
|
|
||||||
organizationName = "Netflix, Inc." # O=
|
|
||||||
organizationalUnitName = "{OU}" # OU=
|
|
||||||
# This is the hostname/subject name on the certificate
|
|
||||||
commonName = "{DNS[0]}" # CN=
|
|
||||||
|
|
||||||
[ req_ext ]
|
|
||||||
# Uncomment the following line if you are requesting a SAN cert
|
|
||||||
{is_san_comment}subjectAltName = @alt_names
|
|
||||||
|
|
||||||
[alt_names]
|
|
||||||
# Put your SANs here
|
|
||||||
{DNS_LINES}
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERISIGN_INTERMEDIATE = """
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFFTCCA/2gAwIBAgIQKC4nkXkzkuQo8iGnTsk3rjANBgkqhkiG9w0BAQsFADCB
|
|
||||||
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
|
||||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
|
|
||||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
|
||||||
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
|
||||||
aG9yaXR5IC0gRzMwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw
|
|
||||||
CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
|
|
||||||
BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs
|
|
||||||
YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
|
||||||
AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb
|
|
||||||
A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW
|
|
||||||
9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu
|
|
||||||
s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T
|
|
||||||
L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK
|
|
||||||
Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBQDCCATwwHQYDVR0O
|
|
||||||
BBYEFF9gz2GQVd+EQxSKYCqy9Xr0QxjvMBIGA1UdEwEB/wQIMAYBAf8CAQAwawYD
|
|
||||||
VR0gBGQwYjBgBgpghkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cu
|
|
||||||
c3ltYXV0aC5jb20vY3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0
|
|
||||||
aC5jb20vcnBhMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNvbS9w
|
|
||||||
Y2EzLWczLmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwxGjAYBgNV
|
|
||||||
BAMTEVN5bWFudGVjUEtJLTEtNTM0MC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcw
|
|
||||||
AYYSaHR0cDovL3Muc3ltY2QuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBbF1K+1lZ7
|
|
||||||
9Pc0CUuWysf2IdBpgO/nmhnoJOJ/2S9h3RPrWmXk4WqQy04q6YoW51KN9kMbRwUN
|
|
||||||
gKOomv4p07wdKNWlStRxPA91xQtzPwBIZXkNq2oeJQzAAt5mrL1LBmuaV4oqgX5n
|
|
||||||
m7pSYHPEFfe7wVDJCKW6V0o6GxBzHOF7tpQDS65RsIJAOloknO4NWF2uuil6yjOe
|
|
||||||
soHCL47BJ89A8AShP/U3wsr8rFNtqVNpT+F2ZAwlgak3A/I5czTSwXx4GByoaxbn
|
|
||||||
5+CdKa/Y5Gk5eZVpuXtcXQGc1PfzSEUTZJXXCm5y2kMiJG8+WnDcwJLgLeVX+OQr
|
|
||||||
J+71/xuzAYN6
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
VERISIGN_ROOT = """
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
|
|
||||||
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
|
|
||||||
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
|
|
||||||
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
|
|
||||||
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
|
||||||
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
|
|
||||||
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
|
|
||||||
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
|
|
||||||
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
|
|
||||||
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
|
|
||||||
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
|
|
||||||
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
|
|
||||||
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
|
|
||||||
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
|
|
||||||
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
|
|
||||||
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
|
|
||||||
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
|
|
||||||
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
|
|
||||||
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
|
|
||||||
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
|
|
||||||
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
|
||||||
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
"""
|
|
||||||
|
|
||||||
OLD_VERISIGN_INTERMEDIATE = """
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFlTCCBH2gAwIBAgIQLP62CQ7ireLp/CI3JPG2vzANBgkqhkiG9w0BAQUFADCB
|
|
||||||
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
|
||||||
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
|
|
||||||
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
|
||||||
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
|
||||||
aG9yaXR5IC0gRzMwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
|
|
||||||
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
|
|
||||||
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
|
|
||||||
aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
|
|
||||||
aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
|
|
||||||
DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
|
|
||||||
5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
|
|
||||||
f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
|
|
||||||
tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
|
|
||||||
GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
|
|
||||||
M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggGIMIIB
|
|
||||||
hDASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4RQEHFwMw
|
|
||||||
VjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczAqBggr
|
|
||||||
BgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMA4GA1UdDwEB
|
|
||||||
/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9naWYwITAf
|
|
||||||
MAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8vbG9nby52
|
|
||||||
ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQ
|
|
||||||
VmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+HSCrJfQBY9i+eaUw
|
|
||||||
NAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy1n
|
|
||||||
My5jcmwwDQYJKoZIhvcNAQEFBQADggEBAHREFQzFWA4YY+3z8CjDeuuSSG/ghSBJ
|
|
||||||
olwwlpIX4IjoeYuzT864Hzk2tTeEeODf4YFIVsSxah8nUsGdpgVTUGPPoUJOMXvn
|
|
||||||
8wJeBSlUDXBwv3td5XbPIPXHy6vmIS6phYRetZUgq1CDTI/pvtWZKXTGM/eYXlLF
|
|
||||||
6QDvXevUHQjfb3cqQvfLljws85xLxbNFmz7cy9YmiLOd5n+gFC6X5hzSDO7+DDMi
|
|
||||||
o//+4Q/nk/UId1UCsobqYWVmqs017AmyiAPO/v3sGncYYQY2BMYgla74dZfeDNu4
|
|
||||||
MXA68Mb6ZdlkhGEmZYVBcOmkaKs+P+SggTofsK27BlpugAtNWjEy5JY=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEOzCCA6SgAwIBAgIQSsnqCI7m94zHpfn6OaSTljANBgkqhkiG9w0BAQUFADBf
|
|
||||||
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
|
|
||||||
LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
|
|
||||||
HhcNMTEwNjA5MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
|
|
||||||
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
|
|
||||||
dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJpU2lnbiwgSW5jLiAtIEZv
|
|
||||||
ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
|
|
||||||
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzMwggEi
|
|
||||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLupxS/HgfGh5vGzdzvfjJa5QS
|
|
||||||
ME/wNkf10JEK9RfIpWHBFkBN+4phkOV2IMERBn2rLG6m9RFBjvotrSphWaRnJkzQ
|
|
||||||
6LxSW3AgBFjResmkabyDF2StBYu80FjOjYz16/BCSQudlydnMm7hrpMVHHC8IE0v
|
|
||||||
GN6SiOhshVcRGul+4yYRVKJFllWDyjCJ6NzYo+0qgD9/eWVXPhUgZggvlZO/qkcv
|
|
||||||
qEaX8BLi/sIKK1Hmdua3RrfiDabMqMNMWVWJ5uhTXBzqnfBiFgunyV8M8N7Cds6v
|
|
||||||
92ry+kGmojMUyeV6Y9OeYjfVhWWeDuZTJHQbXh0SU1vHLOeDSTsVropouVeXAgMB
|
|
||||||
AAGjggEGMIIBAjAPBgNVHRMBAf8EBTADAQH/MD0GA1UdIAQ2MDQwMgYEVR0gADAq
|
|
||||||
MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vY3BzMDEGA1Ud
|
|
||||||
HwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4G
|
|
||||||
A1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9n
|
|
||||||
aWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8v
|
|
||||||
bG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjANBgkqhkiG9w0BAQUFAAOBgQBl
|
|
||||||
2Sr58sJgybnqQQfKNrcYL2iu/gMk5mdU7nTDLNn1M8Fetw6Tz3iejrImFBFT0cjC
|
|
||||||
EiG0PXsq2BzUS2TsiU+/lYeH3pVk9HPGF9+9GZCX6GmBEmlmStMkQA5ZdRWwRHQX
|
|
||||||
op4GYNOwg7jdL+afe2dcFqFH284ueQXZ8fT4PuJKoQ==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
"""
|
|
|
@ -12,6 +12,7 @@
|
||||||
import os
|
import os
|
||||||
import imp
|
import imp
|
||||||
import errno
|
import errno
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
from logging import Formatter
|
from logging import Formatter
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
@ -51,6 +52,7 @@ def create_app(app_name=None, blueprints=None, config=None):
|
||||||
configure_blueprints(app, blueprints)
|
configure_blueprints(app, blueprints)
|
||||||
configure_extensions(app)
|
configure_extensions(app)
|
||||||
configure_logging(app)
|
configure_logging(app)
|
||||||
|
install_plugins(app)
|
||||||
|
|
||||||
@app.teardown_appcontext
|
@app.teardown_appcontext
|
||||||
def teardown(exception=None):
|
def teardown(exception=None):
|
||||||
|
@ -141,3 +143,26 @@ def configure_logging(app):
|
||||||
app.logger.setLevel(app.config.get('LOG_LEVEL', 'DEBUG'))
|
app.logger.setLevel(app.config.get('LOG_LEVEL', 'DEBUG'))
|
||||||
app.logger.addHandler(handler)
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
|
def install_plugins(app):
|
||||||
|
"""
|
||||||
|
Installs new issuers that are not currently bundled with Lemur.
|
||||||
|
|
||||||
|
:param settings:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
from lemur.plugins.base import register
|
||||||
|
# entry_points={
|
||||||
|
# 'lemur.plugins': [
|
||||||
|
# 'verisign = lemur_verisign.plugin:VerisignPlugin'
|
||||||
|
# ],
|
||||||
|
# },
|
||||||
|
for ep in pkg_resources.iter_entry_points('lemur.plugins'):
|
||||||
|
try:
|
||||||
|
plugin = ep.load()
|
||||||
|
except Exception:
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
app.logger.error("Failed to load plugin %r:\n%s\n" % (ep.name, traceback.format_exc()))
|
||||||
|
else:
|
||||||
|
register(plugin)
|
||||||
|
|
194
lemur/manage.py
194
lemur/manage.py
|
@ -1,9 +1,10 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import base64
|
import base64
|
||||||
from gunicorn.config import make_settings
|
from gunicorn.config import make_settings
|
||||||
|
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask.ext.script import Manager, Command, Option, Group, prompt_pass
|
from flask.ext.script import Manager, Command, Option, Group, prompt_pass
|
||||||
from flask.ext.migrate import Migrate, MigrateCommand, stamp
|
from flask.ext.migrate import Migrate, MigrateCommand, stamp
|
||||||
|
@ -20,7 +21,6 @@ from lemur.certificates import sync
|
||||||
from lemur.elbs.sync import sync_all_elbs
|
from lemur.elbs.sync import sync_all_elbs
|
||||||
|
|
||||||
from lemur import create_app
|
from lemur import create_app
|
||||||
from lemur.common.crypto import encrypt, decrypt, lock, unlock
|
|
||||||
|
|
||||||
# Needed to be imported so that SQLAlchemy create_all can find our models
|
# Needed to be imported so that SQLAlchemy create_all can find our models
|
||||||
from lemur.users.models import User
|
from lemur.users.models import User
|
||||||
|
@ -133,78 +133,6 @@ def create():
|
||||||
stamp(revision='head')
|
stamp(revision='head')
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def lock():
|
|
||||||
"""
|
|
||||||
Encrypts all of the files in the `keys` directory with the password
|
|
||||||
given. This is a useful function to ensure that you do no check in
|
|
||||||
your key files into source code in clear text.
|
|
||||||
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
password = prompt_pass("Please enter the encryption password")
|
|
||||||
lock(password)
|
|
||||||
sys.stdout.write("[+] Lemur keys have been encrypted!\n")
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def unlock():
|
|
||||||
"""
|
|
||||||
Decrypts all of the files in the `keys` directory with the password
|
|
||||||
given. This is most commonly used during the startup sequence of Lemur
|
|
||||||
allowing it to go from source code to something that can communicate
|
|
||||||
with external services.
|
|
||||||
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
password = prompt_pass("Please enter the encryption password")
|
|
||||||
unlock(password)
|
|
||||||
sys.stdout.write("[+] Lemur keys have been unencrypted!\n")
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def encrypt_file(source):
|
|
||||||
"""
|
|
||||||
Utility to encrypt sensitive files, Lemur will decrypt these
|
|
||||||
files when admin enters the correct password.
|
|
||||||
|
|
||||||
Uses AES-256-CBC encryption
|
|
||||||
"""
|
|
||||||
dest = source + ".encrypted"
|
|
||||||
password = prompt_pass("Please enter the encryption password")
|
|
||||||
password1 = prompt_pass("Please confirm the encryption password")
|
|
||||||
if password != password1:
|
|
||||||
sys.stdout.write("[!] Encryption passwords do not match!\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
|
||||||
encrypt(in_file, out_file, password)
|
|
||||||
|
|
||||||
sys.stdout.write("[+] Writing encryption files... {0}!\n".format(dest))
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def decrypt_file(source):
|
|
||||||
"""
|
|
||||||
Utility to decrypt, Lemur will decrypt these
|
|
||||||
files when admin enters the correct password.
|
|
||||||
|
|
||||||
Assumes AES-256-CBC encryption
|
|
||||||
"""
|
|
||||||
# cleanup extensions a bit
|
|
||||||
if ".encrypted" in source:
|
|
||||||
dest = ".".join(source.split(".")[:-1]) + ".decrypted"
|
|
||||||
else:
|
|
||||||
dest = source + ".decrypted"
|
|
||||||
|
|
||||||
password = prompt_pass("Please enter the encryption password")
|
|
||||||
|
|
||||||
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
|
||||||
decrypt(in_file, out_file, password)
|
|
||||||
|
|
||||||
sys.stdout.write("[+] Writing decrypted files... {0}!\n".format(dest))
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
@manager.command
|
||||||
def check_revoked():
|
def check_revoked():
|
||||||
"""
|
"""
|
||||||
|
@ -346,45 +274,6 @@ class InitializeApp(Command):
|
||||||
sys.stdout.write("[/] Done!\n")
|
sys.stdout.write("[/] Done!\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#def install_issuers(settings):
|
|
||||||
# """
|
|
||||||
# Installs new issuers that are not currently bundled with Lemur.
|
|
||||||
#
|
|
||||||
# :param settings:
|
|
||||||
# :return:
|
|
||||||
# """
|
|
||||||
# from lemur.issuers import register
|
|
||||||
# # entry_points={
|
|
||||||
# # 'lemur.issuers': [
|
|
||||||
# # 'verisign = lemur_issuers.issuers:VerisignPlugin'
|
|
||||||
# # ],
|
|
||||||
# # },
|
|
||||||
# installed_apps = list(settings.INSTALLED_APPS)
|
|
||||||
# for ep in pkg_resources.iter_entry_points('lemur.apps'):
|
|
||||||
# try:
|
|
||||||
# issuer = ep.load()
|
|
||||||
# except Exception:
|
|
||||||
# import sys
|
|
||||||
# import traceback
|
|
||||||
#
|
|
||||||
# sys.stderr.write("Failed to load app %r:\n%s\n" % (ep.name, traceback.format_exc()))
|
|
||||||
# else:
|
|
||||||
# installed_apps.append(ep.module_name)
|
|
||||||
# settings.INSTALLED_APPS = tuple(installed_apps)
|
|
||||||
#
|
|
||||||
# for ep in pkg_resources.iter_entry_points('lemur.issuers'):
|
|
||||||
# try:
|
|
||||||
# issuer = ep.load()
|
|
||||||
# except Exception:
|
|
||||||
# import sys
|
|
||||||
# import traceback
|
|
||||||
#
|
|
||||||
# sys.stderr.write("Failed to load issuer %r:\n%s\n" % (ep.name, traceback.format_exc()))
|
|
||||||
# else:
|
|
||||||
# register(issuer)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateUser(Command):
|
class CreateUser(Command):
|
||||||
"""
|
"""
|
||||||
This command allows for the creation of a new user within Lemur
|
This command allows for the creation of a new user within Lemur
|
||||||
|
@ -492,7 +381,84 @@ def create_config(config_path=None):
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w') as f:
|
||||||
f.write(config)
|
f.write(config)
|
||||||
|
|
||||||
sys.stdout.write("Created a new configuration file {0}\n".format(config_path))
|
sys.stdout.write("[+] Created a new configuration file {0}\n".format(config_path))
|
||||||
|
|
||||||
|
|
||||||
|
@manager.command
|
||||||
|
def lock(path=None):
|
||||||
|
"""
|
||||||
|
Encrypts a given path. This directory can be used to store secrets needed for normal
|
||||||
|
Lemur operation. This is especially useful for storing secrets needed for communication
|
||||||
|
with third parties (e.g. external certificate authorities).
|
||||||
|
|
||||||
|
Lemur does not assume anything about the contents of the directory and will attempt to
|
||||||
|
encrypt all files contained within. Currently this has only been tested against plain
|
||||||
|
text files.
|
||||||
|
|
||||||
|
Path defaults ~/.lemur/keys
|
||||||
|
|
||||||
|
:param: path
|
||||||
|
"""
|
||||||
|
if not path:
|
||||||
|
path = os.path.expanduser('~/.lemur/keys')
|
||||||
|
|
||||||
|
dest_dir = os.path.join(path, "encrypted")
|
||||||
|
sys.stdout.write("[!] Generating a new key...\n")
|
||||||
|
|
||||||
|
key = Fernet.generate_key()
|
||||||
|
|
||||||
|
if not os.path.exists(dest_dir):
|
||||||
|
sys.stdout.write("[+] Creating encryption directory: {0}\n".format(dest_dir))
|
||||||
|
os.makedirs(dest_dir)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(os.path.join(path, 'decrypted')):
|
||||||
|
for f in files:
|
||||||
|
source = os.path.join(root, f)
|
||||||
|
dest = os.path.join(dest_dir, f + ".enc")
|
||||||
|
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
||||||
|
f = Fernet(key)
|
||||||
|
data = f.encrypt(in_file.read())
|
||||||
|
out_file.write(data)
|
||||||
|
sys.stdout.write("[+] Writing file: {0} Source: {1}\n".format(dest, source))
|
||||||
|
|
||||||
|
sys.stdout.write("[+] Keys have been encrypted with key {0}\n".format(key))
|
||||||
|
|
||||||
|
|
||||||
|
@manager.command
|
||||||
|
def unlock(path=None):
|
||||||
|
"""
|
||||||
|
Decrypts all of the files in a given directory with provided password.
|
||||||
|
This is most commonly used during the startup sequence of Lemur
|
||||||
|
allowing it to go from source code to something that can communicate
|
||||||
|
with external services.
|
||||||
|
|
||||||
|
Path defaults ~/.lemur/keys
|
||||||
|
|
||||||
|
:param: path
|
||||||
|
"""
|
||||||
|
key = prompt_pass("[!] Please enter the encryption password")
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
path = os.path.expanduser('~/.lemur/keys')
|
||||||
|
|
||||||
|
dest_dir = os.path.join(path, "decrypted")
|
||||||
|
source_dir = os.path.join(path, "encrypted")
|
||||||
|
|
||||||
|
if not os.path.exists(dest_dir):
|
||||||
|
sys.stdout.write("[+] Creating decryption directory: {0}\n".format(dest_dir))
|
||||||
|
os.makedirs(dest_dir)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(source_dir):
|
||||||
|
for f in files:
|
||||||
|
source = os.path.join(source_dir, f)
|
||||||
|
dest = os.path.join(dest_dir, ".".join(f.split(".")[:-1]))
|
||||||
|
with open(source, 'rb') as in_file, open(dest, 'wb') as out_file:
|
||||||
|
f = Fernet(key)
|
||||||
|
data = f.decrypt(in_file.read())
|
||||||
|
out_file.write(data)
|
||||||
|
sys.stdout.write("[+] Writing file: {0} Source: {1}\n".format(dest, source))
|
||||||
|
|
||||||
|
sys.stdout.write("[+] Keys have been unencrypted!\n")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from lemur.plugins.base import * # NOQA
|
||||||
|
from lemur.plugins.bases import * # NOQA
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.base
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
|
from lemur.plugins.base.manager import PluginManager
|
||||||
|
from lemur.plugins.base.v1 import * # NOQA
|
||||||
|
|
||||||
|
plugins = PluginManager()
|
||||||
|
register = plugins.register
|
||||||
|
unregister = plugins.unregister
|
|
@ -0,0 +1,59 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.base.manager
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson (kglisson@netflix.com)
|
||||||
|
"""
|
||||||
|
from flask import current_app
|
||||||
|
from lemur.common.managers import InstanceManager
|
||||||
|
|
||||||
|
|
||||||
|
# inspired by https://github.com/getsentry/sentry
|
||||||
|
class PluginManager(InstanceManager):
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.all())
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return sum(1 for i in self.all())
|
||||||
|
|
||||||
|
def all(self, version=1):
|
||||||
|
for plugin in sorted(super(PluginManager, self).all(), key=lambda x: x.get_title()):
|
||||||
|
if not plugin.is_enabled():
|
||||||
|
continue
|
||||||
|
if version is not None and plugin.__version__ != version:
|
||||||
|
continue
|
||||||
|
yield plugin
|
||||||
|
|
||||||
|
def get(self, slug):
|
||||||
|
for plugin in self.all(version=1):
|
||||||
|
if plugin.slug == slug:
|
||||||
|
return plugin
|
||||||
|
for plugin in self.all(version=2):
|
||||||
|
if plugin.slug == slug:
|
||||||
|
return plugin
|
||||||
|
raise KeyError(slug)
|
||||||
|
|
||||||
|
def first(self, func_name, *args, **kwargs):
|
||||||
|
version = kwargs.pop('version', 1)
|
||||||
|
for plugin in self.all(version=version):
|
||||||
|
try:
|
||||||
|
result = getattr(plugin, func_name)(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error('Error processing %s() on %r: %s', func_name, plugin.__class__, e, extra={
|
||||||
|
'func_arg': args,
|
||||||
|
'func_kwargs': kwargs,
|
||||||
|
}, exc_info=True)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
|
||||||
|
def register(self, cls):
|
||||||
|
self.add('%s.%s' % (cls.__module__, cls.__name__))
|
||||||
|
return cls
|
||||||
|
|
||||||
|
def unregister(self, cls):
|
||||||
|
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
||||||
|
return cls
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.base.v1
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from threading import local
|
||||||
|
|
||||||
|
# stolen from https://github.com/getsentry/sentry/
|
||||||
|
class PluginMount(type):
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
new_cls = type.__new__(cls, name, bases, attrs)
|
||||||
|
if IPlugin in bases:
|
||||||
|
return new_cls
|
||||||
|
if new_cls.title is None:
|
||||||
|
new_cls.title = new_cls.__name__
|
||||||
|
if not new_cls.slug:
|
||||||
|
new_cls.slug = new_cls.title.replace(' ', '-').lower()
|
||||||
|
return new_cls
|
||||||
|
|
||||||
|
|
||||||
|
class IPlugin(local):
|
||||||
|
"""
|
||||||
|
Plugin interface. Should not be inherited from directly.
|
||||||
|
A plugin should be treated as if it were a singleton. The owner does not
|
||||||
|
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||||
|
it will happen, or happen more than once.
|
||||||
|
>>> from lemur.plugins import Plugin
|
||||||
|
>>>
|
||||||
|
>>> class MyPlugin(Plugin):
|
||||||
|
>>> def get_title(self):
|
||||||
|
>>> return 'My Plugin'
|
||||||
|
As a general rule all inherited methods should allow ``**kwargs`` to ensure
|
||||||
|
ease of future compatibility.
|
||||||
|
"""
|
||||||
|
# Generic plugin information
|
||||||
|
title = None
|
||||||
|
slug = None
|
||||||
|
description = None
|
||||||
|
version = None
|
||||||
|
author = None
|
||||||
|
author_url = None
|
||||||
|
resource_links = ()
|
||||||
|
|
||||||
|
# Configuration specifics
|
||||||
|
conf_key = None
|
||||||
|
conf_title = None
|
||||||
|
|
||||||
|
# Global enabled state
|
||||||
|
enabled = True
|
||||||
|
can_disable = True
|
||||||
|
|
||||||
|
def is_enabled(self, project=None):
|
||||||
|
"""
|
||||||
|
Returns a boolean representing if this plugin is enabled.
|
||||||
|
If ``project`` is passed, it will limit the scope to that project.
|
||||||
|
>>> plugin.is_enabled()
|
||||||
|
"""
|
||||||
|
if not self.enabled:
|
||||||
|
return False
|
||||||
|
if not self.can_disable:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_conf_key(self):
|
||||||
|
"""
|
||||||
|
Returns a string representing the configuration keyspace prefix for this plugin.
|
||||||
|
"""
|
||||||
|
if not self.conf_key:
|
||||||
|
self.conf_key = self.get_conf_title().lower().replace(' ', '_')
|
||||||
|
return self.conf_key
|
||||||
|
|
||||||
|
def get_conf_title(self):
|
||||||
|
"""
|
||||||
|
Returns a string representing the title to be shown on the configuration page.
|
||||||
|
"""
|
||||||
|
return self.conf_title or self.get_title()
|
||||||
|
|
||||||
|
def get_title(self):
|
||||||
|
"""
|
||||||
|
Returns the general title for this plugin.
|
||||||
|
>>> plugin.get_title()
|
||||||
|
"""
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
"""
|
||||||
|
Returns the description for this plugin. This is shown on the plugin configuration
|
||||||
|
page.
|
||||||
|
>>> plugin.get_description()
|
||||||
|
"""
|
||||||
|
return self.description
|
||||||
|
|
||||||
|
def get_resource_links(self):
|
||||||
|
"""
|
||||||
|
Returns a list of tuples pointing to various resources for this plugin.
|
||||||
|
>>> def get_resource_links(self):
|
||||||
|
>>> return [
|
||||||
|
>>> ('Documentation', 'http://sentry.readthedocs.org'),
|
||||||
|
>>> ('Bug Tracker', 'https://github.com/getsentry/sentry/issues'),
|
||||||
|
>>> ('Source', 'https://github.com/getsentry/sentry'),
|
||||||
|
>>> ]
|
||||||
|
"""
|
||||||
|
return self.resource_links
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(IPlugin):
|
||||||
|
"""
|
||||||
|
A plugin should be treated as if it were a singleton. The owner does not
|
||||||
|
control when or how the plugin gets instantiated, nor is it guaranteed that
|
||||||
|
it will happen, or happen more than once.
|
||||||
|
"""
|
||||||
|
__version__ = 1
|
||||||
|
__metaclass__ = PluginMount
|
|
@ -0,0 +1,2 @@
|
||||||
|
from .destination import DestinationPlugin # NOQA
|
||||||
|
from .issuer import IssuerPlugin # NOQA
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.bases.destination
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
class DestinationPlugin(Plugin):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,32 +1,21 @@
|
||||||
"""
|
"""
|
||||||
.. module: authority
|
.. module: lemur.bases.issuer
|
||||||
:platform: Unix
|
:platform: Unix
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import current_app
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
class IssuerPlugin(Plugin):
|
||||||
class Issuer(object):
|
|
||||||
"""
|
"""
|
||||||
This is the base class from which all of the supported
|
This is the base class from which all of the supported
|
||||||
issuers will inherit from.
|
issuers will inherit from.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.dry_run = current_app.config.get('DRY_RUN')
|
|
||||||
|
|
||||||
def create_certificate(self):
|
def create_certificate(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def create_authority(self):
|
def create_authority(self):
|
||||||
raise NotImplementedError
|
raise NotImplemented
|
||||||
|
|
||||||
def get_authorities(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def get_csr_config(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
|
@ -18,10 +18,8 @@ from requests.adapters import HTTPAdapter
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.exceptions import LemurException
|
from lemur.exceptions import LemurException
|
||||||
from lemur.common.services.issuers.issuer import Issuer
|
from lemur.plugins.bases import IssuerPlugin
|
||||||
|
from lemur.plugins import lemur_cloudca as cloudca
|
||||||
from lemur.common.services.issuers.plugins import cloudca
|
|
||||||
|
|
||||||
|
|
||||||
from lemur.authorities import service as authority_service
|
from lemur.authorities import service as authority_service
|
||||||
|
|
||||||
|
@ -144,7 +142,7 @@ def get_auth_data(ca_name):
|
||||||
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
|
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
|
||||||
|
|
||||||
|
|
||||||
class CloudCA(Issuer):
|
class CloudCAPlugin(IssuerPlugin):
|
||||||
title = 'CloudCA'
|
title = 'CloudCA'
|
||||||
slug = 'cloudca'
|
slug = 'cloudca'
|
||||||
description = 'Enables the creation of certificates from the cloudca API.'
|
description = 'Enables the creation of certificates from the cloudca API.'
|
||||||
|
@ -164,7 +162,7 @@ class CloudCA(Issuer):
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
||||||
|
|
||||||
super(CloudCA, self).__init__(*args, **kwargs)
|
super(CloudCAPlugin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def create_authority(self, options):
|
def create_authority(self, options):
|
||||||
"""
|
"""
|
||||||
|
@ -261,15 +259,6 @@ class CloudCA(Issuer):
|
||||||
|
|
||||||
return cert, "".join(intermediates),
|
return cert, "".join(intermediates),
|
||||||
|
|
||||||
def get_csr_config(self, issuer_options):
|
|
||||||
"""
|
|
||||||
Get a valid CSR for use with CloudCA
|
|
||||||
|
|
||||||
:param issuer_options:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return cloudca.constants.CSR_CONFIG.format(**issuer_options)
|
|
||||||
|
|
||||||
def random(self, length=10):
|
def random(self, length=10):
|
||||||
"""
|
"""
|
||||||
Uses CloudCA as a decent source of randomness.
|
Uses CloudCA as a decent source of randomness.
|
||||||
|
@ -317,9 +306,6 @@ class CloudCA(Issuer):
|
||||||
:param data:
|
:param data:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if self.dry_run:
|
|
||||||
endpoint += '?dry_run=1'
|
|
||||||
|
|
||||||
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
|
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
|
||||||
|
|
||||||
# we set a low timeout, if cloudca is down it shouldn't bring down
|
# we set a low timeout, if cloudca is down it shouldn't bring down
|
||||||
|
@ -334,13 +320,6 @@ class CloudCA(Issuer):
|
||||||
:param endpoint:
|
:param endpoint:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if self.dry_run:
|
|
||||||
endpoint += '?dry_run=1'
|
|
||||||
|
|
||||||
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
||||||
return process_response(response)
|
return process_response(response)
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
return CloudCA()
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
VERISIGN_INTERMEDIATE = """-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFFTCCA/2gAwIBAgIQKC4nkXkzkuQo8iGnTsk3rjANBgkqhkiG9w0BAQsFADCB
|
||||||
|
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
||||||
|
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
|
||||||
|
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
||||||
|
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
||||||
|
aG9yaXR5IC0gRzMwHhcNMTMxMDMxMDAwMDAwWhcNMjMxMDMwMjM1OTU5WjB+MQsw
|
||||||
|
CQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNV
|
||||||
|
BAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVjIENs
|
||||||
|
YXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||||
|
AQ8AMIIBCgKCAQEAstgFyhx0LbUXVjnFSlIJluhL2AzxaJ+aQihiw6UwU35VEYJb
|
||||||
|
A3oNL+F5BMm0lncZgQGUWfm893qZJ4Itt4PdWid/sgN6nFMl6UgfRk/InSn4vnlW
|
||||||
|
9vf92Tpo2otLgjNBEsPIPMzWlnqEIRoiBAMnF4scaGGTDw5RgDMdtLXO637QYqzu
|
||||||
|
s3sBdO9pNevK1T2p7peYyo2qRA4lmUoVlqTObQJUHypqJuIGOmNIrLRM0XWTUP8T
|
||||||
|
L9ba4cYY9Z/JJV3zADreJk20KQnNDz0jbxZKgRb78oMQw7jW2FUyPfG9D72MUpVK
|
||||||
|
Fpd6UiFjdS8W+cRmvvW1Cdj/JwDNRHxvSz+w9wIDAQABo4IBQDCCATwwHQYDVR0O
|
||||||
|
BBYEFF9gz2GQVd+EQxSKYCqy9Xr0QxjvMBIGA1UdEwEB/wQIMAYBAf8CAQAwawYD
|
||||||
|
VR0gBGQwYjBgBgpghkgBhvhFAQc2MFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cu
|
||||||
|
c3ltYXV0aC5jb20vY3BzMCgGCCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0
|
||||||
|
aC5jb20vcnBhMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNvbS9w
|
||||||
|
Y2EzLWczLmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwxGjAYBgNV
|
||||||
|
BAMTEVN5bWFudGVjUEtJLTEtNTM0MC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcw
|
||||||
|
AYYSaHR0cDovL3Muc3ltY2QuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBbF1K+1lZ7
|
||||||
|
9Pc0CUuWysf2IdBpgO/nmhnoJOJ/2S9h3RPrWmXk4WqQy04q6YoW51KN9kMbRwUN
|
||||||
|
gKOomv4p07wdKNWlStRxPA91xQtzPwBIZXkNq2oeJQzAAt5mrL1LBmuaV4oqgX5n
|
||||||
|
m7pSYHPEFfe7wVDJCKW6V0o6GxBzHOF7tpQDS65RsIJAOloknO4NWF2uuil6yjOe
|
||||||
|
soHCL47BJ89A8AShP/U3wsr8rFNtqVNpT+F2ZAwlgak3A/I5czTSwXx4GByoaxbn
|
||||||
|
5+CdKa/Y5Gk5eZVpuXtcXQGc1PfzSEUTZJXXCm5y2kMiJG8+WnDcwJLgLeVX+OQr
|
||||||
|
J+71/xuzAYN6
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
"""
|
||||||
|
|
||||||
|
VERISIGN_ROOT = """-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
|
||||||
|
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
|
||||||
|
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
|
||||||
|
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
|
||||||
|
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
||||||
|
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
|
||||||
|
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
|
||||||
|
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
|
||||||
|
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
|
||||||
|
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
|
||||||
|
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
|
||||||
|
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
|
||||||
|
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
|
||||||
|
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
|
||||||
|
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
|
||||||
|
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
|
||||||
|
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
|
||||||
|
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
|
||||||
|
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
|
||||||
|
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
|
||||||
|
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
||||||
|
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
"""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
.. module: lemur.common.services.issuers.plugins.verisign.verisign
|
.. module: lemur.plugins.lemur_verisign.verisign
|
||||||
:platform: Unix
|
:platform: Unix
|
||||||
:synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
|
:synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
@ -13,10 +13,9 @@ import xmltodict
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.common.services.issuers.issuer import Issuer
|
from lemur.plugins.bases import IssuerPlugin
|
||||||
from lemur.common.services.issuers.plugins import verisign
|
from lemur.plugins import lemur_verisign as verisign
|
||||||
|
from lemur.plugins.lemur_verisign import constants
|
||||||
from lemur.certificates.exceptions import InsufficientDomains
|
|
||||||
|
|
||||||
|
|
||||||
# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
|
# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
|
||||||
|
@ -54,25 +53,11 @@ VERISIGN_ERRORS = {
|
||||||
"0x6013": "only supports DSA keys with (2048, 256) as the bit lengths of the prime parameter pair (p, q), other DSA key sizes will get this error",
|
"0x6013": "only supports DSA keys with (2048, 256) as the bit lengths of the prime parameter pair (p, q), other DSA key sizes will get this error",
|
||||||
"0x600d": "RSA key size < 2A048",
|
"0x600d": "RSA key size < 2A048",
|
||||||
"0x4828": "Verisign certificates can be at most two years in length",
|
"0x4828": "Verisign certificates can be at most two years in length",
|
||||||
"0x3043": "Certificates must have a validity of at least 1 day"
|
"0x3043": "Certificates must have a validity of at least 1 day",
|
||||||
|
"0x950b": "CSR: Invalid State",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Verisign(Issuer):
|
|
||||||
title = 'VeriSign'
|
|
||||||
slug = 'verisign'
|
|
||||||
description = 'Enables the creation of certificates by the VICE2.0 verisign API.'
|
|
||||||
version = verisign.VERSION
|
|
||||||
|
|
||||||
author = 'Kevin Glisson'
|
|
||||||
author_url = 'https://github.com/netflix/lemur'
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.session = requests.Session()
|
|
||||||
self.session.cert = current_app.config.get('VERISIGN_PEM_PATH')
|
|
||||||
super(Verisign, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def handle_response(content):
|
def handle_response(content):
|
||||||
"""
|
"""
|
||||||
Helper function that helps with parsing responses from the Verisign API.
|
Helper function that helps with parsing responses from the Verisign API.
|
||||||
|
@ -89,6 +74,21 @@ class Verisign(Issuer):
|
||||||
raise Exception(VERISIGN_ERRORS[status_code])
|
raise Exception(VERISIGN_ERRORS[status_code])
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
class VerisignPlugin(IssuerPlugin):
|
||||||
|
title = 'VeriSign'
|
||||||
|
slug = 'verisign'
|
||||||
|
description = 'Enables the creation of certificates by the VICE2.0 verisign API.'
|
||||||
|
version = verisign.VERSION
|
||||||
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.session = requests.Session()
|
||||||
|
self.session.cert = current_app.config.get('VERISIGN_PEM_PATH')
|
||||||
|
super(VerisignPlugin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def create_certificate(self, csr, issuer_options):
|
def create_certificate(self, csr, issuer_options):
|
||||||
"""
|
"""
|
||||||
Creates a Verisign certificate.
|
Creates a Verisign certificate.
|
||||||
|
@ -126,42 +126,8 @@ class Verisign(Issuer):
|
||||||
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
||||||
|
|
||||||
response = self.session.post(url, data=data)
|
response = self.session.post(url, data=data)
|
||||||
cert = self.handle_response(response.content)['Response']['Certificate']
|
cert = handle_response(response.content)['Response']['Certificate']
|
||||||
return cert, verisign.constants.VERISIGN_INTERMEDIATE,
|
return cert, constants.VERISIGN_INTERMEDIATE,
|
||||||
|
|
||||||
def get_csr_config(self, issuer_options):
|
|
||||||
"""
|
|
||||||
Used to generate a valid CSR for the given Certificate Authority.
|
|
||||||
|
|
||||||
:param issuer_options:
|
|
||||||
:return: :raise InsufficientDomains:
|
|
||||||
"""
|
|
||||||
domains = []
|
|
||||||
|
|
||||||
if issuer_options.get('commonName'):
|
|
||||||
domains.append(issuer_options.get('commonName'))
|
|
||||||
|
|
||||||
if issuer_options.get('extensions'):
|
|
||||||
for n in issuer_options['extensions']['subAltNames']['names']:
|
|
||||||
if n['value']:
|
|
||||||
domains.append(n['value'])
|
|
||||||
|
|
||||||
is_san_comment = "#"
|
|
||||||
|
|
||||||
dns_lines = []
|
|
||||||
if len(domains) < 1:
|
|
||||||
raise InsufficientDomains
|
|
||||||
|
|
||||||
elif len(domains) > 1:
|
|
||||||
is_san_comment = ""
|
|
||||||
for domain_line in list(set(domains)):
|
|
||||||
dns_lines.append("DNS.{} = {}".format(len(dns_lines) + 1, domain_line))
|
|
||||||
|
|
||||||
return verisign.constants.CSR_CONFIG.format(
|
|
||||||
is_san_comment=is_san_comment,
|
|
||||||
OU=issuer_options.get('organizationalUnit', 'Operations'),
|
|
||||||
DNS=domains,
|
|
||||||
DNS_LINES="\n".join(dns_lines))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_authority(options):
|
def create_authority(options):
|
||||||
|
@ -173,7 +139,7 @@ class Verisign(Issuer):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
role = {'username': '', 'password': '', 'name': 'verisign'}
|
role = {'username': '', 'password': '', 'name': 'verisign'}
|
||||||
return verisign.constants.VERISIGN_ROOT, "", [role]
|
return constants.VERISIGN_ROOT, "", [role]
|
||||||
|
|
||||||
def get_available_units(self):
|
def get_available_units(self):
|
||||||
"""
|
"""
|
||||||
|
@ -184,11 +150,5 @@ class Verisign(Issuer):
|
||||||
"""
|
"""
|
||||||
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
||||||
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
||||||
return self.handle_response(response.content)['Response']['Order']
|
return handle_response(response.content)['Response']['Order']
|
||||||
|
|
||||||
def get_authorities(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
return Verisign()
|
|
|
@ -29,9 +29,9 @@
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Roles'"> <!--filter="{ 'select': 'role' }" filter-data="roleService.getRoleDropDown()">-->
|
<td data-title="'Roles'"> <!--filter="{ 'select': 'role' }" filter-data="roleService.getRoleDropDown()">-->
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button ng-repeat="role in authority.roles" class="btn btn-sm btn-danger">
|
<a href="#/roles/{{ role.id }}/edit" ng-repeat="role in authority.roles" class="btn btn-sm btn-danger">
|
||||||
{{ role.name }}
|
{{ role.name }}
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
State
|
State
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="state" ng-model="certificate.state" placeholder="State" class="form-control" ng-init="certificate.state = 'CA'" required/>
|
<input name="state" ng-model="certificate.state" placeholder="State" class="form-control" ng-init="certificate.state = 'California'" required/>
|
||||||
<p ng-show="dnForm.state.$invalid && !dnForm.state.$pristine" class="help-block">You must enter a state</p>
|
<p ng-show="dnForm.state.$invalid && !dnForm.state.$pristine" class="help-block">You must enter a state</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,40 +20,20 @@
|
||||||
<p ng-show="trackingForm.description.$invalid && !trackingForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
<p ng-show="trackingForm.description.$invalid && !trackingForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group"
|
||||||
|
ng-class="{'has-error': trackingForm.selectedAuthority.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.selectedAuthority.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Certificate Authority
|
Certificate Authority
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group col-sm-12">
|
<div class="input-group col-sm-12">
|
||||||
<input tooltip="If you are unsure which authority you need; you most likely want to use 'verisign'" type="text" ng-model="certificate.selectedAuthority" placeholder="Authority Name" typeahead-on-select="certificate.attachAuthority($item)"
|
<input name="selectedAuthority" tooltip="If you are unsure which authority you need; you most likely want to use 'verisign'" type="text" ng-model="certificate.selectedAuthority" placeholder="Authority Name" typeahead-on-select="certificate.attachAuthority($item)"
|
||||||
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
||||||
class="form-control" typeahead-wait-ms="100" typeahead-template-url="angular/authorities/authority/select.tpl.html">
|
class="form-control" typeahead-wait-ms="100" typeahead-template-url="angular/authorities/authority/select.tpl.html" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="certificate.authority.name == 'verisign'">
|
<div ng-show="certificate.authority" class="form-group">
|
||||||
<div class="form-group" ng-class="{'has-error': !certificate.extensions.subAltNames.names.length&&!certificate.extensions.subAltNames.names , 'has-success': certificate.extensions.subAltNames.names||certificate.subAltValue}">
|
|
||||||
<label class="control-label col-sm-2">Domain(s)</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group input-group-option">
|
|
||||||
<input tooltip="If you request multiple domains a SAN certificate will be created" type="text" name="domain" ng-model="certificate.subAltValue" placeholder="Domain..." class="form-control" required/>
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-default" ng-click="certificate.attachSubAltName()">Add</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<table ng-show="certificate.extensions.subAltNames.names" class="table">
|
|
||||||
<tr ng-repeat="domain in certificate.extensions.subAltNames.names track by $index">
|
|
||||||
<td>{{ domain.value }}</td>
|
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="certificate.removeSubAltName($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ng-show="certificate.authority && certificate.authority.name != 'verisign'" class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Certificate Template
|
Certificate Template
|
||||||
</label>
|
</label>
|
||||||
|
@ -61,19 +41,19 @@
|
||||||
<select class="form-control" ng-change="certificate.useTemplate()" name="certificateTemplate" ng-model="certificate.template" ng-options="template.name for template in templates"></select>
|
<select class="form-control" ng-change="certificate.useTemplate()" name="certificateTemplate" ng-model="certificate.template" ng-options="template.name for template in templates"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="certificate.authority && certificate.authority.name != 'verisign'" class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commonName.$dirty}">
|
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commonName.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Common Name
|
Common Name
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="commonName" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" required/>
|
<input name="commonName" tooltip="If you need a certificate with multiple domains enter your primary domain here and the rest under 'Subject Alternate Names' in the options panel" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" required/>
|
||||||
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2" tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
||||||
Validity Range
|
Validity Range <span class="glyphicon glyphicon-question-sign"></span>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -77,8 +77,8 @@
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<strong>San</strong>
|
<strong>San</strong>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
<i class="glyphicon glyphicon-ok" ng-show="certificate.san == 'true'"></i>
|
<i class="glyphicon glyphicon-ok" ng-show="certificate.san"></i>
|
||||||
<i class="glyphicon glyphicon-remove" ng-show="certificate.san == 'false'"></i>
|
<i class="glyphicon glyphicon-remove" ng-show="!certificate.san"></i>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
|
|
|
@ -45,7 +45,6 @@ def app():
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture(scope="session")
|
@pytest.yield_fixture(scope="session")
|
||||||
def db(app, request):
|
def db(app, request):
|
||||||
_db.drop_all()
|
_db.drop_all()
|
||||||
|
@ -72,7 +71,6 @@ def session(db, request):
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture(scope="function")
|
@pytest.yield_fixture(scope="function")
|
||||||
def client(app, session):
|
def client(app, session, client):
|
||||||
with app.test_client() as client:
|
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@ def test_account_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_account_post(client):
|
def test_account_post(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), {}).status_code == 405
|
assert client.post(api.url_for(Accounts, account_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_account_put(client):
|
def test_account_put(client):
|
||||||
assert client.put(api.url_for(Accounts, account_id=1), {}).status_code == 401
|
assert client.put(api.url_for(Accounts, account_id=1), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_account_delete(client):
|
def test_account_delete(client):
|
||||||
|
@ -34,7 +34,7 @@ def test_account_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_account_patch(client):
|
def test_account_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(Accounts, account_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
|
@ -45,7 +45,7 @@ def test_auth_account_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_post_(client):
|
def test_auth_account_post_(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Accounts, account_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_put(client):
|
def test_auth_account_put(client):
|
||||||
|
@ -57,7 +57,7 @@ def test_auth_account_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_patch(client):
|
def test_auth_account_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Accounts, account_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_ADMIN_HEADER_TOKEN = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
|
@ -68,7 +68,7 @@ def test_admin_account_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_post(client):
|
def test_admin_account_post(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Accounts, account_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_put(client):
|
def test_admin_account_put(client):
|
||||||
|
@ -80,7 +80,7 @@ def test_admin_account_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_patch(client):
|
def test_admin_account_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Accounts, account_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_get(client):
|
def test_accounts_get(client):
|
||||||
|
@ -88,11 +88,11 @@ def test_accounts_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_post(client):
|
def test_accounts_post(client):
|
||||||
assert client.post(api.url_for(AccountsList), {}).status_code == 401
|
assert client.post(api.url_for(AccountsList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_put(client):
|
def test_accounts_put(client):
|
||||||
assert client.put(api.url_for(AccountsList), {}).status_code == 405
|
assert client.put(api.url_for(AccountsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_delete(client):
|
def test_accounts_delete(client):
|
||||||
|
@ -100,7 +100,7 @@ def test_accounts_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_patch(client):
|
def test_accounts_patch(client):
|
||||||
assert client.patch(api.url_for(AccountsList), {}).status_code == 405
|
assert client.patch(api.url_for(AccountsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_accounts_get(client):
|
def test_auth_accounts_get(client):
|
||||||
|
@ -108,7 +108,7 @@ def test_auth_accounts_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_accounts_post(client):
|
def test_auth_accounts_post(client):
|
||||||
assert client.post(api.url_for(AccountsList), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
assert client.post(api.url_for(AccountsList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_admin_accounts_get(client):
|
def test_admin_accounts_get(client):
|
||||||
|
|
|
@ -16,11 +16,11 @@ def test_authority_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authority_post(client):
|
def test_authority_post(client):
|
||||||
assert client.post(api.url_for(Authorities, authority_id=1), {}).status_code == 405
|
assert client.post(api.url_for(Authorities, authority_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authority_put(client):
|
def test_authority_put(client):
|
||||||
assert client.put(api.url_for(Authorities, authority_id=1), {}).status_code == 401
|
assert client.put(api.url_for(Authorities, authority_id=1), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_authority_delete(client):
|
def test_authority_delete(client):
|
||||||
|
@ -28,7 +28,7 @@ def test_authority_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authority_patch(client):
|
def test_authority_patch(client):
|
||||||
assert client.patch(api.url_for(Authorities, authority_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(Authorities, authority_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authorities_get(client):
|
def test_authorities_get(client):
|
||||||
|
@ -36,11 +36,11 @@ def test_authorities_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authorities_post(client):
|
def test_authorities_post(client):
|
||||||
assert client.post(api.url_for(AuthoritiesList), {}).status_code == 401
|
assert client.post(api.url_for(AuthoritiesList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_authorities_put(client):
|
def test_authorities_put(client):
|
||||||
assert client.put(api.url_for(AuthoritiesList), {}).status_code == 405
|
assert client.put(api.url_for(AuthoritiesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authorities_delete(client):
|
def test_authorities_delete(client):
|
||||||
|
@ -48,7 +48,7 @@ def test_authorities_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authorities_patch(client):
|
def test_authorities_patch(client):
|
||||||
assert client.patch(api.url_for(AuthoritiesList), {}).status_code == 405
|
assert client.patch(api.url_for(AuthoritiesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_authorities_get(client):
|
def test_certificate_authorities_get(client):
|
||||||
|
@ -56,11 +56,11 @@ def test_certificate_authorities_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_authorities_post(client):
|
def test_certificate_authorities_post(client):
|
||||||
assert client.post(api.url_for(AuthoritiesList), {}).status_code == 401
|
assert client.post(api.url_for(AuthoritiesList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_authorities_put(client):
|
def test_certificate_authorities_put(client):
|
||||||
assert client.put(api.url_for(AuthoritiesList), {}).status_code == 405
|
assert client.put(api.url_for(AuthoritiesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_authorities_delete(client):
|
def test_certificate_authorities_delete(client):
|
||||||
|
@ -68,7 +68,7 @@ def test_certificate_authorities_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_authorities_patch(client):
|
def test_certificate_authorities_patch(client):
|
||||||
assert client.patch(api.url_for(AuthoritiesList), {}).status_code == 405
|
assert client.patch(api.url_for(AuthoritiesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
|
@ -80,7 +80,7 @@ def test_auth_authority_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_post_(client):
|
def test_auth_authority_post_(client):
|
||||||
assert client.post(api.url_for(Authorities, authority_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Authorities, authority_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_put(client):
|
def test_auth_authority_put(client):
|
||||||
|
@ -92,7 +92,7 @@ def test_auth_authority_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_patch(client):
|
def test_auth_authority_patch(client):
|
||||||
assert client.patch(api.url_for(Authorities, authority_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Authorities, authority_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authorities_get(client):
|
def test_auth_authorities_get(client):
|
||||||
|
@ -100,7 +100,7 @@ def test_auth_authorities_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authorities_post(client):
|
def test_auth_authorities_post(client):
|
||||||
assert client.post(api.url_for(AuthoritiesList), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(AuthoritiesList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_authorities_get(client):
|
def test_auth_certificates_authorities_get(client):
|
||||||
|
@ -116,7 +116,7 @@ def test_admin_authority_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_post(client):
|
def test_admin_authority_post(client):
|
||||||
assert client.post(api.url_for(Authorities, authority_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Authorities, authority_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_put(client):
|
def test_admin_authority_put(client):
|
||||||
|
@ -136,11 +136,11 @@ def test_admin_authorities_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authorities_post(client):
|
def test_admin_authorities_post(client):
|
||||||
assert client.post(api.url_for(AuthoritiesList), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(AuthoritiesList), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authorities_put(client):
|
def test_admin_authorities_put(client):
|
||||||
assert client.put(api.url_for(AuthoritiesList), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(AuthoritiesList), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authorities_delete(client):
|
def test_admin_authorities_delete(client):
|
||||||
|
|
|
@ -1,18 +1,6 @@
|
||||||
import os
|
|
||||||
import pytest
|
import pytest
|
||||||
from mock import mock_open, patch
|
|
||||||
from lemur.certificates.views import *
|
from lemur.certificates.views import *
|
||||||
|
|
||||||
#def test_crud(session):
|
|
||||||
# role = create('role1')
|
|
||||||
# assert role.id > 0
|
|
||||||
#
|
|
||||||
# role = update(role.id, 'role_new', None, [])
|
|
||||||
# assert role.name == 'role_new'
|
|
||||||
# delete(role.id)
|
|
||||||
# assert get(role.id) == None
|
|
||||||
|
|
||||||
|
|
||||||
def test_valid_authority(session):
|
def test_valid_authority(session):
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|
||||||
|
@ -33,28 +21,26 @@ def test_private_key_str():
|
||||||
private_key_str('dfsdfsdf', 'test')
|
private_key_str('dfsdfsdf', 'test')
|
||||||
|
|
||||||
|
|
||||||
def test_create_csr():
|
def test_create_basic_csr():
|
||||||
from lemur.tests.certs import CSR_CONFIG
|
|
||||||
from lemur.certificates.service import create_csr
|
from lemur.certificates.service import create_csr
|
||||||
m = mock_open()
|
csr_config = dict(
|
||||||
with patch('lemur.certificates.service.open', m, create=True):
|
commonName=u'example.com',
|
||||||
path = create_csr(CSR_CONFIG)
|
organization=u'Example, Inc.',
|
||||||
assert path == ''
|
organizationalUnit=u'Operations',
|
||||||
|
country=u'US',
|
||||||
|
state=u'CA',
|
||||||
|
location=u'A place',
|
||||||
|
extensions=dict(subAltNames=[u'test.example.com', u'test2.example.com'])
|
||||||
|
)
|
||||||
|
csr, pem = create_csr(csr_config)
|
||||||
|
|
||||||
|
private_key = serialization.load_pem_private_key(pem, password=None, backend=default_backend())
|
||||||
|
csr = x509.load_pem_x509_csr(csr, default_backend())
|
||||||
|
for name in csr.subject:
|
||||||
|
assert name.value in csr_config.values()
|
||||||
|
|
||||||
|
|
||||||
def test_create_path():
|
def test_import_certificate():
|
||||||
assert 1 == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_ssl_pack():
|
|
||||||
assert 1 == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_ssl_path():
|
|
||||||
assert 1 == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_import_certificate(session):
|
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,20 +123,17 @@ def test_create_name():
|
||||||
True
|
True
|
||||||
) == 'SAN-example.com-ExampleInc-20150507-20150512'
|
) == 'SAN-example.com-ExampleInc-20150507-20150512'
|
||||||
|
|
||||||
def test_is_expired():
|
|
||||||
assert 1 == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_get(client):
|
def test_certificate_get(client):
|
||||||
assert client.get(api.url_for(Certificates, certificate_id=1)).status_code == 401
|
assert client.get(api.url_for(Certificates, certificate_id=1)).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_post(client):
|
def test_certificate_post(client):
|
||||||
assert client.post(api.url_for(Certificates, certificate_id=1), {}).status_code == 405
|
assert client.post(api.url_for(Certificates, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_put(client):
|
def test_certificate_put(client):
|
||||||
assert client.put(api.url_for(Certificates, certificate_id=1), {}).status_code == 401
|
assert client.put(api.url_for(Certificates, certificate_id=1), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_delete(client):
|
def test_certificate_delete(client):
|
||||||
|
@ -158,7 +141,7 @@ def test_certificate_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_patch(client):
|
def test_certificate_patch(client):
|
||||||
assert client.patch(api.url_for(Certificates, certificate_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(Certificates, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_get(client):
|
def test_certificates_get(client):
|
||||||
|
@ -166,11 +149,11 @@ def test_certificates_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_post(client):
|
def test_certificates_post(client):
|
||||||
assert client.post(api.url_for(CertificatesList), {}).status_code == 401
|
assert client.post(api.url_for(CertificatesList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_put(client):
|
def test_certificates_put(client):
|
||||||
assert client.put(api.url_for(CertificatesList), {}).status_code == 405
|
assert client.put(api.url_for(CertificatesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_delete(client):
|
def test_certificates_delete(client):
|
||||||
|
@ -178,7 +161,7 @@ def test_certificates_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_patch(client):
|
def test_certificates_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatesList), {}).status_code == 405
|
assert client.patch(api.url_for(CertificatesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_credentials_get(client):
|
def test_certificate_credentials_get(client):
|
||||||
|
@ -186,11 +169,11 @@ def test_certificate_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_credentials_post(client):
|
def test_certificate_credentials_post(client):
|
||||||
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), {}).status_code == 405
|
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_credentials_put(client):
|
def test_certificate_credentials_put(client):
|
||||||
assert client.put(api.url_for(CertificatePrivateKey, certificate_id=1), {}).status_code == 405
|
assert client.put(api.url_for(CertificatePrivateKey, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_credentials_delete(client):
|
def test_certificate_credentials_delete(client):
|
||||||
|
@ -198,7 +181,7 @@ def test_certificate_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_credentials_patch(client):
|
def test_certificate_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_upload_get(client):
|
def test_certificates_upload_get(client):
|
||||||
|
@ -206,11 +189,11 @@ def test_certificates_upload_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_upload_post(client):
|
def test_certificates_upload_post(client):
|
||||||
assert client.post(api.url_for(CertificatesUpload), {}).status_code == 401
|
assert client.post(api.url_for(CertificatesUpload), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_upload_put(client):
|
def test_certificates_upload_put(client):
|
||||||
assert client.put(api.url_for(CertificatesUpload), {}).status_code == 405
|
assert client.put(api.url_for(CertificatesUpload), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_upload_delete(client):
|
def test_certificates_upload_delete(client):
|
||||||
|
@ -218,7 +201,7 @@ def test_certificates_upload_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificates_upload_patch(client):
|
def test_certificates_upload_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatesUpload), {}).status_code == 405
|
assert client.patch(api.url_for(CertificatesUpload), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
|
@ -230,7 +213,7 @@ def test_auth_certificate_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_post_(client):
|
def test_auth_certificate_post_(client):
|
||||||
assert client.post(api.url_for(Certificates, certificate_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Certificates, certificate_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_put(client):
|
def test_auth_certificate_put(client):
|
||||||
|
@ -242,7 +225,7 @@ def test_auth_certificate_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_patch(client):
|
def test_auth_certificate_patch(client):
|
||||||
assert client.patch(api.url_for(Certificates, certificate_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Certificates, certificate_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_get(client):
|
def test_auth_certificates_get(client):
|
||||||
|
@ -250,7 +233,7 @@ def test_auth_certificates_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_post(client):
|
def test_auth_certificates_post(client):
|
||||||
assert client.post(api.url_for(CertificatesList), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(CertificatesList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_credentials_get(client):
|
def test_auth_certificate_credentials_get(client):
|
||||||
|
@ -258,11 +241,11 @@ def test_auth_certificate_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_credentials_post(client):
|
def test_auth_certificate_credentials_post(client):
|
||||||
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_credentials_put(client):
|
def test_auth_certificate_credentials_put(client):
|
||||||
assert client.put(api.url_for(CertificatePrivateKey, certificate_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_credentials_delete(client):
|
def test_auth_certificate_credentials_delete(client):
|
||||||
|
@ -270,7 +253,7 @@ def test_auth_certificate_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_credentials_patch(client):
|
def test_auth_certificate_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_upload_get(client):
|
def test_auth_certificates_upload_get(client):
|
||||||
|
@ -278,11 +261,11 @@ def test_auth_certificates_upload_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_upload_post(client):
|
def test_auth_certificates_upload_post(client):
|
||||||
assert client.post(api.url_for(CertificatesUpload), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(CertificatesUpload), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_upload_put(client):
|
def test_auth_certificates_upload_put(client):
|
||||||
assert client.put(api.url_for(CertificatesUpload), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(CertificatesUpload), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_upload_delete(client):
|
def test_auth_certificates_upload_delete(client):
|
||||||
|
@ -290,7 +273,7 @@ def test_auth_certificates_upload_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificates_upload_patch(client):
|
def test_auth_certificates_upload_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatesUpload), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(CertificatesUpload), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_ADMIN_HEADER_TOKEN = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
|
@ -302,7 +285,7 @@ def test_admin_certificate_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_post(client):
|
def test_admin_certificate_post(client):
|
||||||
assert client.post(api.url_for(Certificates, certificate_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Certificates, certificate_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_put(client):
|
def test_admin_certificate_put(client):
|
||||||
|
@ -314,7 +297,7 @@ def test_admin_certificate_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_patch(client):
|
def test_admin_certificate_patch(client):
|
||||||
assert client.patch(api.url_for(Certificates, certificate_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Certificates, certificate_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificates_get(client):
|
def test_admin_certificates_get(client):
|
||||||
|
@ -328,7 +311,7 @@ def test_admin_certificate_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_credentials_post(client):
|
def test_admin_certificate_credentials_post(client):
|
||||||
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_credentials_put(client):
|
def test_admin_certificate_credentials_put(client):
|
||||||
|
@ -340,5 +323,5 @@ def test_admin_certificate_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_certificate_credentials_patch(client):
|
def test_admin_certificate_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,11 @@ def test_domain_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_domain_post(client):
|
def test_domain_post(client):
|
||||||
assert client.post(api.url_for(Domains, domain_id=1), {}).status_code == 405
|
assert client.post(api.url_for(Domains, domain_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_domain_put(client):
|
def test_domain_put(client):
|
||||||
assert client.put(api.url_for(Domains, domain_id=1), {}).status_code == 405
|
assert client.put(api.url_for(Domains, domain_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_domain_delete(client):
|
def test_domain_delete(client):
|
||||||
|
@ -17,7 +17,7 @@ def test_domain_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_domain_patch(client):
|
def test_domain_patch(client):
|
||||||
assert client.patch(api.url_for(Domains, domain_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(Domains, domain_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
|
@ -28,7 +28,7 @@ def test_auth_domain_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_domain_post_(client):
|
def test_auth_domain_post_(client):
|
||||||
assert client.post(api.url_for(Domains, domain_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Domains, domain_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_domain_put(client):
|
def test_auth_domain_put(client):
|
||||||
|
@ -40,7 +40,7 @@ def test_auth_domain_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_domain_patch(client):
|
def test_auth_domain_patch(client):
|
||||||
assert client.patch(api.url_for(Domains, domain_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Domains, domain_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_ADMIN_HEADER_TOKEN = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
|
@ -51,7 +51,7 @@ def test_admin_domain_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_domain_post(client):
|
def test_admin_domain_post(client):
|
||||||
assert client.post(api.url_for(Domains, domain_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Domains, domain_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_domain_put(client):
|
def test_admin_domain_put(client):
|
||||||
|
@ -63,7 +63,7 @@ def test_admin_domain_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_domain_patch(client):
|
def test_admin_domain_patch(client):
|
||||||
assert client.patch(api.url_for(Domains, domain_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Domains, domain_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_domains_get(client):
|
def test_domains_get(client):
|
||||||
|
@ -71,11 +71,11 @@ def test_domains_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_domains_post(client):
|
def test_domains_post(client):
|
||||||
assert client.post(api.url_for(DomainsList), {}).status_code == 405
|
assert client.post(api.url_for(DomainsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_domains_put(client):
|
def test_domains_put(client):
|
||||||
assert client.put(api.url_for(DomainsList), {}).status_code == 405
|
assert client.put(api.url_for(DomainsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_domains_delete(client):
|
def test_domains_delete(client):
|
||||||
|
@ -83,7 +83,7 @@ def test_domains_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_domains_patch(client):
|
def test_domains_patch(client):
|
||||||
assert client.patch(api.url_for(DomainsList), {}).status_code == 405
|
assert client.patch(api.url_for(DomainsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_domains_get(client):
|
def test_auth_domains_get(client):
|
||||||
|
@ -101,11 +101,11 @@ def test_certificate_domains_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_domains_post(client):
|
def test_certificate_domains_post(client):
|
||||||
assert client.post(api.url_for(CertificateDomains, certificate_id=1), {}).status_code == 405
|
assert client.post(api.url_for(CertificateDomains, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_domains_put(client):
|
def test_certificate_domains_put(client):
|
||||||
assert client.put(api.url_for(CertificateDomains, certificate_id=1), {}).status_code == 405
|
assert client.put(api.url_for(CertificateDomains, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_domains_delete(client):
|
def test_certificate_domains_delete(client):
|
||||||
|
@ -113,7 +113,7 @@ def test_certificate_domains_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_certificate_domains_patch(client):
|
def test_certificate_domains_patch(client):
|
||||||
assert client.patch(api.url_for(CertificateDomains, certificate_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(CertificateDomains, certificate_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_certificate_domains_get(client):
|
def test_auth_certificate_domains_get(client):
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
KRPZPA&*!_~%dbnuzf153594
|
|
|
@ -1,38 +0,0 @@
|
||||||
|
|
||||||
# Configuration for standard CSR generation for Netflix
|
|
||||||
# Used for procuring VeriSign certificates
|
|
||||||
# Author: jachan
|
|
||||||
# Contact: cloudsecurity@netflix.com
|
|
||||||
|
|
||||||
[ req ]
|
|
||||||
# Use a 2048 bit private key
|
|
||||||
default_bits = 2048
|
|
||||||
default_keyfile = key.pem
|
|
||||||
prompt = no
|
|
||||||
encrypt_key = no
|
|
||||||
|
|
||||||
# base request
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
|
|
||||||
# extensions
|
|
||||||
# Uncomment the following line if you are requesting a SAN cert
|
|
||||||
#req_extensions = req_ext
|
|
||||||
|
|
||||||
# distinguished_name
|
|
||||||
[ req_distinguished_name ]
|
|
||||||
countryName = "US" # C=
|
|
||||||
stateOrProvinceName = "CALIFORNIA" # ST=
|
|
||||||
localityName = "Los Gatos" # L=
|
|
||||||
organizationName = "Netflix, Inc." # O=
|
|
||||||
organizationalUnitName = "Operations" # OU=
|
|
||||||
# This is the hostname/subject name on the certificate
|
|
||||||
commonName = "dfdsflkj.net" # CN=
|
|
||||||
|
|
||||||
[ req_ext ]
|
|
||||||
# Uncomment the following line if you are requesting a SAN cert
|
|
||||||
#subjectAltName = @alt_names
|
|
||||||
|
|
||||||
[alt_names]
|
|
||||||
# Put your SANs here
|
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEogIBAAKCAQEAvNudwW+UeQqkpY71MIdEg501AFlPKuOXG2xU8DZhvZS6dKv+
|
|
||||||
kDmIWdEqodDgkQiy0jyTgTwxwRqDSw96R6ZgrXefUoJJo66aCsosTBZtVaE85f1L
|
|
||||||
bj2+3U678c+rekUdkrnGcGCo6b8QtdvBpiDy2clneox8tSvmffAdcR1uCv/790/k
|
|
||||||
PzQ/djWDX9JcBRyDkcTJwYC0/ek7URvA/+MXmgUL13T+gWKqduaKuIBlFetonDjn
|
|
||||||
nO11QUBiusIuHV62wzKn8m5Nc+4XoaBR0YWMFn/g6qXDYrwfCsMpka7vSWJFv5Ff
|
|
||||||
yf+7kY3wU4xIwU2vXlIDcCsdUu6b/pYoQ0YOsQIDAQABAoIBAGbFH6iWnnXrq8MH
|
|
||||||
8zcQNOFmF+RztRgCt0TOA76f6TowB/LbcXBsTl2J7CgYMUvbLuwm2KHX7r9FPTMI
|
|
||||||
XiNFT5C16rYMfiQbLGo4sDhLb/3L+wawem6oHQfzA2VH++lSWRByFaEriF+CgIZl
|
|
||||||
6pALl/uZlLzkXCx+kjPwCSV3vV0wFkDnNs6+wPrz2IhkePsuC8J0QKQLlwsES2It
|
|
||||||
Gizzhpehdv9lc9MyZC//1QlD9gMDl5ok5Bt1Xm2c12XUEEcLlKQkJxiOrBOfXPmV
|
|
||||||
PHCdLc7gZO30hc6dyQ1SSnLpywhz/a0ir2GMvkMbS5hculpcZmwEcdZl1HYD8ObP
|
|
||||||
yOMbPE0CgYEA4LVGJKGtbM8RiBB0MstxNstMYVJ4mXB0lSQ0RazdO3S3ojn+oLpF
|
|
||||||
b2pvV6m9WnHiCGigWkzhqtGGCo6aqE0MoiR4jTN8GhiZz4ggDDaVgc4Px5reUD+r
|
|
||||||
tRsTpBHseGQ+ODGgkMI8eJYkdyqkECkYjAOrdy6uorvgxUAZecRIfJMCgYEA1yhM
|
|
||||||
7NidTNRuA+huS5GcQwQweTM6P1qF7Kfk1JYQMVu4gibLZiLHlWCyHI9lrbI7IaMm
|
|
||||||
g/4jXXoewv7IvyrrSEFulkPeVWxCe3mjfQ8JANfUj4kuR915LSn4lX2pbUgUS66K
|
|
||||||
vJSUJtnzLUmb8khLEcOmDbmTFZl8D/bTHFFZlisCgYAeelfWNhuoq3lMRDcOgKuN
|
|
||||||
bAujE6WJ4kfdxrhUTvr+ynjxxv3zXPB4CS6q7Dnjn5ix3UcKmGzvV1Xf7rGpbDHv
|
|
||||||
eBTlyfrmKzoJfQQjw++JWKKpRycqKUin2tFSKqAxQB90Tb7ig4XiMTMm+qCgFILg
|
|
||||||
0sqZ8rn7FpKJDoWmD2ppgwKBgG2Dl9QeVcKbhfv7PNi+HvmFkl6+knFY1D4nHzSN
|
|
||||||
xWQ6OWoV8QXlwgzokQA0hR6qT6rJbntUyg90b1/1a5zSbbvzgiR+GxcD6bsLqQmo
|
|
||||||
s354XTtKKgJuWpWAfYUp1ylGvP3gs8FVJyu3WC2+/9+MqJk8KrNlt9YQr7M4gTAy
|
|
||||||
wBTNAoGAGU7Po4uI3xDKGLLK/ot3D3P8U9ByfeLlrUZtTz1PASsMOr92bkXmUPlE
|
|
||||||
DYUd5uFfwwlvbMNT1Ooeyrzg3bARd9B6ATyMkOaJeGoQwFAI468iucnm9rNXB+/t
|
|
||||||
U2rbIi1pXSm8zSNEY85tf6C8DU/5YbcAPf47a2UYhwCpYAJfMk0=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
|
@ -1,17 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE REQUEST-----
|
|
||||||
MIICvzCCAacCAQAwejELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNBTElGT1JOSUEx
|
|
||||||
EjAQBgNVBAcTCUxvcyBHYXRvczEWMBQGA1UEChMNTmV0ZmxpeCwgSW5jLjETMBEG
|
|
||||||
A1UECxMKT3BlcmF0aW9uczEVMBMGA1UEAxMMZGZkc2Zsa2oubmV0MIIBIjANBgkq
|
|
||||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNudwW+UeQqkpY71MIdEg501AFlPKuOX
|
|
||||||
G2xU8DZhvZS6dKv+kDmIWdEqodDgkQiy0jyTgTwxwRqDSw96R6ZgrXefUoJJo66a
|
|
||||||
CsosTBZtVaE85f1Lbj2+3U678c+rekUdkrnGcGCo6b8QtdvBpiDy2clneox8tSvm
|
|
||||||
ffAdcR1uCv/790/kPzQ/djWDX9JcBRyDkcTJwYC0/ek7URvA/+MXmgUL13T+gWKq
|
|
||||||
duaKuIBlFetonDjnnO11QUBiusIuHV62wzKn8m5Nc+4XoaBR0YWMFn/g6qXDYrwf
|
|
||||||
CsMpka7vSWJFv5Ffyf+7kY3wU4xIwU2vXlIDcCsdUu6b/pYoQ0YOsQIDAQABoAAw
|
|
||||||
DQYJKoZIhvcNAQEFBQADggEBAE8b0+IYGiR64Me/L0/njYvSR5WR4EnjW99Sc8X5
|
|
||||||
k93zpk4hExrZhrlkDBA/jUHhBZcPNV9w/YkhSu5ubPjRp9gRM2d4B9gGJFAs+bwe
|
|
||||||
LS9hCOxWIMKgvaBMEDQFcwqAv6kEJzmrIa7LtWS39wNfdko2hANtm7z9qskc8bPr
|
|
||||||
265+Z48DwSNCF4RPhVp9eDifjHrj0I//GMXYa92uvgj1BlPo/SGMS+XFQF779p2b
|
|
||||||
622HmUCop3pYeIyYd6rirvl9+KwqvIhm2MqHk62eHOK7Bn/FPev8OUDeV6pIvvSV
|
|
||||||
UxsEHjjLm0V/lOD65lROc7dTq4jO5PkpoKnFQDgV5v0Bf/k=
|
|
||||||
-----END CERTIFICATE REQUEST-----
|
|
|
@ -1,21 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDcDCCAlgCCQC8msHu/aa61zANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
|
|
||||||
UzETMBEGA1UECBMKQ0FMSUZPUk5JQTESMBAGA1UEBxMJTG9zIEdhdG9zMRYwFAYD
|
|
||||||
VQQKEw1OZXRmbGl4LCBJbmMuMRMwEQYDVQQLEwpPcGVyYXRpb25zMRUwEwYDVQQD
|
|
||||||
EwxkZmRzZmxrai5uZXQwHhcNMTQwNTI1MTczMDMzWhcNMTUwNTI1MTczMDMzWjB6
|
|
||||||
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ0FMSUZPUk5JQTESMBAGA1UEBxMJTG9z
|
|
||||||
IEdhdG9zMRYwFAYDVQQKEw1OZXRmbGl4LCBJbmMuMRMwEQYDVQQLEwpPcGVyYXRp
|
|
||||||
b25zMRUwEwYDVQQDEwxkZmRzZmxrai5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
|
||||||
DwAwggEKAoIBAQC8253Bb5R5CqSljvUwh0SDnTUAWU8q45cbbFTwNmG9lLp0q/6Q
|
|
||||||
OYhZ0Sqh0OCRCLLSPJOBPDHBGoNLD3pHpmCtd59SgkmjrpoKyixMFm1VoTzl/Utu
|
|
||||||
Pb7dTrvxz6t6RR2SucZwYKjpvxC128GmIPLZyWd6jHy1K+Z98B1xHW4K//v3T+Q/
|
|
||||||
ND92NYNf0lwFHIORxMnBgLT96TtRG8D/4xeaBQvXdP6BYqp25oq4gGUV62icOOec
|
|
||||||
7XVBQGK6wi4dXrbDMqfybk1z7hehoFHRhYwWf+DqpcNivB8KwymRru9JYkW/kV/J
|
|
||||||
/7uRjfBTjEjBTa9eUgNwKx1S7pv+lihDRg6xAgMBAAEwDQYJKoZIhvcNAQEFBQAD
|
|
||||||
ggEBAJHwa4l2iSiFBb6wVFBJEWEt31qp+njiVCoTg2OJzCT60Xb26hkrsiTldIIh
|
|
||||||
eB9+y+fwdfwopzWhkNbIOlCfudx/uxtpor8/3BRbjSlNwDUg2L8pfAircJMFLQUM
|
|
||||||
O6nqPOBWCe8hXwe9FQM/oFOavf/AAw/FED+892xlytjirK9u3B28O20W11+fY7hp
|
|
||||||
8LQVBrMoVxFeLWmmwETAltJ7HEYutplRzYTM0vLBARl4Vd5kLJlY3j2Dp1ZpRGcg
|
|
||||||
CrQp26UD/oaAPGtiZQSC4LJ+4JfOuuqbm3CI24QMCh9rxv3ZoOQnFuC+7cZgqrat
|
|
||||||
V4bxCrVvWhrrDSgy9+A80NVzQ3k=
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -18,11 +18,11 @@ def test_role_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_role_post(client):
|
def test_role_post(client):
|
||||||
assert client.post(api.url_for(Roles, role_id=1), {}).status_code == 405
|
assert client.post(api.url_for(Roles, role_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_role_put(client):
|
def test_role_put(client):
|
||||||
assert client.put(api.url_for(Roles, role_id=1), {}).status_code == 401
|
assert client.put(api.url_for(Roles, role_id=1), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_role_delete(client):
|
def test_role_delete(client):
|
||||||
|
@ -30,7 +30,7 @@ def test_role_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_role_patch(client):
|
def test_role_patch(client):
|
||||||
assert client.patch(api.url_for(Roles, role_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(Roles, role_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_roles_get(client):
|
def test_roles_get(client):
|
||||||
|
@ -38,11 +38,11 @@ def test_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_roles_post(client):
|
def test_roles_post(client):
|
||||||
assert client.post(api.url_for(RolesList), {}).status_code == 401
|
assert client.post(api.url_for(RolesList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_roles_put(client):
|
def test_roles_put(client):
|
||||||
assert client.put(api.url_for(RolesList), {}).status_code == 405
|
assert client.put(api.url_for(RolesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_roles_delete(client):
|
def test_roles_delete(client):
|
||||||
|
@ -50,7 +50,7 @@ def test_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_roles_patch(client):
|
def test_roles_patch(client):
|
||||||
assert client.patch(api.url_for(RolesList), {}).status_code == 405
|
assert client.patch(api.url_for(RolesList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_role_credentials_get(client):
|
def test_role_credentials_get(client):
|
||||||
|
@ -58,11 +58,11 @@ def test_role_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_role_credentials_post(client):
|
def test_role_credentials_post(client):
|
||||||
assert client.post(api.url_for(RoleViewCredentials, role_id=1), {}).status_code == 405
|
assert client.post(api.url_for(RoleViewCredentials, role_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_role_credentials_put(client):
|
def test_role_credentials_put(client):
|
||||||
assert client.put(api.url_for(RoleViewCredentials, role_id=1), {}).status_code == 405
|
assert client.put(api.url_for(RoleViewCredentials, role_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_role_credentials_delete(client):
|
def test_role_credentials_delete(client):
|
||||||
|
@ -70,7 +70,7 @@ def test_role_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_role_credentials_patch(client):
|
def test_role_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(RoleViewCredentials, role_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(RoleViewCredentials, role_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_user_roles_get(client):
|
def test_user_roles_get(client):
|
||||||
|
@ -78,11 +78,11 @@ def test_user_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_user_roles_post(client):
|
def test_user_roles_post(client):
|
||||||
assert client.post(api.url_for(UserRolesList, user_id=1), {}).status_code == 405
|
assert client.post(api.url_for(UserRolesList, user_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_user_roles_put(client):
|
def test_user_roles_put(client):
|
||||||
assert client.put(api.url_for(UserRolesList, user_id=1), {}).status_code == 405
|
assert client.put(api.url_for(UserRolesList, user_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_user_roles_delete(client):
|
def test_user_roles_delete(client):
|
||||||
|
@ -90,7 +90,7 @@ def test_user_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_user_roles_patch(client):
|
def test_user_roles_patch(client):
|
||||||
assert client.patch(api.url_for(UserRolesList, user_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(UserRolesList, user_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authority_roles_get(client):
|
def test_authority_roles_get(client):
|
||||||
|
@ -98,11 +98,11 @@ def test_authority_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authority_roles_post(client):
|
def test_authority_roles_post(client):
|
||||||
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), {}).status_code == 405
|
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authority_roles_put(client):
|
def test_authority_roles_put(client):
|
||||||
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), {}).status_code == 405
|
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_authority_roles_delete(client):
|
def test_authority_roles_delete(client):
|
||||||
|
@ -110,7 +110,7 @@ def test_authority_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_authority_roles_patch(client):
|
def test_authority_roles_patch(client):
|
||||||
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), {}).status_code == 405
|
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
|
@ -122,7 +122,7 @@ def test_auth_role_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_post_(client):
|
def test_auth_role_post_(client):
|
||||||
assert client.post(api.url_for(Roles, role_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Roles, role_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_put(client):
|
def test_auth_role_put(client):
|
||||||
|
@ -134,7 +134,7 @@ def test_auth_role_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_patch(client):
|
def test_auth_role_patch(client):
|
||||||
assert client.patch(api.url_for(Roles, role_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Roles, role_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_roles_get(client):
|
def test_auth_roles_get(client):
|
||||||
|
@ -142,7 +142,7 @@ def test_auth_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_roles_post(client):
|
def test_auth_roles_post(client):
|
||||||
assert client.post(api.url_for(RolesList), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
assert client.post(api.url_for(RolesList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_credentials_get(client):
|
def test_auth_role_credentials_get(client):
|
||||||
|
@ -150,11 +150,11 @@ def test_auth_role_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_credentials_post(client):
|
def test_auth_role_credentials_post(client):
|
||||||
assert client.post(api.url_for(RoleViewCredentials, role_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(RoleViewCredentials, role_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_credentials_put(client):
|
def test_auth_role_credentials_put(client):
|
||||||
assert client.put(api.url_for(RoleViewCredentials, role_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(RoleViewCredentials, role_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_credentials_delete(client):
|
def test_auth_role_credentials_delete(client):
|
||||||
|
@ -162,7 +162,7 @@ def test_auth_role_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_role_credentials_patch(client):
|
def test_auth_role_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(RoleViewCredentials, role_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(RoleViewCredentials, role_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_user_roles_get(client):
|
def test_auth_user_roles_get(client):
|
||||||
|
@ -170,11 +170,11 @@ def test_auth_user_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_user_roles_post(client):
|
def test_auth_user_roles_post(client):
|
||||||
assert client.post(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_user_roles_put(client):
|
def test_auth_user_roles_put(client):
|
||||||
assert client.put(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_user_roles_delete(client):
|
def test_auth_user_roles_delete(client):
|
||||||
|
@ -182,7 +182,7 @@ def test_auth_user_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_user_roles_patch(client):
|
def test_auth_user_roles_patch(client):
|
||||||
assert client.patch(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_roles_get(client):
|
def test_auth_authority_roles_get(client):
|
||||||
|
@ -190,11 +190,11 @@ def test_auth_authority_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_roles_post(client):
|
def test_auth_authority_roles_post(client):
|
||||||
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_roles_put(client):
|
def test_auth_authority_roles_put(client):
|
||||||
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_roles_delete(client):
|
def test_auth_authority_roles_delete(client):
|
||||||
|
@ -202,7 +202,7 @@ def test_auth_authority_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_auth_authority_roles_patch(client):
|
def test_auth_authority_roles_patch(client):
|
||||||
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_ADMIN_HEADER_TOKEN = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
|
@ -214,7 +214,7 @@ def test_admin_role_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_post(client):
|
def test_admin_role_post(client):
|
||||||
assert client.post(api.url_for(Roles, role_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Roles, role_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_put(client):
|
def test_admin_role_put(client):
|
||||||
|
@ -226,7 +226,7 @@ def test_admin_role_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_patch(client):
|
def test_admin_role_patch(client):
|
||||||
assert client.patch(api.url_for(Roles, role_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Roles, role_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_roles_get(client):
|
def test_admin_roles_get(client):
|
||||||
|
@ -240,11 +240,11 @@ def test_admin_role_credentials_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_credentials_post(client):
|
def test_admin_role_credentials_post(client):
|
||||||
assert client.post(api.url_for(RolesList), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(RolesList), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_credentials_put(client):
|
def test_admin_role_credentials_put(client):
|
||||||
assert client.put(api.url_for(RolesList), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(RolesList), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_credentials_delete(client):
|
def test_admin_role_credentials_delete(client):
|
||||||
|
@ -252,7 +252,7 @@ def test_admin_role_credentials_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_role_credentials_patch(client):
|
def test_admin_role_credentials_patch(client):
|
||||||
assert client.patch(api.url_for(RolesList), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(RolesList), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_roles_get(client):
|
def test_admin_user_roles_get(client):
|
||||||
|
@ -260,11 +260,11 @@ def test_admin_user_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_roles_post(client):
|
def test_admin_user_roles_post(client):
|
||||||
assert client.post(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_roles_put(client):
|
def test_admin_user_roles_put(client):
|
||||||
assert client.put(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_roles_delete(client):
|
def test_admin_user_roles_delete(client):
|
||||||
|
@ -272,7 +272,7 @@ def test_admin_user_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_roles_patch(client):
|
def test_admin_user_roles_patch(client):
|
||||||
assert client.patch(api.url_for(UserRolesList, user_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(UserRolesList, user_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_roles_get(client):
|
def test_admin_authority_roles_get(client):
|
||||||
|
@ -280,11 +280,11 @@ def test_admin_authority_roles_get(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_roles_post(client):
|
def test_admin_authority_roles_post(client):
|
||||||
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_roles_put(client):
|
def test_admin_authority_roles_put(client):
|
||||||
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.put(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_roles_delete(client):
|
def test_admin_authority_roles_delete(client):
|
||||||
|
@ -292,7 +292,7 @@ def test_admin_authority_roles_delete(client):
|
||||||
|
|
||||||
|
|
||||||
def test_admin_authority_roles_patch(client):
|
def test_admin_authority_roles_patch(client):
|
||||||
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), {}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(AuthorityRolesList, authority_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_roles_crud(client):
|
def test_admin_roles_crud(client):
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -47,7 +47,8 @@ tests_require = [
|
||||||
'pyflakes',
|
'pyflakes',
|
||||||
'moto',
|
'moto',
|
||||||
'nose',
|
'nose',
|
||||||
'pytest'
|
'pytest',
|
||||||
|
'pytest-flask'
|
||||||
]
|
]
|
||||||
|
|
||||||
docs_require = [
|
docs_require = [
|
||||||
|
@ -104,6 +105,10 @@ setup(
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'lemur = lemur.manage:main',
|
'lemur = lemur.manage:main',
|
||||||
],
|
],
|
||||||
|
'lemur.plugins': [
|
||||||
|
'verisign = lemur.plugins.lemur_verisign.plugin:VerisignPlugin',
|
||||||
|
'cloudca = lemur.plugins.lemur_cloudca.plugin:CloudCAPlugin',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Framework :: Flask',
|
'Framework :: Flask',
|
||||||
|
|
Loading…
Reference in New Issue