commit
389ad1981b
|
@ -1,11 +1,11 @@
|
|||
Writing a Plugin
|
||||
================
|
||||
|
||||
**The plugin interface is a work in progress.**
|
||||
|
||||
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
|
||||
---------
|
||||
|
@ -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::
|
||||
|
||||
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'
|
||||
slug = 'pluginname'
|
||||
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
|
||||
via the web interface based on the hooks you enabled.
|
||||
That's it! Users will be able to install your plugin via ``pip install <package name>``.
|
||||
|
||||
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
|
||||
on everything.
|
||||
The `IssuerPlugin` interface only required that you implement one function::
|
||||
|
||||
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
|
||||
|
@ -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 ============================
|
||||
|
||||
|
||||
.. 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 () {
|
||||
browserSyncInit([
|
||||
'.tmp',
|
||||
'app'
|
||||
'lemur/static/app'
|
||||
], [
|
||||
'.tmp/*.html',
|
||||
'.tmp/styles/**/*.css',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
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/**/*.css', ['dev:styles']);
|
||||
gulp.watch('app/**/*.js', ['dev:scripts']);
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
|
||||
"""
|
||||
from flask import jsonify
|
||||
|
||||
from lemur import factory
|
||||
|
||||
from lemur.users.views import mod as users_bp
|
||||
|
|
|
@ -72,13 +72,13 @@ def create_token(user):
|
|||
:param user:
|
||||
: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 = {
|
||||
'sub': user.id,
|
||||
'iat': datetime.now(),
|
||||
'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')
|
||||
|
||||
|
||||
|
@ -102,7 +102,7 @@ def login_required(f):
|
|||
return dict(message='Token is invalid'), 403
|
||||
|
||||
try:
|
||||
payload = jwt.decode(token, current_app.config['TOKEN_SECRET'])
|
||||
payload = jwt.decode(token, current_app.config['LEMUR_TOKEN_SECRET'])
|
||||
except jwt.DecodeError:
|
||||
return dict(message='Token is invalid'), 403
|
||||
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.principal import Identity, identity_changed
|
||||
|
||||
from lemur.common.crypto import unlock
|
||||
|
||||
from lemur.auth.permissions import admin_permission
|
||||
from lemur.users import service as user_service
|
||||
from lemur.roles import service as role_service
|
||||
|
@ -234,24 +232,7 @@ class Ping(Resource):
|
|||
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(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
|
||||
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):
|
||||
"""
|
||||
|
@ -49,12 +49,12 @@ def create(kwargs):
|
|||
:return:
|
||||
"""
|
||||
|
||||
issuer = get_plugin_by_name(kwargs.get('pluginName'))
|
||||
issuer = plugins.get(kwargs.get('pluginName'))
|
||||
|
||||
kwargs['creator'] = g.current_user.email
|
||||
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
|
||||
|
||||
# we create and attach any roles that the issuer gives us
|
||||
|
@ -65,9 +65,11 @@ def create(kwargs):
|
|||
password=r['password'],
|
||||
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
||||
username=r['username'])
|
||||
|
||||
# the user creating the authority should be able to administer it
|
||||
if role.username == 'admin':
|
||||
g.current_user.roles.append(role)
|
||||
|
||||
role_objs.append(role)
|
||||
|
||||
authority = Authority(
|
||||
|
@ -80,7 +82,6 @@ def create(kwargs):
|
|||
roles=role_objs
|
||||
)
|
||||
|
||||
# do this last encase we need to roll back/abort
|
||||
database.update(cert)
|
||||
authority = database.create(authority)
|
||||
|
||||
|
|
|
@ -20,13 +20,12 @@ from sqlalchemy_utils import EncryptedType
|
|||
from lemur.database import db
|
||||
|
||||
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.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
|
||||
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
|
||||
: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:
|
||||
t = SAN_NAMING_TEMPLATE
|
||||
else:
|
||||
|
@ -53,7 +47,14 @@ def create_name(issuer, not_before, not_after, common_name, san):
|
|||
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):
|
||||
|
@ -85,11 +86,6 @@ def cert_get_domains(cert):
|
|||
domains.append(entry)
|
||||
except Exception as 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
|
||||
|
||||
|
||||
|
@ -111,11 +107,8 @@ def cert_is_san(cert):
|
|||
:param cert:
|
||||
:return: Bool
|
||||
"""
|
||||
domains = cert_get_domains(cert)
|
||||
if len(domains) > 1:
|
||||
if len(cert_get_domains(cert)) > 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def cert_is_wildcard(cert):
|
||||
"""
|
||||
|
@ -127,7 +120,6 @@ def cert_is_wildcard(cert):
|
|||
domains = cert_get_domains(cert)
|
||||
if len(domains) == 1 and domains[0][0:1] == "*":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def cert_get_bitstrength(cert):
|
||||
|
@ -205,8 +197,8 @@ class Certificate(db.Model):
|
|||
owner = Column(String(128))
|
||||
body = Column(Text())
|
||||
private_key = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
||||
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
||||
csr_config = Column(Text())
|
||||
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY'))) # TODO deprecate
|
||||
csr_config = Column(Text()) # TODO deprecate
|
||||
status = Column(String(128))
|
||||
deleted = Column(Boolean, index=True)
|
||||
name = Column(String(128))
|
||||
|
@ -227,13 +219,11 @@ class Certificate(db.Model):
|
|||
domains = relationship("Domain", secondary=certificate_associations, 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
|
||||
# We encrypt the private_key on creation
|
||||
self.private_key = private_key
|
||||
self.chain = chain
|
||||
self.csr_config = csr_config
|
||||
self.challenge = challenge
|
||||
cert = x509.load_pem_x509_certificate(str(self.body), default_backend())
|
||||
self.bits = cert_get_bitstrength(cert)
|
||||
self.issuer = cert_get_issuer(cert)
|
||||
|
|
|
@ -5,24 +5,17 @@
|
|||
:license: Apache, see LICENSE for more details.
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
import os
|
||||
import arrow
|
||||
import string
|
||||
import random
|
||||
import hashlib
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
from sqlalchemy import func, or_
|
||||
from flask import g, current_app
|
||||
|
||||
from lemur import database
|
||||
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.exceptions import UnableToCreateCSR, \
|
||||
UnableToCreatePrivateKey, MissingFiles
|
||||
|
||||
from lemur.accounts.models import Account
|
||||
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 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):
|
||||
"""
|
||||
|
@ -127,24 +126,18 @@ def mint(issuer_options):
|
|||
"""
|
||||
authority = issuer_options['authority']
|
||||
|
||||
issuer = get_plugin_by_name(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 = plugins.get(authority.plugin_name)
|
||||
|
||||
issuer_options['challenge'] = challenge
|
||||
csr, private_key = create_csr(issuer_options)
|
||||
|
||||
issuer_options['challenge'] = create_challenge()
|
||||
issuer_options['creator'] = g.user.email
|
||||
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.authority = authority
|
||||
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,
|
||||
|
||||
|
||||
|
@ -152,7 +145,7 @@ def import_certificate(**kwargs):
|
|||
"""
|
||||
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.
|
||||
|
||||
Internally this is used to bootstrap Lemur with external
|
||||
|
@ -180,7 +173,7 @@ def import_certificate(**kwargs):
|
|||
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.
|
||||
|
||||
|
@ -189,9 +182,9 @@ def save_cert(cert_body, private_key, cert_chain, challenge, csr_config, account
|
|||
:param cert_chain:
|
||||
:param challenge:
|
||||
: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 accounts:
|
||||
for account in accounts:
|
||||
|
@ -211,8 +204,6 @@ def upload(**kwargs):
|
|||
kwargs.get('public_cert'),
|
||||
kwargs.get('private_key'),
|
||||
kwargs.get('intermediate_cert'),
|
||||
None,
|
||||
None,
|
||||
kwargs.get('accounts')
|
||||
)
|
||||
|
||||
|
@ -230,6 +221,7 @@ def create(**kwargs):
|
|||
|
||||
cert.owner = kwargs['owner']
|
||||
database.create(cert)
|
||||
cert.description = kwargs['description']
|
||||
g.user.certificates.append(cert)
|
||||
database.update(g.user)
|
||||
return cert
|
||||
|
@ -302,94 +294,92 @@ def create_csr(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
|
||||
path = create_path(hashlib.md5(csr_config).hexdigest())
|
||||
# TODO When we figure out a better way to validate these options they should be parsed as unicode
|
||||
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()
|
||||
challenge_path = os.path.join(path, 'challenge.txt')
|
||||
builder = builder.add_extension(
|
||||
x509.BasicConstraints(ca=False, path_length=None), critical=True,
|
||||
)
|
||||
|
||||
with open(challenge_path, 'w') as c:
|
||||
c.write(challenge)
|
||||
#for k, v in csr_config.get('extensions', {}).items():
|
||||
# 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:
|
||||
f.write(csr_config)
|
||||
request = builder.sign(
|
||||
private_key, hashes.SHA256(), default_backend()
|
||||
)
|
||||
|
||||
#TODO use cloudCA to seed a -rand file for each call
|
||||
#TODO replace openssl shell calls with cryptograph
|
||||
with open('/dev/null', 'w') as devnull:
|
||||
code = subprocess.call(['openssl', 'genrsa',
|
||||
'-out', os.path.join(path, 'private.key'), '2048'],
|
||||
stdout=devnull, stderr=devnull)
|
||||
# serialize our private key and CSR
|
||||
pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL, # would like to use PKCS8 but AWS ELBs don't like it
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
|
||||
if code != 0:
|
||||
raise UnableToCreatePrivateKey(code)
|
||||
|
||||
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])
|
||||
csr = request.public_bytes(
|
||||
encoding=serialization.Encoding.PEM
|
||||
)
|
||||
|
||||
return csr, pem
|
||||
|
||||
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_cert_from_arn
|
||||
|
||||
from lemur.common.services.issuers.manager import get_plugin_by_name
|
||||
|
||||
from lemur.plugins.base import plugins
|
||||
|
||||
def aws():
|
||||
"""
|
||||
|
@ -101,7 +100,7 @@ def cloudca():
|
|||
"""
|
||||
user = user_service.get_by_email('lemur@nobody')
|
||||
# sync all new certificates/authorities not created through lemur
|
||||
issuer = get_plugin_by_name('cloudca')
|
||||
issuer = plugins.get('cloudca')
|
||||
authorities = issuer.get_authorities()
|
||||
total = 0
|
||||
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 imp
|
||||
import errno
|
||||
import pkg_resources
|
||||
|
||||
from logging import Formatter
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
@ -51,6 +52,7 @@ def create_app(app_name=None, blueprints=None, config=None):
|
|||
configure_blueprints(app, blueprints)
|
||||
configure_extensions(app)
|
||||
configure_logging(app)
|
||||
install_plugins(app)
|
||||
|
||||
@app.teardown_appcontext
|
||||
def teardown(exception=None):
|
||||
|
@ -141,3 +143,26 @@ def configure_logging(app):
|
|||
app.logger.setLevel(app.config.get('LOG_LEVEL', 'DEBUG'))
|
||||
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 sys
|
||||
import base64
|
||||
from gunicorn.config import make_settings
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
from flask import current_app
|
||||
from flask.ext.script import Manager, Command, Option, Group, prompt_pass
|
||||
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 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
|
||||
from lemur.users.models import User
|
||||
|
@ -133,78 +133,6 @@ def create():
|
|||
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
|
||||
def check_revoked():
|
||||
"""
|
||||
|
@ -346,45 +274,6 @@ class InitializeApp(Command):
|
|||
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):
|
||||
"""
|
||||
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:
|
||||
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():
|
||||
|
|
|
@ -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
|
||||
: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.plugins.base import Plugin
|
||||
|
||||
|
||||
class Issuer(object):
|
||||
class IssuerPlugin(Plugin):
|
||||
"""
|
||||
This is the base class from which all of the supported
|
||||
issuers will inherit from.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.dry_run = current_app.config.get('DRY_RUN')
|
||||
|
||||
def create_certificate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def create_authority(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_authorities(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_csr_config(self):
|
||||
raise NotImplementedError
|
||||
raise NotImplemented
|
||||
|
|
@ -18,10 +18,8 @@ from requests.adapters import HTTPAdapter
|
|||
from flask import current_app
|
||||
|
||||
from lemur.exceptions import LemurException
|
||||
from lemur.common.services.issuers.issuer import Issuer
|
||||
|
||||
from lemur.common.services.issuers.plugins import cloudca
|
||||
|
||||
from lemur.plugins.bases import IssuerPlugin
|
||||
from lemur.plugins import lemur_cloudca as cloudca
|
||||
|
||||
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))
|
||||
|
||||
|
||||
class CloudCA(Issuer):
|
||||
class CloudCAPlugin(IssuerPlugin):
|
||||
title = 'CloudCA'
|
||||
slug = 'cloudca'
|
||||
description = 'Enables the creation of certificates from the cloudca API.'
|
||||
|
@ -164,7 +162,7 @@ class CloudCA(Issuer):
|
|||
else:
|
||||
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):
|
||||
"""
|
||||
|
@ -261,15 +259,6 @@ class CloudCA(Issuer):
|
|||
|
||||
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):
|
||||
"""
|
||||
Uses CloudCA as a decent source of randomness.
|
||||
|
@ -317,9 +306,6 @@ class CloudCA(Issuer):
|
|||
:param data:
|
||||
:return:
|
||||
"""
|
||||
if self.dry_run:
|
||||
endpoint += '?dry_run=1'
|
||||
|
||||
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
|
||||
|
@ -334,13 +320,6 @@ class CloudCA(Issuer):
|
|||
:param endpoint:
|
||||
:return:
|
||||
"""
|
||||
if self.dry_run:
|
||||
endpoint += '?dry_run=1'
|
||||
|
||||
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
||||
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
|
||||
:synopsis: This module is responsible for communicating with the VeriSign VICE 2.0 API.
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
|
@ -13,10 +13,9 @@ import xmltodict
|
|||
|
||||
from flask import current_app
|
||||
|
||||
from lemur.common.services.issuers.issuer import Issuer
|
||||
from lemur.common.services.issuers.plugins import verisign
|
||||
|
||||
from lemur.certificates.exceptions import InsufficientDomains
|
||||
from lemur.plugins.bases import IssuerPlugin
|
||||
from lemur.plugins import lemur_verisign as verisign
|
||||
from lemur.plugins.lemur_verisign import constants
|
||||
|
||||
|
||||
# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
|
||||
|
@ -54,26 +53,12 @@ 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",
|
||||
"0x600d": "RSA key size < 2A048",
|
||||
"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.
|
||||
:param content:
|
||||
|
@ -89,6 +74,21 @@ class Verisign(Issuer):
|
|||
raise Exception(VERISIGN_ERRORS[status_code])
|
||||
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):
|
||||
"""
|
||||
Creates a Verisign certificate.
|
||||
|
@ -126,42 +126,8 @@ class Verisign(Issuer):
|
|||
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
||||
|
||||
response = self.session.post(url, data=data)
|
||||
cert = self.handle_response(response.content)['Response']['Certificate']
|
||||
return cert, verisign.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))
|
||||
cert = handle_response(response.content)['Response']['Certificate']
|
||||
return cert, constants.VERISIGN_INTERMEDIATE,
|
||||
|
||||
@staticmethod
|
||||
def create_authority(options):
|
||||
|
@ -173,7 +139,7 @@ class Verisign(Issuer):
|
|||
:return:
|
||||
"""
|
||||
role = {'username': '', 'password': '', 'name': 'verisign'}
|
||||
return verisign.constants.VERISIGN_ROOT, "", [role]
|
||||
return constants.VERISIGN_ROOT, "", [role]
|
||||
|
||||
def get_available_units(self):
|
||||
"""
|
||||
|
@ -184,11 +150,5 @@ class Verisign(Issuer):
|
|||
"""
|
||||
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
||||
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 data-title="'Roles'"> <!--filter="{ 'select': 'role' }" filter-data="roleService.getRoleDropDown()">-->
|
||||
<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 }}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="''">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
State
|
||||
</label>
|
||||
<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>
|
||||
</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>
|
||||
</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">
|
||||
Certificate Authority
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<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"
|
||||
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 ng-show="certificate.authority.name == 'verisign'">
|
||||
<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">
|
||||
<div ng-show="certificate.authority" class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Certificate Template
|
||||
</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>
|
||||
</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}">
|
||||
<label class="control-label col-sm-2">
|
||||
Common Name
|
||||
</label>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Validity Range
|
||||
<label class="control-label col-sm-2" tooltip="If no date is selected Lemur attempts to issue a 2 year certificate">
|
||||
Validity Range <span class="glyphicon glyphicon-question-sign"></span>
|
||||
</label>
|
||||
<div class="col-sm-4">
|
||||
<div>
|
||||
|
|
|
@ -77,8 +77,8 @@
|
|||
<li class="list-group-item">
|
||||
<strong>San</strong>
|
||||
<span class="pull-right">
|
||||
<i class="glyphicon glyphicon-ok" ng-show="certificate.san == 'true'"></i>
|
||||
<i class="glyphicon glyphicon-remove" ng-show="certificate.san == 'false'"></i>
|
||||
<i class="glyphicon glyphicon-ok" ng-show="certificate.san"></i>
|
||||
<i class="glyphicon glyphicon-remove" ng-show="!certificate.san"></i>
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
|
|
|
@ -45,7 +45,6 @@ def app():
|
|||
ctx.pop()
|
||||
|
||||
|
||||
|
||||
@pytest.yield_fixture(scope="session")
|
||||
def db(app, request):
|
||||
_db.drop_all()
|
||||
|
@ -72,7 +71,6 @@ def session(db, request):
|
|||
|
||||
|
||||
@pytest.yield_fixture(scope="function")
|
||||
def client(app, session):
|
||||
with app.test_client() as client:
|
||||
def client(app, session, client):
|
||||
yield client
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ def test_account_get(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):
|
||||
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):
|
||||
|
@ -34,7 +34,7 @@ def test_account_delete(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 = {
|
||||
|
@ -45,7 +45,7 @@ def test_auth_account_get(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):
|
||||
|
@ -57,7 +57,7 @@ def test_auth_account_delete(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 = {
|
||||
|
@ -68,7 +68,7 @@ def test_admin_account_get(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):
|
||||
|
@ -80,7 +80,7 @@ def test_admin_account_delete(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):
|
||||
|
@ -88,11 +88,11 @@ def test_accounts_get(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):
|
||||
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):
|
||||
|
@ -100,7 +100,7 @@ def test_accounts_delete(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):
|
||||
|
@ -108,7 +108,7 @@ def test_auth_accounts_get(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):
|
||||
|
|
|
@ -16,11 +16,11 @@ def test_authority_get(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):
|
||||
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):
|
||||
|
@ -28,7 +28,7 @@ def test_authority_delete(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):
|
||||
|
@ -36,11 +36,11 @@ def test_authorities_get(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):
|
||||
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):
|
||||
|
@ -48,7 +48,7 @@ def test_authorities_delete(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):
|
||||
|
@ -56,11 +56,11 @@ def test_certificate_authorities_get(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):
|
||||
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):
|
||||
|
@ -68,7 +68,7 @@ def test_certificate_authorities_delete(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 = {
|
||||
|
@ -80,7 +80,7 @@ def test_auth_authority_get(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):
|
||||
|
@ -92,7 +92,7 @@ def test_auth_authority_delete(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):
|
||||
|
@ -100,7 +100,7 @@ def test_auth_authorities_get(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):
|
||||
|
@ -116,7 +116,7 @@ def test_admin_authority_get(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):
|
||||
|
@ -136,11 +136,11 @@ def test_admin_authorities_get(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):
|
||||
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):
|
||||
|
|
|
@ -1,18 +1,6 @@
|
|||
import os
|
||||
import pytest
|
||||
from mock import mock_open, patch
|
||||
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):
|
||||
assert 1 == 2
|
||||
|
||||
|
@ -33,28 +21,26 @@ def test_private_key_str():
|
|||
private_key_str('dfsdfsdf', 'test')
|
||||
|
||||
|
||||
def test_create_csr():
|
||||
from lemur.tests.certs import CSR_CONFIG
|
||||
def test_create_basic_csr():
|
||||
from lemur.certificates.service import create_csr
|
||||
m = mock_open()
|
||||
with patch('lemur.certificates.service.open', m, create=True):
|
||||
path = create_csr(CSR_CONFIG)
|
||||
assert path == ''
|
||||
csr_config = dict(
|
||||
commonName=u'example.com',
|
||||
organization=u'Example, Inc.',
|
||||
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():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_load_ssl_pack():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_delete_ssl_path():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_import_certificate(session):
|
||||
def test_import_certificate():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
|
@ -137,20 +123,17 @@ def test_create_name():
|
|||
True
|
||||
) == 'SAN-example.com-ExampleInc-20150507-20150512'
|
||||
|
||||
def test_is_expired():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_certificate_get(client):
|
||||
assert client.get(api.url_for(Certificates, certificate_id=1)).status_code == 401
|
||||
|
||||
|
||||
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):
|
||||
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):
|
||||
|
@ -158,7 +141,7 @@ def test_certificate_delete(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):
|
||||
|
@ -166,11 +149,11 @@ def test_certificates_get(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):
|
||||
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):
|
||||
|
@ -178,7 +161,7 @@ def test_certificates_delete(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):
|
||||
|
@ -186,11 +169,11 @@ def test_certificate_credentials_get(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):
|
||||
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):
|
||||
|
@ -198,7 +181,7 @@ def test_certificate_credentials_delete(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):
|
||||
|
@ -206,11 +189,11 @@ def test_certificates_upload_get(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):
|
||||
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):
|
||||
|
@ -218,7 +201,7 @@ def test_certificates_upload_delete(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 = {
|
||||
|
@ -230,7 +213,7 @@ def test_auth_certificate_get(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):
|
||||
|
@ -242,7 +225,7 @@ def test_auth_certificate_delete(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):
|
||||
|
@ -250,7 +233,7 @@ def test_auth_certificates_get(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):
|
||||
|
@ -258,11 +241,11 @@ def test_auth_certificate_credentials_get(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):
|
||||
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):
|
||||
|
@ -270,7 +253,7 @@ def test_auth_certificate_credentials_delete(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):
|
||||
|
@ -278,11 +261,11 @@ def test_auth_certificates_upload_get(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):
|
||||
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):
|
||||
|
@ -290,7 +273,7 @@ def test_auth_certificates_upload_delete(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 = {
|
||||
|
@ -302,7 +285,7 @@ def test_admin_certificate_get(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):
|
||||
|
@ -314,7 +297,7 @@ def test_admin_certificate_delete(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):
|
||||
|
@ -328,7 +311,7 @@ def test_admin_certificate_credentials_get(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):
|
||||
|
@ -340,5 +323,5 @@ def test_admin_certificate_credentials_delete(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):
|
||||
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):
|
||||
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):
|
||||
|
@ -17,7 +17,7 @@ def test_domain_delete(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 = {
|
||||
|
@ -28,7 +28,7 @@ def test_auth_domain_get(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):
|
||||
|
@ -40,7 +40,7 @@ def test_auth_domain_delete(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 = {
|
||||
|
@ -51,7 +51,7 @@ def test_admin_domain_get(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):
|
||||
|
@ -63,7 +63,7 @@ def test_admin_domain_delete(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):
|
||||
|
@ -71,11 +71,11 @@ def test_domains_get(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):
|
||||
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):
|
||||
|
@ -83,7 +83,7 @@ def test_domains_delete(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):
|
||||
|
@ -101,11 +101,11 @@ def test_certificate_domains_get(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):
|
||||
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):
|
||||
|
@ -113,7 +113,7 @@ def test_certificate_domains_delete(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):
|
||||
|
|
|
@ -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):
|
||||
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):
|
||||
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):
|
||||
|
@ -30,7 +30,7 @@ def test_role_delete(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):
|
||||
|
@ -38,11 +38,11 @@ def test_roles_get(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):
|
||||
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):
|
||||
|
@ -50,7 +50,7 @@ def test_roles_delete(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):
|
||||
|
@ -58,11 +58,11 @@ def test_role_credentials_get(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):
|
||||
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):
|
||||
|
@ -70,7 +70,7 @@ def test_role_credentials_delete(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):
|
||||
|
@ -78,11 +78,11 @@ def test_user_roles_get(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):
|
||||
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):
|
||||
|
@ -90,7 +90,7 @@ def test_user_roles_delete(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):
|
||||
|
@ -98,11 +98,11 @@ def test_authority_roles_get(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):
|
||||
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):
|
||||
|
@ -110,7 +110,7 @@ def test_authority_roles_delete(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 = {
|
||||
|
@ -122,7 +122,7 @@ def test_auth_role_get(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):
|
||||
|
@ -134,7 +134,7 @@ def test_auth_role_delete(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):
|
||||
|
@ -142,7 +142,7 @@ def test_auth_roles_get(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):
|
||||
|
@ -150,11 +150,11 @@ def test_auth_role_credentials_get(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):
|
||||
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):
|
||||
|
@ -162,7 +162,7 @@ def test_auth_role_credentials_delete(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):
|
||||
|
@ -170,11 +170,11 @@ def test_auth_user_roles_get(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):
|
||||
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):
|
||||
|
@ -182,7 +182,7 @@ def test_auth_user_roles_delete(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):
|
||||
|
@ -190,11 +190,11 @@ def test_auth_authority_roles_get(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):
|
||||
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):
|
||||
|
@ -202,7 +202,7 @@ def test_auth_authority_roles_delete(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 = {
|
||||
|
@ -214,7 +214,7 @@ def test_admin_role_get(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):
|
||||
|
@ -226,7 +226,7 @@ def test_admin_role_delete(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):
|
||||
|
@ -240,11 +240,11 @@ def test_admin_role_credentials_get(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):
|
||||
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):
|
||||
|
@ -252,7 +252,7 @@ def test_admin_role_credentials_delete(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):
|
||||
|
@ -260,11 +260,11 @@ def test_admin_user_roles_get(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):
|
||||
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):
|
||||
|
@ -272,7 +272,7 @@ def test_admin_user_roles_delete(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):
|
||||
|
@ -280,11 +280,11 @@ def test_admin_authority_roles_get(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):
|
||||
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):
|
||||
|
@ -292,7 +292,7 @@ def test_admin_authority_roles_delete(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):
|
||||
|
|
7
setup.py
7
setup.py
|
@ -47,7 +47,8 @@ tests_require = [
|
|||
'pyflakes',
|
||||
'moto',
|
||||
'nose',
|
||||
'pytest'
|
||||
'pytest',
|
||||
'pytest-flask'
|
||||
]
|
||||
|
||||
docs_require = [
|
||||
|
@ -104,6 +105,10 @@ setup(
|
|||
'console_scripts': [
|
||||
'lemur = lemur.manage:main',
|
||||
],
|
||||
'lemur.plugins': [
|
||||
'verisign = lemur.plugins.lemur_verisign.plugin:VerisignPlugin',
|
||||
'cloudca = lemur.plugins.lemur_cloudca.plugin:CloudCAPlugin',
|
||||
],
|
||||
},
|
||||
classifiers=[
|
||||
'Framework :: Flask',
|
||||
|
|
Loading…
Reference in New Issue