commit
c04e23d3ea
|
@ -26,3 +26,5 @@ pip-log.txt
|
|||
docs/_build
|
||||
.editorconfig
|
||||
.idea
|
||||
test.conf
|
||||
lemur/tests/tmp
|
|
@ -1,4 +1,2 @@
|
|||
tests/
|
||||
lemur/static/lemur/scripts/lib/
|
||||
lemur/static/lemur/dist/
|
||||
lemur/static/lemur/vendor/
|
||||
lemur/static//dist/
|
||||
lemur/static/app/vendor/
|
|
@ -3,13 +3,13 @@
|
|||
"browser": true,
|
||||
"esnext": true,
|
||||
"bitwise": true,
|
||||
"camelcase": true,
|
||||
"camelcase": false,
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"indent": 2,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"newcap": false,
|
||||
"noarg": true,
|
||||
"quotmark": "single",
|
||||
"regexp": true,
|
||||
|
@ -19,6 +19,9 @@
|
|||
"trailing": true,
|
||||
"smarttabs": true,
|
||||
"globals": {
|
||||
"angular": false
|
||||
"angular": false,
|
||||
"moment": false,
|
||||
"toaster": false,
|
||||
"_": false
|
||||
}
|
||||
}
|
||||
|
|
31
.travis.yml
31
.travis.yml
|
@ -1,7 +1,26 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '0.8'
|
||||
- '0.10'
|
||||
sudo: false
|
||||
language: python
|
||||
addons:
|
||||
postgresql: "9.4"
|
||||
python:
|
||||
- "2.7"
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- .pip_download_cache
|
||||
- "$HOME/virtualenv/python2.7.9"
|
||||
env:
|
||||
global:
|
||||
- PIP_DOWNLOAD_CACHE=".pip_download_cache"
|
||||
install:
|
||||
- make dev-postgres
|
||||
before_script:
|
||||
- 'npm install -g bower grunt-cli'
|
||||
- 'bower install'
|
||||
- psql -c "create database lemur;" -U postgres
|
||||
- psql -c "create user lemur with password 'lemur;'" -U postgres
|
||||
- npm install -g bower
|
||||
script:
|
||||
- make test
|
||||
|
||||
notifications:
|
||||
email:
|
||||
kglisson@netflix.com
|
|
@ -0,0 +1,85 @@
|
|||
NPM_ROOT = ./node_modules
|
||||
STATIC_DIR = src/lemur/static/app
|
||||
|
||||
develop: update-submodules setup-git
|
||||
@echo "--> Installing dependencies"
|
||||
npm install
|
||||
pip install "setuptools>=0.9.8"
|
||||
# order matters here, base package must install first
|
||||
# this is temporary until the version we need is released
|
||||
pip install -e 'git+https://git@github.com/pyca/cryptography.git#egg=cryptography-1.0.dev1'
|
||||
pip install -e .
|
||||
pip install "file://`pwd`#egg=lemur[dev]"
|
||||
pip install "file://`pwd`#egg=lemur[tests]"
|
||||
@echo ""
|
||||
|
||||
dev-docs:
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
reset-db:
|
||||
@echo "--> Dropping existing 'lemur' database"
|
||||
dropdb lemur || true
|
||||
@echo "--> Creating 'lemur' database"
|
||||
createdb -E utf-8 lemur
|
||||
@echo "--> Applying migrations"
|
||||
lemur db upgrade
|
||||
|
||||
setup-git:
|
||||
@echo "--> Installing git hooks"
|
||||
git config branch.autosetuprebase always
|
||||
cd .git/hooks && ln -sf ../../hooks/* ./
|
||||
@echo ""
|
||||
|
||||
clean:
|
||||
@echo "--> Cleaning static cache"
|
||||
${NPM_ROOT}/.bin/gulp clean
|
||||
@echo "--> Cleaning pyc files"
|
||||
find . -name "*.pyc" -delete
|
||||
@echo ""
|
||||
|
||||
test: develop lint test-python
|
||||
|
||||
testloop: develop
|
||||
pip install pytest-xdist
|
||||
py.test tests -f
|
||||
|
||||
test-cli:
|
||||
@echo "--> Testing CLI"
|
||||
rm -rf test_cli
|
||||
mkdir test_cli
|
||||
cd test_cli && lemur create_config -c ./test.conf > /dev/null
|
||||
cd test_cli && lemur -c ./test.conf db upgrade > /dev/null
|
||||
cd test_cli && lemur -c ./test.conf help 2>&1 | grep start > /dev/null
|
||||
rm -r test_cli
|
||||
@echo ""
|
||||
|
||||
test-js:
|
||||
@echo "--> Running JavaScript tests"
|
||||
npm test
|
||||
@echo ""
|
||||
|
||||
test-python:
|
||||
@echo "--> Running Python tests"
|
||||
py.test lemur/tests || exit 1
|
||||
@echo ""
|
||||
|
||||
lint: lint-python lint-js
|
||||
|
||||
lint-python:
|
||||
@echo "--> Linting Python files"
|
||||
PYFLAKES_NODOCTEST=1 flake8 lemur
|
||||
@echo ""
|
||||
|
||||
lint-js:
|
||||
@echo "--> Linting JavaScript files"
|
||||
npm run lint
|
||||
@echo ""
|
||||
|
||||
coverage: develop
|
||||
coverage run --source=lemur -m py.test
|
||||
coverage html
|
||||
|
||||
publish:
|
||||
python setup.py sdist bdist_wheel upload
|
||||
|
||||
.PHONY: develop dev-postgres dev-docs setup-git build clean update-submodules test testloop test-cli test-js test-python lint lint-python lint-js coverage publish
|
|
@ -0,0 +1,5 @@
|
|||
Jinja2>=2.3
|
||||
Pygments>=1.2
|
||||
Sphinx>=1.3
|
||||
docutils>=0.7
|
||||
markupsafe
|
|
@ -26,9 +26,9 @@ var gulp = require('gulp'),
|
|||
imagemin = require('gulp-imagemin'),
|
||||
minifyHtml = require('gulp-minify-html'),
|
||||
bowerFiles = require('main-bower-files'),
|
||||
karma = require('karma'),
|
||||
replace = require('gulp-replace');
|
||||
|
||||
|
||||
gulp.task('default', ['clean'], function () {
|
||||
gulp.start('fonts', 'styles');
|
||||
});
|
||||
|
@ -37,6 +37,15 @@ gulp.task('clean', function (cb) {
|
|||
del(['.tmp', 'lemur/static/dist'], cb);
|
||||
});
|
||||
|
||||
gulp.task('test', function (done) {
|
||||
new karma.Server({
|
||||
configFile: __dirname + '/karma.conf.js',
|
||||
singleRun: true
|
||||
}, function() {
|
||||
done();
|
||||
}).start();
|
||||
});
|
||||
|
||||
gulp.task('dev:fonts', function () {
|
||||
var fileList = [
|
||||
'lemur/static/app/vendor/bower_components/bootstrap/dist/fonts/*',
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Contents of: config/karma.conf.js
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath : '../',
|
||||
|
||||
// Fix for "JASMINE is not supported anymore" warning
|
||||
frameworks : ["jasmine"],
|
||||
|
||||
files : [
|
||||
'app/lib/angular/angular.js',
|
||||
'app/lib/angular/angular-*.js',
|
||||
'test/lib/angular/angular-mocks.js',
|
||||
'app/js/**/*.js',
|
||||
'test/unit/**/*.js'
|
||||
],
|
||||
|
||||
autoWatch : true,
|
||||
|
||||
browsers : ['Chrome'],
|
||||
|
||||
junitReporter : {
|
||||
outputFile : 'test_out/unit.xml',
|
||||
suite : 'unit'
|
||||
//...
|
||||
}
|
||||
});
|
||||
};
|
|
@ -27,5 +27,5 @@ gulp.task('default', function () {
|
|||
console.log(c.green + '-------------------------------------------' + c.reset);
|
||||
console.log(Object.keys(gulp.tasks).sort().join('\n'));
|
||||
console.log('');
|
||||
return;
|
||||
|
||||
});
|
||||
|
|
|
@ -29,14 +29,6 @@ def py_lint(files_modified):
|
|||
return report.total_errors != 0
|
||||
|
||||
|
||||
def js_lint(files_modified):
|
||||
has_errors = False
|
||||
if os.system('node_modules/.bin/jshint src/sentry'):
|
||||
has_errors = True
|
||||
|
||||
return has_errors
|
||||
|
||||
|
||||
def main():
|
||||
from flake8.hooks import run
|
||||
|
||||
|
@ -46,7 +38,7 @@ def main():
|
|||
|
||||
files_modified = filter(lambda x: os.path.exists(x), files_modified)
|
||||
|
||||
if any((py_lint(files_modified), js_lint(files_modified))):
|
||||
if py_lint(files_modified):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ LEMUR_BLUEPRINTS = (
|
|||
plugins_bp,
|
||||
)
|
||||
|
||||
|
||||
def create_app(config=None):
|
||||
app = factory.create_app(app_name=__name__, blueprints=LEMUR_BLUEPRINTS, config=config)
|
||||
configure_hook(app)
|
||||
|
@ -61,4 +62,3 @@ def configure_hook(app):
|
|||
response = {'message': 'You are not allow to access this resource'}
|
||||
response.status_code = 403
|
||||
return response
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
:license: Apache, see LICENSE for more details.
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
#def analyze(endpoints, truststores):
|
||||
# def analyze(endpoints, truststores):
|
||||
# results = {"headings": ["Endpoint"],
|
||||
# "results": [],
|
||||
# "time": datetime.now().strftime("#Y%m%d %H:%M:%S")}
|
||||
|
@ -37,7 +37,9 @@
|
|||
# log.debug(e)
|
||||
# if 'hostname' in str(e):
|
||||
# tests.append('pass')
|
||||
# result['details'].append("{}: This test passed ssl negotiation but failed hostname verification becuase the hostname is not included in the certificate".format(region))
|
||||
# result['details'].append(
|
||||
# "{}: This test passed ssl negotiation but failed hostname verification because \
|
||||
# the hostname is not included in the certificate".format(region))
|
||||
# elif 'certificate verify failed' in str(e):
|
||||
# tests.append('fail')
|
||||
# result['details'].append("{}: This test failed to verify the SSL certificate".format(region))
|
||||
|
|
|
@ -28,7 +28,7 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
|
|||
|
||||
from lemur.users import service as user_service
|
||||
from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed, \
|
||||
AuthorityCreatorNeed, AuthorityOwnerNeed, ViewRoleCredentialsNeed
|
||||
AuthorityCreatorNeed, ViewRoleCredentialsNeed
|
||||
|
||||
|
||||
def base64url_decode(data):
|
||||
|
@ -143,7 +143,6 @@ def fetch_token_header(token):
|
|||
raise jwt.DecodeError('Invalid header padding')
|
||||
|
||||
|
||||
|
||||
@identity_loaded.connect
|
||||
def on_identity_loaded(sender, identity):
|
||||
"""
|
||||
|
@ -187,5 +186,3 @@ class AuthenticatedResource(Resource):
|
|||
|
||||
def __init__(self):
|
||||
super(AuthenticatedResource, self).__init__()
|
||||
|
||||
|
||||
|
|
|
@ -9,16 +9,16 @@ import jwt
|
|||
import base64
|
||||
import requests
|
||||
|
||||
from flask import g, Blueprint, current_app, abort
|
||||
from flask import g, Blueprint, current_app
|
||||
|
||||
from flask.ext.restful import reqparse, Resource, Api
|
||||
from flask.ext.principal import Identity, identity_changed
|
||||
|
||||
from lemur.auth.permissions import admin_permission
|
||||
from lemur.common.utils import get_psuedo_random_string
|
||||
|
||||
from lemur.users import service as user_service
|
||||
from lemur.roles import service as role_service
|
||||
from lemur.certificates import service as cert_service
|
||||
from lemur.auth.service import AuthenticatedResource, create_token, fetch_token_header, get_rsa_public_key
|
||||
from lemur.auth.service import create_token, fetch_token_header, get_rsa_public_key
|
||||
|
||||
|
||||
mod = Blueprint('auth', __name__)
|
||||
|
@ -203,7 +203,7 @@ class Ping(Resource):
|
|||
|
||||
user = user_service.create(
|
||||
profile['email'],
|
||||
cert_service.create_challenge(),
|
||||
get_psuedo_random_string(),
|
||||
profile['email'],
|
||||
True,
|
||||
profile.get('thumbnailPhotoUrl'),
|
||||
|
@ -222,7 +222,7 @@ class Ping(Resource):
|
|||
profile['email'],
|
||||
profile['email'],
|
||||
True,
|
||||
profile.get('thumbnailPhotoUrl'), # incase profile isn't google+ enabled
|
||||
profile.get('thumbnailPhotoUrl'), # Encase profile isn't google+ enabled
|
||||
roles
|
||||
)
|
||||
|
||||
|
@ -234,5 +234,3 @@ class Ping(Resource):
|
|||
|
||||
api.add_resource(Login, '/auth/login', endpoint='login')
|
||||
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import lemur.certificates.service as cert_service
|
|||
|
||||
from lemur.plugins.base import plugins
|
||||
|
||||
|
||||
def update(authority_id, active=None, roles=None):
|
||||
"""
|
||||
Update a an authority with new values.
|
||||
|
@ -30,7 +31,7 @@ def update(authority_id, active=None, roles=None):
|
|||
"""
|
||||
authority = get(authority_id)
|
||||
if roles:
|
||||
authority = database.update_list(authority, 'roles', Role, roles)
|
||||
authority = database.update_list(authority, 'roles', Role, roles)
|
||||
|
||||
if active:
|
||||
authority.active = active
|
||||
|
@ -62,9 +63,9 @@ def create(kwargs):
|
|||
for r in issuer_roles:
|
||||
role = role_service.create(
|
||||
r['name'],
|
||||
password=r['password'],
|
||||
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
||||
username=r['username'])
|
||||
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':
|
||||
|
@ -132,7 +133,7 @@ def get_authority_role(ca_name):
|
|||
"""
|
||||
if g.current_user.is_admin:
|
||||
authority = get_by_name(ca_name)
|
||||
#TODO we should pick admin ca roles for admin
|
||||
# TODO we should pick admin ca roles for admin
|
||||
return authority.roles[0]
|
||||
else:
|
||||
for role in g.current_user.roles:
|
||||
|
@ -156,7 +157,7 @@ def render(args):
|
|||
|
||||
if filt:
|
||||
terms = filt.split(';')
|
||||
if 'active' in filt: # this is really weird but strcmp seems to not work here??
|
||||
if 'active' in filt: # this is really weird but strcmp seems to not work here??
|
||||
query = query.filter(Authority.active == terms[1])
|
||||
else:
|
||||
query = database.filter(query, Authority, terms)
|
||||
|
|
|
@ -183,8 +183,8 @@ class AuthoritiesList(AuthenticatedResource):
|
|||
self.reqparse.add_argument('caDescription', type=str, location='json', required=False)
|
||||
self.reqparse.add_argument('ownerEmail', type=str, location='json', required=True)
|
||||
self.reqparse.add_argument('caDN', type=dict, location='json', required=False)
|
||||
self.reqparse.add_argument('validityStart', type=str, location='json', required=False) # TODO validate
|
||||
self.reqparse.add_argument('validityEnd', type=str, location='json', required=False) # TODO validate
|
||||
self.reqparse.add_argument('validityStart', type=str, location='json', required=False) # TODO validate
|
||||
self.reqparse.add_argument('validityEnd', type=str, location='json', required=False) # TODO validate
|
||||
self.reqparse.add_argument('extensions', type=dict, location='json', required=False)
|
||||
self.reqparse.add_argument('pluginName', type=str, location='json', required=True)
|
||||
self.reqparse.add_argument('caType', type=str, location='json', required=False)
|
||||
|
|
|
@ -53,6 +53,7 @@ class UnableToCreateCSR(LemurException):
|
|||
def __str__(self):
|
||||
return repr(self.data['message'])
|
||||
|
||||
|
||||
class UnableToCreatePrivateKey(LemurException):
|
||||
def __init__(self):
|
||||
self.code = 500
|
||||
|
@ -63,6 +64,7 @@ class UnableToCreatePrivateKey(LemurException):
|
|||
def __str__(self):
|
||||
return repr(self.data['message'])
|
||||
|
||||
|
||||
class MissingFiles(LemurException):
|
||||
def __init__(self, path):
|
||||
self.code = 500
|
||||
|
@ -84,4 +86,3 @@ class NoPersistanceFound(LemurException):
|
|||
|
||||
def __str__(self):
|
||||
return repr(self.data['message'])
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ from lemur.database import db
|
|||
|
||||
from lemur.domains.models import Domain
|
||||
|
||||
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE, NONSTANDARD_NAMING_TEMPLATE
|
||||
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE
|
||||
from lemur.models import certificate_associations, certificate_destination_associations
|
||||
|
||||
|
||||
|
@ -51,6 +51,7 @@ def create_name(issuer, not_before, not_after, subject, san):
|
|||
# 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("-", "")
|
||||
disallowed_chars = disallowed_chars.replace(".", "")
|
||||
temp = temp.replace('*', "WILDCARD")
|
||||
temp = temp.translate(None, disallowed_chars)
|
||||
# white space is silly too
|
||||
|
@ -76,7 +77,7 @@ def cert_get_domains(cert):
|
|||
return the common name.
|
||||
|
||||
:param cert:
|
||||
:return: List of domainss
|
||||
:return: List of domains
|
||||
"""
|
||||
domains = []
|
||||
try:
|
||||
|
@ -86,6 +87,7 @@ def cert_get_domains(cert):
|
|||
domains.append(entry)
|
||||
except Exception as e:
|
||||
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
|
||||
|
||||
return domains
|
||||
|
||||
|
||||
|
@ -110,6 +112,7 @@ def cert_is_san(cert):
|
|||
if len(cert_get_domains(cert)) > 1:
|
||||
return True
|
||||
|
||||
|
||||
def cert_is_wildcard(cert):
|
||||
"""
|
||||
Determines if certificate is a wildcard certificate.
|
||||
|
@ -121,6 +124,9 @@ def cert_is_wildcard(cert):
|
|||
if len(domains) == 1 and domains[0][0:1] == "*":
|
||||
return True
|
||||
|
||||
if cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value[0:1] == "*":
|
||||
return True
|
||||
|
||||
|
||||
def cert_get_bitstrength(cert):
|
||||
"""
|
||||
|
@ -197,8 +203,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'))) # TODO deprecate
|
||||
csr_config = Column(Text()) # TODO deprecate
|
||||
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))
|
||||
|
@ -266,4 +272,3 @@ class Certificate(db.Model):
|
|||
|
||||
def as_dict(self):
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
import arrow
|
||||
import string
|
||||
import random
|
||||
|
||||
from sqlalchemy import func, or_
|
||||
from flask import g, current_app
|
||||
|
@ -27,7 +25,6 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
|
||||
|
||||
def get(cert_id):
|
||||
"""
|
||||
Retrieves certificate by it's ID.
|
||||
|
@ -106,7 +103,6 @@ def mint(issuer_options):
|
|||
|
||||
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)
|
||||
|
||||
|
@ -212,8 +208,8 @@ def render(args):
|
|||
time_range = args.pop('time_range')
|
||||
destination_id = args.pop('destination_id')
|
||||
show = args.pop('show')
|
||||
owner = args.pop('owner')
|
||||
creator = args.pop('creator') # TODO we should enabling filtering by owner
|
||||
# owner = args.pop('owner')
|
||||
# creator = args.pop('creator') # TODO we should enabling filtering by owner
|
||||
|
||||
filt = args.pop('filter')
|
||||
|
||||
|
@ -235,7 +231,7 @@ def render(args):
|
|||
|
||||
if 'destination' in terms:
|
||||
query = query.filter(Certificate.destinations.any(Destination.id == terms[1]))
|
||||
elif 'active' in filt: # this is really weird but strcmp seems to not work here??
|
||||
elif 'active' in filt: # this is really weird but strcmp seems to not work here??
|
||||
query = query.filter(Certificate.active == terms[1])
|
||||
else:
|
||||
query = database.filter(query, Certificate, terms)
|
||||
|
@ -288,7 +284,7 @@ def create_csr(csr_config):
|
|||
x509.BasicConstraints(ca=False, path_length=None), critical=True,
|
||||
)
|
||||
|
||||
#for k, v in csr_config.get('extensions', {}).items():
|
||||
# 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,
|
||||
|
@ -354,16 +350,6 @@ def create_csr(csr_config):
|
|||
|
||||
return csr, pem
|
||||
|
||||
def create_challenge():
|
||||
"""
|
||||
Create a random and strongish csr challenge.
|
||||
"""
|
||||
challenge = ''.join(random.choice(string.ascii_uppercase) for x in range(6))
|
||||
challenge += ''.join(random.choice("~!@#$%^&*()_+") for x in range(6))
|
||||
challenge += ''.join(random.choice(string.ascii_lowercase) for x in range(6))
|
||||
challenge += ''.join(random.choice(string.digits) for x in range(6))
|
||||
return challenge
|
||||
|
||||
|
||||
def stats(**kwargs):
|
||||
"""
|
||||
|
@ -405,5 +391,3 @@ def stats(**kwargs):
|
|||
values.append(count)
|
||||
|
||||
return {'labels': keys, 'values': values}
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from lemur.certificates import service as cert_service
|
|||
from lemur.plugins.base import plugins
|
||||
from lemur.plugins.bases.source import SourcePlugin
|
||||
|
||||
|
||||
def sync():
|
||||
for plugin in plugins:
|
||||
new = 0
|
||||
|
@ -42,5 +43,4 @@ def sync():
|
|||
|
||||
# TODO associated cert with source
|
||||
# TODO update cert if found from different source
|
||||
# TODO dissassociate source if missing
|
||||
|
||||
# TODO disassociate source if missing
|
||||
|
|
|
@ -30,7 +30,7 @@ def ocsp_verify(cert_path, issuer_chain_path):
|
|||
url, err = p1.communicate()
|
||||
|
||||
p2 = subprocess.Popen(['openssl', 'ocsp', '-issuer', issuer_chain_path,
|
||||
'-cert', cert_path, "-url", url.strip()], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
'-cert', cert_path, "-url", url.strip()], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
message, err = p2.communicate()
|
||||
if 'error' in message or 'Error' in message:
|
||||
|
|
|
@ -51,7 +51,7 @@ def valid_authority(authority_options):
|
|||
"""
|
||||
Defends against invalid authorities
|
||||
|
||||
:param authority_name:
|
||||
:param authority_options:
|
||||
:return: :raise ValueError:
|
||||
"""
|
||||
name = authority_options['name']
|
||||
|
@ -76,7 +76,7 @@ def pem_str(value, name):
|
|||
"""
|
||||
try:
|
||||
x509.load_pem_x509_certificate(str(value), default_backend())
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name))
|
||||
return value
|
||||
|
||||
|
@ -91,12 +91,11 @@ def private_key_str(value, name):
|
|||
"""
|
||||
try:
|
||||
serialization.load_pem_private_key(str(value), None, backend=default_backend())
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name))
|
||||
return value
|
||||
|
||||
|
||||
|
||||
class CertificatesList(AuthenticatedResource):
|
||||
""" Defines the 'certificates' endpoint """
|
||||
def __init__(self):
|
||||
|
@ -274,8 +273,8 @@ class CertificatesList(AuthenticatedResource):
|
|||
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||
self.reqparse.add_argument('elbs', type=list, location='json')
|
||||
self.reqparse.add_argument('owner', type=str, location='json')
|
||||
self.reqparse.add_argument('validityStart', type=str, location='json') # parse date
|
||||
self.reqparse.add_argument('validityEnd', type=str, location='json') # parse date
|
||||
self.reqparse.add_argument('validityStart', type=str, location='json') # TODO validate
|
||||
self.reqparse.add_argument('validityEnd', type=str, location='json') # TODO validate
|
||||
self.reqparse.add_argument('authority', type=valid_authority, location='json')
|
||||
self.reqparse.add_argument('description', type=str, location='json')
|
||||
self.reqparse.add_argument('country', type=str, location='json')
|
||||
|
|
|
@ -10,6 +10,7 @@ from flask import Blueprint
|
|||
|
||||
mod = Blueprint('healthCheck', __name__)
|
||||
|
||||
|
||||
@mod.route('/healthcheck')
|
||||
def health():
|
||||
return 'ok'
|
|
@ -8,6 +8,7 @@
|
|||
"""
|
||||
from flask import current_app
|
||||
|
||||
|
||||
# inspired by https://github.com/getsentry/sentry
|
||||
class InstanceManager(object):
|
||||
def __init__(self, class_list=None, instances=True):
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from flask import current_app
|
||||
import boto.ses
|
||||
from flask import current_app
|
||||
|
||||
from lemur.templates.config import env
|
||||
|
||||
|
@ -22,8 +22,7 @@ def send(subject, data, email_type, recipients):
|
|||
:param recipients:
|
||||
"""
|
||||
conn = boto.connect_ses()
|
||||
#jinja template depending on type
|
||||
# jinja template depending on type
|
||||
template = env.get_template('{}.html'.format(email_type))
|
||||
body = template.render(**data)
|
||||
conn.send_email(current_app.config.get("LEMUR_EMAIL"), subject, body, recipients, format='html')
|
||||
|
||||
|
|
|
@ -6,16 +6,28 @@
|
|||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
import string
|
||||
import random
|
||||
from functools import wraps
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from flask.ext.restful import marshal
|
||||
from flask.ext.restful.reqparse import RequestParser
|
||||
|
||||
from flask.ext.sqlalchemy import Pagination
|
||||
|
||||
|
||||
def get_psuedo_random_string():
|
||||
"""
|
||||
Create a random and strongish challenge.
|
||||
"""
|
||||
challenge = ''.join(random.choice(string.ascii_uppercase) for x in range(6)) # noqa
|
||||
challenge += ''.join(random.choice("~!@#$%^&*()_+") for x in range(6)) # noqa
|
||||
challenge += ''.join(random.choice(string.ascii_lowercase) for x in range(6))
|
||||
challenge += ''.join(random.choice(string.digits) for x in range(6)) # noqa
|
||||
return challenge
|
||||
|
||||
|
||||
class marshal_items(object):
|
||||
def __init__(self, fields, envelope=None):
|
||||
self.fields = fields
|
||||
|
|
|
@ -6,5 +6,3 @@
|
|||
SAN_NAMING_TEMPLATE = "SAN-{subject}-{issuer}-{not_before}-{not_after}"
|
||||
DEFAULT_NAMING_TEMPLATE = "{subject}-{issuer}-{not_before}-{not_after}"
|
||||
NONSTANDARD_NAMING_TEMPLATE = "{issuer}-{not_before}-{not_after}"
|
||||
|
||||
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from flask import current_app
|
||||
|
||||
from sqlalchemy import exc
|
||||
from sqlalchemy.sql import and_, or_
|
||||
|
||||
from lemur.extensions import db
|
||||
from lemur.exceptions import AttrNotFound, IntegrityError, DuplicateError
|
||||
from lemur.exceptions import AttrNotFound, DuplicateError
|
||||
|
||||
|
||||
def filter_none(kwargs):
|
||||
|
@ -126,7 +124,7 @@ def get(model, value, field="id"):
|
|||
query = session_query(model)
|
||||
try:
|
||||
return query.filter(getattr(model, field) == value).one()
|
||||
except:
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
|
@ -178,8 +176,9 @@ def delete(model):
|
|||
|
||||
:param model:
|
||||
"""
|
||||
db.session.delete(model)
|
||||
db.session.commit()
|
||||
if model:
|
||||
db.session.delete(model)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def filter(query, model, terms):
|
||||
|
@ -209,7 +208,7 @@ def sort(query, model, field, direction):
|
|||
direction = getattr(field, direction)
|
||||
query = query.order_by(direction())
|
||||
return query
|
||||
except AttributeError as e:
|
||||
except AttributeError:
|
||||
raise AttrNotFound(field)
|
||||
|
||||
|
||||
|
@ -274,6 +273,3 @@ def sort_and_page(query, model, args):
|
|||
query = sort(query, model, sort_by, sort_dir)
|
||||
|
||||
return paginate(query, page, count)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,9 +9,10 @@ from flask import make_response, request, current_app
|
|||
from functools import update_wrapper
|
||||
|
||||
|
||||
# this is only used for dev
|
||||
def crossdomain(origin=None, methods=None, headers=None,
|
||||
max_age=21600, attach_to_all=True,
|
||||
automatic_options=True):
|
||||
automatic_options=True): # pragma: no cover
|
||||
if methods is not None:
|
||||
methods = ', '.join(sorted(x.upper() for x in methods))
|
||||
|
||||
|
@ -44,12 +45,10 @@ def crossdomain(origin=None, methods=None, headers=None,
|
|||
h['Access-Control-Allow-Origin'] = origin
|
||||
h['Access-Control-Allow-Methods'] = get_methods()
|
||||
h['Access-Control-Max-Age'] = str(max_age)
|
||||
#if headers is not None:
|
||||
h['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization " # headers
|
||||
h['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization "
|
||||
h['Access-Control-Allow-Credentials'] = 'true'
|
||||
return resp
|
||||
|
||||
f.provide_automatic_options = False
|
||||
return update_wrapper(wrapped_function, f)
|
||||
return decorator
|
||||
|
||||
|
|
|
@ -7,18 +7,13 @@ ADMINS = frozenset([''])
|
|||
|
||||
THREADS_PER_PAGE = 8
|
||||
|
||||
#############
|
||||
## General ##
|
||||
#############
|
||||
# General
|
||||
|
||||
# These will need to be set to `True` if you are developing locally
|
||||
CORS = False
|
||||
debug = False
|
||||
|
||||
#############
|
||||
## Logging ##
|
||||
#############
|
||||
# Logging
|
||||
|
||||
LOG_LEVEL = "DEBUG"
|
||||
LOG_FILE = "lemur.log"
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ from lemur.database import db
|
|||
|
||||
from lemur.plugins.base import plugins
|
||||
|
||||
|
||||
class Destination(db.Model):
|
||||
__tablename__ = 'destinations'
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
|
|
@ -37,7 +37,7 @@ def update(destination_id, label, options, description):
|
|||
destination = get(destination_id)
|
||||
|
||||
destination.label = label
|
||||
description.options = options
|
||||
destination.options = options
|
||||
destination.description = description
|
||||
|
||||
return database.update(destination)
|
||||
|
@ -107,4 +107,3 @@ def render(args):
|
|||
query = database.sort(query, Destination, sort_by, sort_dir)
|
||||
|
||||
return database.paginate(query, page, count)
|
||||
|
||||
|
|
|
@ -229,7 +229,6 @@ class Destinations(AuthenticatedResource):
|
|||
return {'result': True}
|
||||
|
||||
|
||||
|
||||
class CertificateDestinations(AuthenticatedResource):
|
||||
""" Defines the 'certificate/<int:certificate_id/destinations'' endpoint """
|
||||
def __init__(self):
|
||||
|
@ -274,5 +273,5 @@ class CertificateDestinations(AuthenticatedResource):
|
|||
|
||||
api.add_resource(DestinationsList, '/destinations', endpoint='destinations')
|
||||
api.add_resource(Destinations, '/destinations/<int:destination_id>', endpoint='account')
|
||||
api.add_resource(CertificateDestinations, '/certificates/<int:certificate_id>/destinations', endpoint='certificateDestinations')
|
||||
|
||||
api.add_resource(CertificateDestinations, '/certificates/<int:certificate_id>/destinations',
|
||||
endpoint='certificateDestinations')
|
||||
|
|
|
@ -24,4 +24,3 @@ class Domain(db.Model):
|
|||
blob = self.as_dict()
|
||||
blob['certificates'] = [x.id for x in self.certificate]
|
||||
return blob
|
||||
|
||||
|
|
|
@ -61,4 +61,3 @@ def render(args):
|
|||
query = database.sort(query, Domain, sort_by, sort_dir)
|
||||
|
||||
return database.paginate(query, page, count)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||
"""
|
||||
from sqlalchemy import Column, BigInteger, String, ForeignKey, DateTime, PassiveDefault, func
|
||||
from sqlalchemy import Column, BigInteger, String, DateTime, PassiveDefault, func
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from lemur.database import db
|
||||
|
@ -16,7 +16,7 @@ from lemur.listeners.models import Listener
|
|||
class ELB(db.Model):
|
||||
__tablename__ = 'elbs'
|
||||
id = Column(BigInteger, primary_key=True)
|
||||
#account_id = Column(BigInteger, ForeignKey("accounts.id"), index=True)
|
||||
# account_id = Column(BigInteger, ForeignKey("accounts.id"), index=True)
|
||||
region = Column(String(32))
|
||||
name = Column(String(128))
|
||||
vpc_id = Column(String(128))
|
||||
|
|
|
@ -14,6 +14,7 @@ from lemur import database
|
|||
from lemur.elbs.models import ELB
|
||||
from lemur.listeners.models import Listener
|
||||
|
||||
|
||||
def get_all(account_id, elb_name):
|
||||
"""
|
||||
Retrieves all ELBs in a given account
|
||||
|
@ -112,7 +113,7 @@ def stats(**kwargs):
|
|||
|
||||
if kwargs.get('active') == 'true':
|
||||
query = query.join(ELB.listeners)
|
||||
query = query.filter(Listener.certificate_id != None)
|
||||
query = query.filter(Listener.certificate_id != None) # noqa
|
||||
|
||||
items = query.group_by(attr).all()
|
||||
|
||||
|
@ -121,5 +122,3 @@ def stats(**kwargs):
|
|||
if key:
|
||||
results.append({"key": key, "y": count})
|
||||
return results
|
||||
|
||||
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
|
||||
"""
|
||||
.. module: lemur.elbs.sync
|
||||
:platform: Unix
|
||||
:synopsis: This module attempts to sync with AWS and ensure that all elbs
|
||||
currently available in AWS are available in Lemur as well
|
||||
|
||||
: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.accounts import service as account_service
|
||||
from lemur.elbs import service as elb_service
|
||||
#from lemur.common.services.aws.elb import get_all_elbs, get_all_regions
|
||||
|
||||
|
||||
def create_new(known, aws, account):
|
||||
new = 0
|
||||
for elb in aws:
|
||||
for n in known:
|
||||
if elb.name == n.name:
|
||||
break
|
||||
else:
|
||||
new += 1
|
||||
current_app.logger.debug("Creating {0}".format(elb.name))
|
||||
try:
|
||||
elb_service.create(account, elb)
|
||||
except AttributeError as e:
|
||||
current_app.logger.exception(e)
|
||||
return new
|
||||
|
||||
|
||||
def remove_missing(known, aws):
|
||||
deleted = 0
|
||||
for ke in known:
|
||||
for elb in aws:
|
||||
if elb.name == ke.name:
|
||||
break
|
||||
else:
|
||||
deleted += 1
|
||||
current_app.logger.debug("Deleting {0}".format(ke.name))
|
||||
elb_service.delete(ke.id)
|
||||
return deleted
|
||||
|
||||
|
||||
def sync_all_elbs():
|
||||
for account in account_service.get_all():
|
||||
regions = get_all_regions()
|
||||
for region in regions:
|
||||
current_app.logger.info("Importing ELBs from '{0}/{1}/{2}'... ".format(account.account_number, account.label, region))
|
||||
try:
|
||||
aws_elbs = get_all_elbs(account.account_number, region)
|
||||
except Exception as e:
|
||||
current_app.logger.error("Failed to get ELBS from '{0}/{1}/{2}' reason: {3}".format(
|
||||
account.label, account.account_number, region, e.message)
|
||||
)
|
||||
continue
|
||||
|
||||
known_elbs = elb_service.get_by_region_and_account(region, account.id)
|
||||
|
||||
new_elbs = create_new(known_elbs, aws_elbs, account)
|
||||
current_app.logger.info(
|
||||
"Created {0} new ELBs in '{1}/{2}/{3}'...".format(
|
||||
new_elbs, account.account_number, account.label, region))
|
||||
|
||||
deleted_elbs = remove_missing(known_elbs, aws_elbs)
|
||||
current_app.logger.info(
|
||||
"Deleted {0} missing ELBs from '{1}/{2}/{3}'...".format(
|
||||
deleted_elbs, account.account_number, account.label, region))
|
|
@ -3,7 +3,6 @@
|
|||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
:license: Apache, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
db = SQLAlchemy()
|
||||
|
||||
|
@ -15,4 +14,3 @@ bcrypt = Bcrypt()
|
|||
|
||||
from flask.ext.principal import Principal
|
||||
principal = Principal()
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ from logging.handlers import RotatingFileHandler
|
|||
|
||||
from flask import Flask
|
||||
from lemur.common.health import mod as health
|
||||
from lemur.exceptions import NoEncryptionKeyFound
|
||||
from lemur.extensions import db, migrate, principal
|
||||
|
||||
|
||||
|
@ -161,7 +160,6 @@ def install_plugins(app):
|
|||
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:
|
||||
|
|
|
@ -40,4 +40,3 @@ class Listener(db.Model):
|
|||
blob = self.as_dict()
|
||||
del blob['date_created']
|
||||
return blob
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ from lemur.listeners.models import Listener
|
|||
from lemur.elbs import service as elb_service
|
||||
from lemur.certificates import service as certificate_service
|
||||
|
||||
#from lemur.common.services.aws.elb import update_listeners, create_new_listeners, delete_listeners
|
||||
# from lemur.common.services.aws.elb import update_listeners, create_new_listeners, delete_listeners
|
||||
|
||||
|
||||
def verify_attachment(certificate_id, elb_account_number):
|
||||
|
@ -60,7 +60,7 @@ def create(elb_id, instance_protocol, instance_port, load_balancer_port, load_ba
|
|||
|
||||
cert = verify_attachment(certificate_id, account_number)
|
||||
listener_tuple = (load_balancer_port, instance_port, load_balancer_protocol, cert.get_art(account_number),)
|
||||
create_new_listeners(account_number, elb.region, elb.name, [listener_tuple])
|
||||
# create_new_listeners(account_number, elb.region, elb.name, [listener_tuple])
|
||||
|
||||
return {'message': 'Listener has been created'}
|
||||
|
||||
|
@ -98,7 +98,7 @@ def update(listener_id, **kwargs):
|
|||
|
||||
database.update(listener)
|
||||
listener_tuple = (listener.load_balancer_port, listener.instance_port, listener.load_balancer_protocol, arn,)
|
||||
update_listeners(account_number, elb.region, elb.name, [listener_tuple], ports)
|
||||
# update_listeners(account_number, elb.region, elb.name, [listener_tuple], ports)
|
||||
|
||||
return {'message': 'Listener has been updated'}
|
||||
|
||||
|
@ -106,7 +106,7 @@ def update(listener_id, **kwargs):
|
|||
def delete(listener_id):
|
||||
# first try to delete the listener in aws
|
||||
listener = get(listener_id)
|
||||
delete_listeners(listener.elb.account.account_number, listener.elb.region, listener.elb.name, [listener.load_balancer_port])
|
||||
# delete_listeners(listener.elb.account.account_number, listener.elb.region, listener.elb.name, [listener.load_balancer_port])
|
||||
# cleanup operation in lemur
|
||||
database.delete(listener)
|
||||
|
||||
|
@ -149,7 +149,7 @@ def stats(**kwargs):
|
|||
query = query.filter(ELB.account_id == kwargs.get('account_id'))
|
||||
|
||||
if kwargs.get('active') == 'true':
|
||||
query = query.filter(Listener.certificate_id != None)
|
||||
query = query.filter(Listener.certificate_id != None) # noqa
|
||||
|
||||
items = query.group_by(attr).all()
|
||||
results = []
|
||||
|
@ -157,6 +157,3 @@ def stats(**kwargs):
|
|||
if key:
|
||||
results.append({"key": key, "y": count})
|
||||
return results
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,19 +20,18 @@ from lemur.plugins.base import plugins
|
|||
|
||||
from lemur.certificates.verify import verify_string
|
||||
from lemur.certificates import sync
|
||||
from lemur.elbs.sync import sync_all_elbs
|
||||
|
||||
from lemur import create_app
|
||||
|
||||
# Needed to be imported so that SQLAlchemy create_all can find our models
|
||||
from lemur.users.models import User
|
||||
from lemur.roles.models import Role
|
||||
from lemur.authorities.models import Authority
|
||||
from lemur.certificates.models import Certificate
|
||||
from lemur.destinations.models import Destination
|
||||
from lemur.domains.models import Domain
|
||||
from lemur.elbs.models import ELB
|
||||
from lemur.listeners.models import Listener
|
||||
from lemur.users.models import User # noqa
|
||||
from lemur.roles.models import Role # noqa
|
||||
from lemur.authorities.models import Authority # noqa
|
||||
from lemur.certificates.models import Certificate # noqa
|
||||
from lemur.destinations.models import Destination # noqa
|
||||
from lemur.domains.models import Domain # noqa
|
||||
from lemur.elbs.models import ELB # noqa
|
||||
from lemur.listeners.models import Listener # noqa
|
||||
|
||||
manager = Manager(create_app)
|
||||
manager.add_option('-c', '--config', dest='config')
|
||||
|
@ -55,48 +54,42 @@ ADMINS = frozenset([''])
|
|||
|
||||
THREADS_PER_PAGE = 8
|
||||
|
||||
#############
|
||||
## General ##
|
||||
#############
|
||||
# General
|
||||
|
||||
# These will need to be set to `True` if you are developing locally
|
||||
CORS = False
|
||||
debug = False
|
||||
|
||||
# this is the secret key used by flask session management
|
||||
SECRET_KEY = '{flask_secret_key}'
|
||||
|
||||
# You should consider storing these separately from your config
|
||||
LEMUR_SECRET_TOKEN = '{secret_token}'
|
||||
LEMUR_TOKEN_SECRET = '{secret_token}'
|
||||
LEMUR_ENCRYPTION_KEY = '{encryption_key}'
|
||||
|
||||
# this is a list of domains as regexes that only admins can issue
|
||||
LEMUR_RESTRICTED_DOMAINS = []
|
||||
|
||||
#################
|
||||
## Mail Server ##
|
||||
#################
|
||||
# Mail Server
|
||||
|
||||
# Lemur currently only supports SES for sending email, this address
|
||||
# needs to be verified
|
||||
LEMUR_EMAIL = ''
|
||||
LEMUR_SECURITY_TEAM_EMAIL = []
|
||||
|
||||
#############
|
||||
## Logging ##
|
||||
#############
|
||||
# Logging
|
||||
|
||||
LOG_LEVEL = "DEBUG"
|
||||
LOG_FILE = "lemur.log"
|
||||
|
||||
|
||||
##############
|
||||
## Database ##
|
||||
##############
|
||||
# Database
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = ''
|
||||
# modify this if you are not using a local database
|
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://lemur:lemur@localhost:5432/lemur'
|
||||
|
||||
|
||||
#########
|
||||
## AWS ##
|
||||
#########
|
||||
# AWS
|
||||
|
||||
# Lemur will need STS assume role access to every destination you want to monitor
|
||||
#AWS_ACCOUNT_MAPPINGS = {{
|
||||
|
@ -129,6 +122,7 @@ SQLALCHEMY_DATABASE_URI = ''
|
|||
#VERSIGN_EMAIL = ''
|
||||
"""
|
||||
|
||||
|
||||
@MigrateCommand.command
|
||||
def create():
|
||||
database.db.create_all()
|
||||
|
@ -178,7 +172,8 @@ def generate_settings():
|
|||
"""
|
||||
output = CONFIG_TEMPLATE.format(
|
||||
encryption_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
||||
secret_token=base64.b64encode(os.urandom(KEY_LENGTH))
|
||||
secret_token=base64.b64encode(os.urandom(KEY_LENGTH)),
|
||||
flask_secret_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
||||
)
|
||||
|
||||
return output
|
||||
|
@ -207,7 +202,7 @@ class Sync(Command):
|
|||
sys.stdout.write("[!] Starting to sync with AWS!\n")
|
||||
try:
|
||||
sync.aws()
|
||||
#sync_all_elbs()
|
||||
# sync_all_elbs()
|
||||
sys.stdout.write("[+] Finished syncing with AWS!\n")
|
||||
except Exception as e:
|
||||
sys.stdout.write("[-] Syncing with AWS failed!\n")
|
||||
|
@ -480,8 +475,8 @@ def main():
|
|||
manager.add_command("show_urls", ShowUrls())
|
||||
manager.add_command("db", MigrateCommand)
|
||||
manager.add_command("init", InitializeApp())
|
||||
manager.add_command('create_user', CreateUser())
|
||||
manager.add_command('create_role', CreateRole())
|
||||
manager.add_command("create_user", CreateUser())
|
||||
manager.add_command("create_role", CreateRole())
|
||||
manager.add_command("sync", Sync())
|
||||
manager.run()
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
"""Refactors Accounts to Destinations
|
||||
|
||||
Revision ID: 3b718f59b8ce
|
||||
Revises: None
|
||||
Create Date: 2015-07-09 17:44:55.626221
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3b718f59b8ce'
|
||||
down_revision = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('certificate_account_associations')
|
||||
op.drop_table('accounts')
|
||||
op.add_column('destinations', sa.Column('plugin_name', sa.String(length=32), nullable=True))
|
||||
op.drop_index('ix_elbs_account_id', table_name='elbs')
|
||||
op.drop_constraint(u'elbs_account_id_fkey', 'elbs', type_='foreignkey')
|
||||
op.drop_column('elbs', 'account_id')
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('elbs', sa.Column('account_id', sa.BIGINT(), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key(u'elbs_account_id_fkey', 'elbs', 'accounts', ['account_id'], ['id'])
|
||||
op.create_index('ix_elbs_account_id', 'elbs', ['account_id'], unique=False)
|
||||
op.drop_column('destinations', 'plugin_name')
|
||||
op.create_table('accounts',
|
||||
sa.Column('id', sa.INTEGER(), server_default=sa.text(u"nextval('accounts_id_seq'::regclass)"), nullable=False),
|
||||
sa.Column('account_number', sa.VARCHAR(length=32), autoincrement=False, nullable=True),
|
||||
sa.Column('label', sa.VARCHAR(length=32), autoincrement=False, nullable=True),
|
||||
sa.Column('notes', sa.TEXT(), autoincrement=False, nullable=True),
|
||||
sa.PrimaryKeyConstraint('id', name=u'accounts_pkey'),
|
||||
sa.UniqueConstraint('account_number', name=u'accounts_account_number_key'),
|
||||
postgresql_ignore_search_path=False
|
||||
)
|
||||
op.create_table('certificate_account_associations',
|
||||
sa.Column('account_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column('certificate_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['account_id'], [u'accounts.id'], name=u'certificate_account_associations_account_id_fkey', ondelete=u'CASCADE'),
|
||||
sa.ForeignKeyConstraint(['certificate_id'], [u'certificates.id'], name=u'certificate_account_associations_certificate_id_fkey', ondelete=u'CASCADE')
|
||||
)
|
||||
### end Alembic commands ###
|
|
@ -14,17 +14,18 @@ from sqlalchemy import Column, Integer, ForeignKey
|
|||
from lemur.database import db
|
||||
|
||||
certificate_associations = db.Table('certificate_associations',
|
||||
Column('domain_id', Integer, ForeignKey('domains.id')),
|
||||
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
||||
)
|
||||
Column('domain_id', Integer, ForeignKey('domains.id')),
|
||||
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
||||
)
|
||||
|
||||
certificate_destination_associations = db.Table('certificate_destination_associations',
|
||||
Column('destination_id', Integer, ForeignKey('destinations.id', ondelete='cascade')),
|
||||
Column('certificate_id', Integer, ForeignKey('certificates.id', ondelete='cascade'))
|
||||
)
|
||||
Column('destination_id', Integer,
|
||||
ForeignKey('destinations.id', ondelete='cascade')),
|
||||
Column('certificate_id', Integer,
|
||||
ForeignKey('certificates.id', ondelete='cascade'))
|
||||
)
|
||||
|
||||
roles_users = db.Table('roles_users',
|
||||
Column('user_id', Integer, ForeignKey('users.id')),
|
||||
Column('role_id', Integer, ForeignKey('roles.id'))
|
||||
)
|
||||
|
||||
Column('user_id', Integer, ForeignKey('users.id')),
|
||||
Column('role_id', Integer, ForeignKey('roles.id'))
|
||||
)
|
||||
|
|
|
@ -58,7 +58,7 @@ def _find_superseded(domains):
|
|||
current_app.logger.info("Trying to resolve {0}".format(domain.name))
|
||||
|
||||
query = query.filter(Certificate.domains.any(Domain.name.in_([x.name for x in domains])))
|
||||
query = query.filter(Certificate.active == True)
|
||||
query = query.filter(Certificate.active == True) # noqa
|
||||
query = query.filter(Certificate.not_after >= arrow.utcnow().format('YYYY-MM-DD'))
|
||||
ss_list.extend(query.all())
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
from lemur.plugins.base import * # NOQA
|
||||
from lemur.plugins.bases import * # NOQA
|
||||
from lemur.plugins.base import * # noqa
|
||||
from lemur.plugins.bases import * # noqa
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
from __future__ import absolute_import, print_function
|
||||
|
||||
from lemur.plugins.base.manager import PluginManager
|
||||
from lemur.plugins.base.v1 import * # NOQA
|
||||
from lemur.plugins.base.v1 import * # noqa
|
||||
|
||||
plugins = PluginManager()
|
||||
register = plugins.register
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
from flask import current_app
|
||||
from lemur.common.managers import InstanceManager
|
||||
|
||||
|
||||
# inspired by https://github.com/getsentry/sentry
|
||||
class PluginManager(InstanceManager):
|
||||
def __iter__(self):
|
||||
|
@ -57,4 +58,3 @@ class PluginManager(InstanceManager):
|
|||
def unregister(self, cls):
|
||||
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
||||
return cls
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"""
|
||||
from threading import local
|
||||
|
||||
|
||||
# stolen from https://github.com/getsentry/sentry/
|
||||
class PluginMount(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from .destination import DestinationPlugin # NOQA
|
||||
from .issuer import IssuerPlugin # NOQA
|
||||
from .source import SourcePlugin
|
||||
from .destination import DestinationPlugin # noqa
|
||||
from .issuer import IssuerPlugin # noqa
|
||||
from .source import SourcePlugin # noqa
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
"""
|
||||
from lemur.plugins.base import Plugin
|
||||
|
||||
|
||||
class DestinationPlugin(Plugin):
|
||||
type = 'destination'
|
||||
|
||||
def upload(self):
|
||||
raise NotImplemented
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"""
|
||||
from lemur.plugins.base import Plugin
|
||||
|
||||
|
||||
class IssuerPlugin(Plugin):
|
||||
"""
|
||||
This is the base class from which all of the supported
|
||||
|
@ -20,4 +21,3 @@ class IssuerPlugin(Plugin):
|
|||
|
||||
def create_authority(self):
|
||||
raise NotImplemented
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"""
|
||||
from lemur.plugins.base import Plugin
|
||||
|
||||
|
||||
class SourcePlugin(Plugin):
|
||||
type = 'source'
|
||||
|
||||
|
@ -16,4 +17,3 @@ class SourcePlugin(Plugin):
|
|||
|
||||
def get_options(self):
|
||||
return {}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ def is_valid(listener_tuple):
|
|||
|
||||
return listener_tuple
|
||||
|
||||
|
||||
def get_all_regions():
|
||||
"""
|
||||
Retrieves all current EC2 regions.
|
||||
|
@ -49,6 +50,7 @@ def get_all_regions():
|
|||
regions.append(r.name)
|
||||
return regions
|
||||
|
||||
|
||||
def get_all_elbs(account_number, region):
|
||||
"""
|
||||
Fetches all elb objects for a given account and region.
|
||||
|
@ -74,7 +76,6 @@ def get_all_elbs(account_number, region):
|
|||
# return elbs
|
||||
|
||||
|
||||
|
||||
def attach_certificate(account_number, region, name, port, certificate_id):
|
||||
"""
|
||||
Attaches a certificate to a listener, throws exception
|
||||
|
@ -137,4 +138,3 @@ def delete_listeners(account_number, region, name, ports):
|
|||
:return:
|
||||
"""
|
||||
return assume_service(account_number, 'elb', region).delete_load_balancer_listeners(name, ports)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
.. module: lemur.common.services.aws.iam
|
||||
.. module: lemur.plugins.lemur_aws.iam
|
||||
:platform: Unix
|
||||
:synopsis: Contains helper functions for interactive with AWS IAM Apis.
|
||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||
|
@ -19,21 +19,6 @@ def get_name_from_arn(arn):
|
|||
return arn.split("/", 1)[1]
|
||||
|
||||
|
||||
def ssl_split(param_string):
|
||||
"""
|
||||
|
||||
:param param_string:
|
||||
:return:
|
||||
"""
|
||||
output = {}
|
||||
parts = str(param_string).split("/")
|
||||
for part in parts:
|
||||
if "=" in part:
|
||||
key, value = part.split("=", 1)
|
||||
output[key] = value
|
||||
return output
|
||||
|
||||
|
||||
def upload_cert(account_number, cert, private_key, cert_chain=None):
|
||||
"""
|
||||
Upload a certificate to AWS
|
||||
|
@ -44,7 +29,8 @@ def upload_cert(account_number, cert, private_key, cert_chain=None):
|
|||
:param cert_chain:
|
||||
:return:
|
||||
"""
|
||||
return assume_service(account_number, 'iam').upload_server_cert(cert.name, str(cert.body), str(private_key), cert_chain=str(cert_chain))
|
||||
return assume_service(account_number, 'iam').upload_server_cert(cert.name, str(cert.body), str(private_key),
|
||||
cert_chain=str(cert_chain))
|
||||
|
||||
|
||||
def delete_cert(account_number, cert):
|
||||
|
@ -109,5 +95,3 @@ def digest_aws_cert_response(response):
|
|||
chain = cert['certificate_chain']
|
||||
|
||||
return str(body), str(chain),
|
||||
|
||||
|
||||
|
|
|
@ -35,11 +35,11 @@ class AWSDestinationPlugin(DestinationPlugin):
|
|||
'helpMessage': 'Must be a valid AWS account number!',
|
||||
}
|
||||
]
|
||||
#'elb': {
|
||||
# 'name': {'type': 'name'},
|
||||
# 'region': {'type': 'str'},
|
||||
# 'port': {'type': 'int'}
|
||||
#}
|
||||
# 'elb': {
|
||||
# 'name': {'type': 'name'},
|
||||
# 'region': {'type': 'str'},
|
||||
# 'port': {'type': 'int'}
|
||||
# }
|
||||
|
||||
def upload(self, cert, private_key, cert_chain, options, **kwargs):
|
||||
iam.upload_cert(find_value('accountNumber', options), cert, private_key, cert_chain=cert_chain)
|
||||
|
@ -58,10 +58,22 @@ class AWSSourcePlugin(SourcePlugin):
|
|||
author = 'Kevin Glisson'
|
||||
author_url = 'https://github.com/netflix/lemur'
|
||||
|
||||
options = {
|
||||
'accountNumber': {'type': 'int'},
|
||||
'pollRate': {'type': 'int', 'default': '60'}
|
||||
}
|
||||
options = [
|
||||
{
|
||||
'name': 'accountNumber',
|
||||
'type': 'int',
|
||||
'required': True,
|
||||
'validation': '/^[0-9]{12,12}$/',
|
||||
'helpMessage': 'Must be a valid AWS account number!',
|
||||
},
|
||||
{
|
||||
'name': 'pollRate',
|
||||
'type': 'int',
|
||||
'required': False,
|
||||
'helpMessage': 'Rate in seconds to poll source for new information.',
|
||||
'default': '60',
|
||||
}
|
||||
]
|
||||
|
||||
def get_certificates(self, **kwargs):
|
||||
certs = []
|
||||
|
|
|
@ -25,17 +25,13 @@ def assume_service(account_number, service, region=None):
|
|||
|
||||
elif service in 'elb':
|
||||
return boto.ec2.elb.connect_to_region(
|
||||
region,
|
||||
aws_access_key_id=role.credentials.access_key,
|
||||
aws_secret_access_key=role.credentials.secret_key,
|
||||
security_token=role.credentials.session_token)
|
||||
region,
|
||||
aws_access_key_id=role.credentials.access_key,
|
||||
aws_secret_access_key=role.credentials.secret_key,
|
||||
security_token=role.credentials.session_token)
|
||||
|
||||
elif service in 'vpc':
|
||||
return boto.connect_vpc(
|
||||
aws_access_key_id=role.credentials.access_key,
|
||||
aws_secret_access_key=role.credentials.secret_key,
|
||||
security_token=role.credentials.session_token)
|
||||
|
||||
|
||||
|
||||
|
||||
aws_access_key_id=role.credentials.access_key,
|
||||
aws_secret_access_key=role.credentials.secret_key,
|
||||
security_token=role.credentials.session_token)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
from moto import mock_iam, mock_sts
|
||||
|
||||
from lemur.certificates.models import Certificate
|
||||
|
||||
from lemur.tests.certs import EXTERNAL_VALID_STR, PRIVATE_KEY_STR
|
||||
|
||||
|
||||
def test_get_name_from_arn():
|
||||
from lemur.plugins.lemur_aws.iam import get_name_from_arn
|
||||
arn = 'arn:aws:iam::123456789012:server-certificate/tttt2.netflixtest.net-NetflixInc-20150624-20150625'
|
||||
assert get_name_from_arn(arn) == 'tttt2.netflixtest.net-NetflixInc-20150624-20150625'
|
||||
|
||||
|
||||
@mock_sts()
|
||||
@mock_iam()
|
||||
def test_get_all_server_certs(app):
|
||||
from lemur.plugins.lemur_aws.iam import upload_cert, get_all_server_certs
|
||||
cert = Certificate(EXTERNAL_VALID_STR)
|
||||
upload_cert('123456789012', cert, PRIVATE_KEY_STR)
|
||||
certs = get_all_server_certs('123456789012')
|
||||
assert len(certs) == 1
|
||||
|
||||
|
||||
@mock_sts()
|
||||
@mock_iam()
|
||||
def test_get_cert_from_arn(app):
|
||||
from lemur.plugins.lemur_aws.iam import upload_cert, get_cert_from_arn
|
||||
cert = Certificate(EXTERNAL_VALID_STR)
|
||||
upload_cert('123456789012', cert, PRIVATE_KEY_STR)
|
||||
body, chain = get_cert_from_arn('arn:aws:iam::123456789012:server-certificate/tttt2.netflixtest.net-NetflixInc-20150624-20150625')
|
||||
assert body.replace('\n', '') == EXTERNAL_VALID_STR.replace('\n', '')
|
|
@ -23,7 +23,7 @@ from lemur.plugins import lemur_cloudca as cloudca
|
|||
|
||||
from lemur.authorities import service as authority_service
|
||||
|
||||
API_ENDPOINT = '/v1/ca/netflix' # TODO this should be configurable
|
||||
API_ENDPOINT = '/v1/ca/netflix' # TODO this should be configurable
|
||||
|
||||
|
||||
class CloudCAException(LemurException):
|
||||
|
@ -72,7 +72,8 @@ def get_default_issuance(options):
|
|||
if not options.get('validityStart') and not options.get('validityEnd'):
|
||||
start = arrow.utcnow()
|
||||
options['validityStart'] = start.floor('second').isoformat()
|
||||
options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY')).ceil('second').isoformat()
|
||||
options['validityEnd'] = start.replace(years=current_app.config.get('CLOUDCA_DEFAULT_VALIDITY'))\
|
||||
.ceil('second').isoformat()
|
||||
return options
|
||||
|
||||
|
||||
|
@ -95,7 +96,8 @@ def convert_date_to_utc_time(date):
|
|||
:return:
|
||||
"""
|
||||
d = arrow.get(date)
|
||||
return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year).replace(microsecond=0)
|
||||
return arrow.utcnow().replace(day=d.naive.day).replace(month=d.naive.month).replace(year=d.naive.year)\
|
||||
.replace(microsecond=0)
|
||||
|
||||
|
||||
def process_response(response):
|
||||
|
@ -152,7 +154,9 @@ class CloudCA(object):
|
|||
self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
|
||||
self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
|
||||
else:
|
||||
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
||||
current_app.logger.warning(
|
||||
"No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA"
|
||||
)
|
||||
|
||||
super(CloudCA, self).__init__(*args, **kwargs)
|
||||
|
||||
|
@ -203,7 +207,7 @@ class CloudCA(object):
|
|||
for ca in self.get(endpoint)['data']['caList']:
|
||||
try:
|
||||
authorities.append(ca['caName'])
|
||||
except AttributeError as e:
|
||||
except AttributeError:
|
||||
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
||||
|
||||
return authorities
|
||||
|
@ -235,7 +239,8 @@ class CloudCAIssuerPlugin(IssuerPlugin, CloudCA):
|
|||
options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
|
||||
options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).isoformat()
|
||||
|
||||
response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10, verify=self.ca_bundle)
|
||||
response = self.session.post(self.url + endpoint, data=dumps(remove_none(options)), timeout=10,
|
||||
verify=self.ca_bundle)
|
||||
|
||||
json = process_response(response)
|
||||
roles = []
|
||||
|
@ -326,7 +331,8 @@ class CloudCASourcePlugin(SourcePlugin, CloudCA):
|
|||
:return:
|
||||
"""
|
||||
endpoint = '{0}/getCert'.format(API_ENDPOINT)
|
||||
response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10, verify=self.ca_bundle)
|
||||
response = self.session.post(self.url + endpoint, data=dumps({'caName': ca_name}), timeout=10,
|
||||
verify=self.ca_bundle)
|
||||
raw = process_response(response)
|
||||
|
||||
certs = []
|
||||
|
|
|
@ -55,4 +55,3 @@ F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
|||
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ from flask import current_app
|
|||
from lemur.plugins.bases import IssuerPlugin
|
||||
from lemur.plugins import lemur_verisign as verisign
|
||||
from lemur.plugins.lemur_verisign import constants
|
||||
from lemur.common.utils import get_psuedo_random_string
|
||||
|
||||
|
||||
# https://support.venafi.com/entries/66445046-Info-VeriSign-Error-Codes
|
||||
|
@ -58,9 +59,57 @@ VERISIGN_ERRORS = {
|
|||
}
|
||||
|
||||
|
||||
def process_options(options):
|
||||
"""
|
||||
Processes and maps the incoming issuer options to fields/options that
|
||||
verisign understands
|
||||
|
||||
:param options:
|
||||
:return: dict or valid verisign options
|
||||
"""
|
||||
data = {
|
||||
'challenge': get_psuedo_random_string(),
|
||||
'serverType': 'Apache',
|
||||
'certProductType': 'Server',
|
||||
'firstName': current_app.config.get("VERISIGN_FIRST_NAME"),
|
||||
'lastName': current_app.config.get("VERISIGN_LAST_NAME"),
|
||||
'signatureAlgorithm': 'sha256WithRSAEncryption',
|
||||
'email': current_app.config.get("VERISIGN_EMAIL")
|
||||
}
|
||||
|
||||
if options.get('validityEnd'):
|
||||
end_date, period = get_default_issuance(options)
|
||||
data['specificEndDate'] = end_date
|
||||
data['validityPeriod'] = period
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_default_issuance(options):
|
||||
"""
|
||||
Gets the default time range for certificates
|
||||
|
||||
:param options:
|
||||
:return:
|
||||
"""
|
||||
specific_end_date = arrow.get(options['validityEnd']).replace(days=-1).format("MM/DD/YYYY")
|
||||
|
||||
now = arrow.utcnow()
|
||||
then = arrow.get(options['validityEnd'])
|
||||
|
||||
if then < now.replace(years=+1):
|
||||
validity_period = '1Y'
|
||||
elif then < now.replace(years=+2):
|
||||
validity_period = '2Y'
|
||||
else:
|
||||
raise Exception("Verisign issued certificates cannot exceed two years in validity")
|
||||
|
||||
return specific_end_date, validity_period
|
||||
|
||||
|
||||
def handle_response(content):
|
||||
"""
|
||||
Helper function that helps with parsing responses from the Verisign API.
|
||||
Helper function for parsing responses from the Verisign API.
|
||||
:param content:
|
||||
:return: :raise Exception:
|
||||
"""
|
||||
|
@ -99,29 +148,8 @@ class VerisignIssuerPlugin(IssuerPlugin):
|
|||
"""
|
||||
url = current_app.config.get("VERISIGN_URL") + '/enroll'
|
||||
|
||||
data = {
|
||||
'csr': csr,
|
||||
'challenge': issuer_options['challenge'],
|
||||
'serverType': 'Apache',
|
||||
'certProductType': 'Server',
|
||||
'firstName': current_app.config.get("VERISIGN_FIRST_NAME"),
|
||||
'lastName': current_app.config.get("VERISIGN_LAST_NAME"),
|
||||
'signatureAlgorithm': 'sha256WithRSAEncryption',
|
||||
'email': current_app.config.get("VERISIGN_EMAIL")
|
||||
}
|
||||
|
||||
if issuer_options.get('validityEnd'):
|
||||
data['specificEndDate'] = arrow.get(issuer_options['validityEnd']).replace(days=-1).format("MM/DD/YYYY")
|
||||
|
||||
now = arrow.utcnow()
|
||||
then = arrow.get(issuer_options['validityEnd'])
|
||||
|
||||
if then < now.replace(years=+1):
|
||||
data['validityPeriod'] = '1Y'
|
||||
elif then < now.replace(years=+2):
|
||||
data['validityPeriod'] = '2Y'
|
||||
else:
|
||||
raise Exception("Verisign issued certificates cannot exceed two years in validity")
|
||||
data = process_options(issuer_options)
|
||||
data['csr'] = csr
|
||||
|
||||
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
||||
|
||||
|
@ -151,4 +179,3 @@ class VerisignIssuerPlugin(IssuerPlugin):
|
|||
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
||||
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
||||
return handle_response(response.content)['Response']['Order']
|
||||
|
||||
|
|
|
@ -137,4 +137,3 @@ class PluginsTypeList(AuthenticatedResource):
|
|||
|
||||
api.add_resource(PluginsList, '/plugins', endpoint='plugins')
|
||||
api.add_resource(PluginsTypeList, '/plugins/<plugin_type>', endpoint='pluginType')
|
||||
|
||||
|
|
|
@ -36,4 +36,3 @@ class Role(db.Model):
|
|||
def serialize(self):
|
||||
blob = self.as_dict()
|
||||
return blob
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from lemur import database
|
|||
from lemur.roles.models import Role
|
||||
from lemur.users.models import User
|
||||
|
||||
|
||||
def update(role_id, name, description, users):
|
||||
"""
|
||||
Update a role
|
||||
|
@ -122,4 +123,3 @@ def render(args):
|
|||
query = database.sort(query, Role, sort_by, sort_dir)
|
||||
|
||||
return database.paginate(query, page, count)
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
|||
RestangularConfigurer.setBaseUrl('http://localhost:5000/api/1');
|
||||
RestangularConfigurer.setDefaultHttpFields({withCredentials: true});
|
||||
|
||||
RestangularConfigurer.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
|
||||
RestangularConfigurer.addResponseInterceptor(function (data, operation) {
|
||||
var extractedData;
|
||||
|
||||
// .. to look for getList operations
|
||||
if (operation === "getList") {
|
||||
if (operation === 'getList') {
|
||||
// .. and handle the data and meta data
|
||||
extractedData = data.items;
|
||||
extractedData.total = data.total;
|
||||
|
@ -79,7 +79,7 @@ lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
|||
return extractedData;
|
||||
});
|
||||
|
||||
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params, httpConfig) {
|
||||
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params) {
|
||||
// We want to make sure the user is auth'd before any requests
|
||||
if (!$auth.isAuthenticated()) {
|
||||
$location.path('/login');
|
||||
|
@ -97,7 +97,7 @@ lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
|||
newParams.sortDir = params[item];
|
||||
} else if (item.indexOf(f) > -1) {
|
||||
var key = regExp.exec(item)[1];
|
||||
newParams['filter'] = key + ";" + params[item];
|
||||
newParams.filter = key + ';' + params[item];
|
||||
} else {
|
||||
newParams[item] = params[item];
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ angular.module('lemur')
|
|||
AuthenticationService.authenticate = function (provider) {
|
||||
$auth.authenticate(provider)
|
||||
.then(
|
||||
function (user) {
|
||||
function () {
|
||||
UserService.getCurrentUser();
|
||||
$rootScope.$emit('user:login');
|
||||
$location.url('/certificates');
|
||||
|
@ -41,7 +41,7 @@ angular.module('lemur')
|
|||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
AuthenticationService.logout = function () {
|
||||
if (!$auth.isAuthenticated()) {
|
||||
|
@ -56,7 +56,7 @@ angular.module('lemur')
|
|||
body: 'You have been successfully logged out.'
|
||||
});
|
||||
$location.path('/');
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
@ -19,10 +19,10 @@ angular.module('lemur')
|
|||
$scope.loading = false;
|
||||
$scope.create = function (authority) {
|
||||
WizardHandler.wizard().context.loading = true;
|
||||
AuthorityService.create(authority).then(function (resposne) {
|
||||
AuthorityService.create(authority).then(function () {
|
||||
WizardHandler.wizard().context.loading = false;
|
||||
$modalInstance.close();
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
PluginService.get('issuer').then(function (plugins) {
|
||||
|
|
|
@ -35,7 +35,7 @@ angular.module('lemur')
|
|||
|
||||
$scope.getAuthorityStatus = function () {
|
||||
var def = $q.defer();
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}])
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}]);
|
||||
return def;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ angular.module('lemur')
|
|||
|
||||
$scope.create = function (certificate) {
|
||||
WizardHandler.wizard().context.loading = true;
|
||||
CertificateService.create(certificate).then(function (response) {
|
||||
CertificateService.create(certificate).then(function () {
|
||||
WizardHandler.wizard().context.loading = false;
|
||||
$modalInstance.close();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/**
|
||||
* Created by kglisson on 1/19/15.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('CertificateApi', function (LemurRestangular, DomainService) {
|
||||
LemurRestangular.extendModel('certificates', function (obj) {
|
||||
|
@ -102,7 +101,7 @@ angular.module('lemur')
|
|||
CertificateService.create = function (certificate) {
|
||||
certificate.attachSubAltName();
|
||||
return CertificateApi.post(certificate).then(
|
||||
function (response) {
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
|
@ -132,8 +131,8 @@ angular.module('lemur')
|
|||
};
|
||||
|
||||
CertificateService.upload = function (certificate) {
|
||||
CertificateApi.customPOST(certificate, "upload").then(
|
||||
function (response) {
|
||||
CertificateApi.customPOST(certificate, 'upload').then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
|
@ -163,7 +162,7 @@ angular.module('lemur')
|
|||
certificate.privateKey = response.key;
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
|
|
|
@ -48,7 +48,7 @@ angular.module('lemur')
|
|||
|
||||
$scope.getCertificateStatus = function () {
|
||||
var def = $q.defer();
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}])
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}]);
|
||||
return def;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur').
|
||||
filter('titleCase', function () {
|
||||
return function (str) {
|
||||
|
|
|
@ -7,9 +7,8 @@ angular.module('lemur')
|
|||
controller: 'DashboardController'
|
||||
});
|
||||
})
|
||||
.controller('DashboardController', function ($scope, $rootScope, $filter, $location, LemurRestangular, ngTableParams) {
|
||||
.controller('DashboardController', function ($scope, $rootScope, $filter, $location, LemurRestangular) {
|
||||
|
||||
var baseStats = LemurRestangular.all('stats');
|
||||
var baseAccounts = LemurRestangular.all('accounts');
|
||||
|
||||
baseAccounts.getList()
|
||||
|
@ -78,16 +77,16 @@ angular.module('lemur')
|
|||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'issuer'})
|
||||
.then(function (data) {
|
||||
$scope.issuers = data['items'];
|
||||
$scope.issuers = data.items;
|
||||
});
|
||||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'bits'})
|
||||
.then(function (data) {
|
||||
$scope.bits = data['items'];
|
||||
$scope.bits = data.items;
|
||||
});
|
||||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'not_after'})
|
||||
.then(function (data) {
|
||||
$scope.expiring = {labels: data['items']['labels'], values: [data['items']['values']]};
|
||||
$scope.expiring = {labels: data.items.labels, values: [data.items.values]};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ angular.module('lemur')
|
|||
DestinationService.update(destination).then(function () {
|
||||
$modalInstance.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('DomainApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('domains');
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('ELBApi', function (LemurRestangular, ListenerService) {
|
||||
.service('ELBApi', function (LemurRestangular) {
|
||||
LemurRestangular.extendModel('elbs', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
attachListener: function (listener) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('ListenerApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('listeners');
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('PluginApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('plugins');
|
||||
})
|
||||
.service('PluginService', function (PluginApi) {
|
||||
var PluginService = this;
|
||||
PluginService.get = function (type) {
|
||||
return PluginApi.customGETLIST(type).then(function (plugins) {
|
||||
return plugins;
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,3 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.service('RoleApi', function (LemurRestangular) {
|
||||
LemurRestangular.extendModel('roles', function (obj) {
|
||||
|
@ -108,7 +110,7 @@ angular.module('lemur')
|
|||
role.username = response.username;
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: role.name,
|
||||
|
|
|
@ -18,7 +18,7 @@ angular.module('lemur')
|
|||
UserService.update(user).then(function () {
|
||||
$modalInstance.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import unittest
|
||||
|
||||
|
||||
class LemurTestCase(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class LemurPluginTestCase(LemurTestCase):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
# This is just Python which means you can inherit and tweak settings
|
||||
|
||||
import os
|
||||
_basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
ADMINS = frozenset([''])
|
||||
|
||||
THREADS_PER_PAGE = 8
|
||||
|
||||
# General
|
||||
|
||||
# These will need to be set to `True` if you are developing locally
|
||||
CORS = False
|
||||
debug = False
|
||||
|
||||
TESTING = True
|
||||
|
||||
# this is the secret key used by flask session management
|
||||
SECRET_KEY = 'I/dVhOZNSMZMqrFJa5tWli6VQccOGudKerq3eWPMSzQNmHHVhMAQfQ=='
|
||||
|
||||
# You should consider storing these separately from your config
|
||||
LEMUR_TOKEN_SECRET = 'test'
|
||||
LEMUR_ENCRYPTION_KEY = 'jPd2xwxgVGXONqghHNq7/S761sffYSrT3UAgKwgtMxbqa0gmKYCfag=='
|
||||
|
||||
# this is a list of domains as regexes that only admins can issue
|
||||
LEMUR_RESTRICTED_DOMAINS = []
|
||||
|
||||
# Mail Server
|
||||
|
||||
# Lemur currently only supports SES for sending email, this address
|
||||
# needs to be verified
|
||||
LEMUR_EMAIL = ''
|
||||
LEMUR_SECURITY_TEAM_EMAIL = []
|
||||
|
||||
# Logging
|
||||
|
||||
LOG_LEVEL = "DEBUG"
|
||||
LOG_FILE = "lemur.log"
|
||||
|
||||
|
||||
# Database
|
||||
|
||||
# modify this if you are not using a local database
|
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://lemur:lemur@localhost:5432/lemur'
|
||||
|
||||
|
||||
# AWS
|
||||
|
||||
LEMUR_INSTANCE_PROFILE = 'Lemur'
|
||||
|
||||
# Issuers
|
||||
|
||||
# These will be dependent on which 3rd party that Lemur is
|
||||
# configured to use.
|
||||
|
||||
# CLOUDCA_URL = ''
|
||||
# CLOUDCA_PEM_PATH = ''
|
||||
# CLOUDCA_BUNDLE = ''
|
||||
|
||||
# number of years to issue if not specified
|
||||
# CLOUDCA_DEFAULT_VALIDITY = 2
|
||||
|
||||
VERISIGN_URL = 'http://example.com'
|
||||
VERISIGN_PEM_PATH = '~/'
|
||||
VERISIGN_FIRST_NAME = 'Jim'
|
||||
VERISIGN_LAST_NAME = 'Bob'
|
||||
VERSIGN_EMAIL = 'jim@example.com'
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import pytest
|
||||
|
||||
from lemur import create_app
|
||||
|
@ -33,14 +34,11 @@ def app():
|
|||
Creates a new Flask application for a test duration.
|
||||
Uses application factory `create_app`.
|
||||
"""
|
||||
app = create_app()
|
||||
app.config['TESTING'] = True
|
||||
app.config['LEMUR_ENCRYPTION_KEY'] = 'test'
|
||||
|
||||
ctx = app.app_context()
|
||||
_app = create_app(os.path.dirname(os.path.realpath(__file__)) + '/conf.py')
|
||||
ctx = _app.app_context()
|
||||
ctx.push()
|
||||
|
||||
yield app
|
||||
yield _app
|
||||
|
||||
ctx.pop()
|
||||
|
||||
|
@ -73,4 +71,3 @@ def session(db, request):
|
|||
@pytest.yield_fixture(scope="function")
|
||||
def client(app, session, client):
|
||||
yield client
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import pytest
|
||||
from lemur.authorities.views import *
|
||||
from lemur.authorities.views import * # noqa
|
||||
|
||||
#def test_crud(session):
|
||||
# def test_crud(session):
|
||||
# role = create('role1')
|
||||
# assert role.id > 0
|
||||
#
|
||||
|
@ -149,15 +148,3 @@ def test_admin_authorities_delete(client):
|
|||
|
||||
def test_admin_certificate_authorities_get(client):
|
||||
assert client.get(api.url_for(CertificateAuthority, certificate_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 404
|
||||
|
||||
|
||||
def test_admin_certificate_authorities_post(client):
|
||||
assert client.post(api.url_for(CertificateAuthority, certficate_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||
|
||||
|
||||
def test_admin_certificate_authorities_put(client):
|
||||
assert client.put(api.url_for(CertificateAuthority, certificate_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||
|
||||
|
||||
def test_admin_certificate_authorities_delete(client):
|
||||
assert client.delete(api.url_for(CertificateAuthority, certificate_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import pytest
|
||||
from lemur.certificates.views import *
|
||||
|
||||
def test_valid_authority(session):
|
||||
assert 1 == 2
|
||||
from lemur.certificates.views import * # noqa
|
||||
|
||||
|
||||
def test_pem_str():
|
||||
|
@ -40,18 +37,6 @@ def test_create_basic_csr():
|
|||
assert name.value in csr_config.values()
|
||||
|
||||
|
||||
def test_import_certificate():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_mint():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_disassociate_aws_account():
|
||||
assert 1 == 2
|
||||
|
||||
|
||||
def test_cert_get_cn():
|
||||
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
||||
from lemur.certificates.models import cert_get_cn
|
||||
|
@ -59,19 +44,19 @@ def test_cert_get_cn():
|
|||
assert cert_get_cn(INTERNAL_VALID_LONG_CERT) == 'long.lived.com'
|
||||
|
||||
|
||||
def test_cert_get_domains():
|
||||
def test_cert_get_subAltDomains():
|
||||
from lemur.tests.certs import INTERNAL_VALID_SAN_CERT, INTERNAL_VALID_LONG_CERT
|
||||
from lemur.certificates.models import cert_get_domains
|
||||
|
||||
assert cert_get_domains(INTERNAL_VALID_LONG_CERT) == ['long.lived.com']
|
||||
assert cert_get_domains(INTERNAL_VALID_SAN_CERT) == ['example2.long.com', 'example3.long.com', 'san.example.com']
|
||||
assert cert_get_domains(INTERNAL_VALID_LONG_CERT) == []
|
||||
assert cert_get_domains(INTERNAL_VALID_SAN_CERT) == ['example2.long.com', 'example3.long.com']
|
||||
|
||||
|
||||
def test_cert_is_san():
|
||||
from lemur.tests.certs import INTERNAL_VALID_SAN_CERT, INTERNAL_VALID_LONG_CERT
|
||||
from lemur.certificates.models import cert_is_san
|
||||
|
||||
assert cert_is_san(INTERNAL_VALID_LONG_CERT) == False
|
||||
assert cert_is_san(INTERNAL_VALID_LONG_CERT) == None
|
||||
assert cert_is_san(INTERNAL_VALID_SAN_CERT) == True
|
||||
|
||||
|
||||
|
@ -79,7 +64,7 @@ def test_cert_is_wildcard():
|
|||
from lemur.tests.certs import INTERNAL_VALID_WILDCARD_CERT, INTERNAL_VALID_LONG_CERT
|
||||
from lemur.certificates.models import cert_is_wildcard
|
||||
assert cert_is_wildcard(INTERNAL_VALID_WILDCARD_CERT) == True
|
||||
assert cert_is_wildcard(INTERNAL_VALID_LONG_CERT) == False
|
||||
assert cert_is_wildcard(INTERNAL_VALID_LONG_CERT) == None
|
||||
|
||||
|
||||
def test_cert_get_bitstrength():
|
||||
|
@ -87,6 +72,7 @@ def test_cert_get_bitstrength():
|
|||
from lemur.certificates.models import cert_get_bitstrength
|
||||
assert cert_get_bitstrength(INTERNAL_VALID_LONG_CERT) == 2048
|
||||
|
||||
|
||||
def test_cert_get_issuer():
|
||||
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
||||
from lemur.certificates.models import cert_get_issuer
|
||||
|
@ -324,4 +310,3 @@ def test_admin_certificate_credentials_delete(client):
|
|||
|
||||
def test_admin_certificate_credentials_patch(client):
|
||||
assert client.patch(api.url_for(CertificatePrivateKey, certificate_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
TEST_CSR = """
|
||||
# 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 = "Operations" # 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}
|
||||
"""
|
|
@ -1,15 +1,15 @@
|
|||
from lemur.destinations.service import *
|
||||
from lemur.destinations.views import *
|
||||
from lemur.destinations.service import * # noqa
|
||||
from lemur.destinations.views import * # noqa
|
||||
|
||||
from json import dumps
|
||||
|
||||
|
||||
def test_crud(session):
|
||||
destination = create('111111', 'destination1')
|
||||
destination = create('testdest', 'aws-destination', {}, description='destination1')
|
||||
assert destination.id > 0
|
||||
|
||||
destination = update(destination.id, 11111, 'destination2')
|
||||
assert destination.label == 'destination2'
|
||||
destination = update(destination.id, 'testdest2', {}, 'destination2')
|
||||
assert destination.label == 'testdest2'
|
||||
|
||||
assert len(get_all()) == 1
|
||||
|
||||
|
@ -40,6 +40,7 @@ def test_destination_patch(client):
|
|||
VALID_USER_HEADER_TOKEN = {
|
||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
||||
|
||||
|
||||
def test_auth_destination_get(client):
|
||||
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
@ -63,6 +64,7 @@ def test_auth_destination_patch(client):
|
|||
VALID_ADMIN_HEADER_TOKEN = {
|
||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
||||
|
||||
|
||||
def test_admin_destination_get(client):
|
||||
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
@ -76,7 +78,7 @@ def test_admin_destination_put(client):
|
|||
|
||||
|
||||
def test_admin_destination_delete(client):
|
||||
assert client.delete(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 500
|
||||
assert client.delete(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
||||
def test_admin_destination_patch(client):
|
||||
|
@ -119,13 +121,13 @@ def test_admin_destinations_get(client):
|
|||
|
||||
def test_admin_destinations_crud(client):
|
||||
assert client.post(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||
data = {'destinationNumber': 111, 'label': 'test', 'comments': 'test'}
|
||||
resp = client.post(api.url_for(DestinationsList), data=dumps(data), content_type='application/json', headers=VALID_ADMIN_HEADER_TOKEN)
|
||||
data = {'plugin': {'slug': 'aws-destination', 'pluginOptions': {}}, 'label': 'test', 'description': 'test'}
|
||||
resp = client.post(api.url_for(DestinationsList), data=dumps(data), content_type='application/json', headers=VALID_ADMIN_HEADER_TOKEN)
|
||||
assert resp.status_code == 200
|
||||
assert client.get(api.url_for(Destinations, destination_id=resp.json['id']), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json == {'items': [{'destinationNumber': 111, 'label': 'test', 'comments': 'test', 'id': 2}], 'total': 1}
|
||||
assert resp.json['items'][0]['description'] == 'test'
|
||||
assert client.delete(api.url_for(Destinations, destination_id=2), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||
assert resp.status_code == 200
|
|
@ -1,4 +1,5 @@
|
|||
from lemur.domains.views import *
|
||||
from lemur.domains.views import * # noqa
|
||||
|
||||
|
||||
def test_domain_get(client):
|
||||
assert client.get(api.url_for(Domains, domain_id=1)).status_code == 401
|
||||
|
@ -23,6 +24,7 @@ def test_domain_patch(client):
|
|||
VALID_USER_HEADER_TOKEN = {
|
||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
||||
|
||||
|
||||
def test_auth_domain_get(client):
|
||||
assert client.get(api.url_for(Domains, domain_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
@ -46,6 +48,7 @@ def test_auth_domain_patch(client):
|
|||
VALID_ADMIN_HEADER_TOKEN = {
|
||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
||||
|
||||
|
||||
def test_admin_domain_get(client):
|
||||
assert client.get(api.url_for(Domains, domain_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
@ -119,5 +122,6 @@ def test_certificate_domains_patch(client):
|
|||
def test_auth_certificate_domains_get(client):
|
||||
assert client.get(api.url_for(CertificateDomains, certificate_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
||||
|
||||
|
||||
def test_admin_certificate_domains_get(client):
|
||||
assert client.get(api.url_for(CertificateDomains, certificate_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import boto
|
||||
from lemur.tests import LemurTestCase
|
||||
|
||||
from moto import mock_elb, mock_sts
|
||||
|
||||
|
||||
#class ELBTestCase(LemurTestCase):
|
||||
# @mock_sts
|
||||
# @mock_elb
|
||||
# def test_add_listener(self):
|
||||
# from lemur.common.services.aws.elb import create_new_listeners
|
||||
# conn = boto.connect_elb()
|
||||
# zones = ['us-east-1a', 'us-east-1b']
|
||||
# ports = [(80, 8080, 'http')]
|
||||
# conn.create_load_balancer('my-lb', zones, ports)
|
||||
# create_new_listeners('111', 'us-east-1', 'my-lb', listeners=[('443', '80', 'HTTP')])
|
||||
# balancer = conn.get_all_load_balancers()[0]
|
||||
# self.assertEqual(balancer.name, "my-lb")
|
||||
# self.assertEqual(len(balancer.listeners), 2)
|
||||
#
|
||||
# @mock_sts
|
||||
# @mock_elb
|
||||
# def test_update_listener(self):
|
||||
# from lemur.common.services.aws.elb import update_listeners
|
||||
# conn = boto.connect_elb()
|
||||
# zones = ['us-east-1a', 'us-east-1b']
|
||||
# ports = [(80, 8080, 'http')]
|
||||
# conn.create_load_balancer('my-lb', zones, ports)
|
||||
# update_listeners('111', 'us-east-1', 'my-lb', listeners=[('80', '7001', 'http')])
|
||||
# balancer = conn.get_all_load_balancers()[0]
|
||||
# listener = balancer.listeners[0]
|
||||
# self.assertEqual(listener.load_balancer_port, 80)
|
||||
# self.assertEqual(listener.instance_port, 7001)
|
||||
# self.assertEqual(listener.protocol, "HTTP")
|
||||
#
|
||||
# @mock_sts
|
||||
# @mock_elb
|
||||
# def test_set_certificate(self):
|
||||
# from lemur.common.services.aws.elb import attach_certificate
|
||||
# conn = boto.connect_elb()
|
||||
# zones = ['us-east-1a', 'us-east-1b']
|
||||
# ports = [(443, 7001, 'https', 'sslcert')]
|
||||
# conn.create_load_balancer('my-lb', zones, ports)
|
||||
# attach_certificate('1111', 'us-east-1', 'my-lb', 443, 'somecert')
|
||||
# balancer = conn.get_all_load_balancers()[0]
|
||||
# listener = balancer.listeners[0]
|
||||
# self.assertEqual(listener.load_balancer_port, 443)
|
||||
# self.assertEqual(listener.instance_port, 7001)
|
||||
# self.assertEqual(listener.protocol, "HTTPS")
|
||||
# self.assertEqual(listener.ssl_certificate_id, 'somecert')
|
||||
#
|
|
@ -1,35 +0,0 @@
|
|||
from lemur.tests import LemurTestCase
|
||||
|
||||
from lemur.certificates.models import Certificate
|
||||
|
||||
from moto import mock_iam, mock_sts
|
||||
|
||||
|
||||
#class IAMTestCase(LemurTestCase):
|
||||
# @mock_sts
|
||||
# @mock_iam
|
||||
# def test_get_all_server_certs(self):
|
||||
# from lemur.common.services.aws.iam import upload_cert, get_all_server_certs
|
||||
# cert = Certificate(TEST_CERT)
|
||||
# upload_cert('1111', cert, TEST_KEY)
|
||||
# certs = get_all_server_certs('1111')
|
||||
# self.assertEquals(len(certs), 1)
|
||||
#
|
||||
# @mock_sts
|
||||
# @mock_iam
|
||||
# def test_get_server_cert(self):
|
||||
# from lemur.common.services.aws.iam import upload_cert, get_cert_from_arn
|
||||
# cert = Certificate(TEST_CERT)
|
||||
# upload_cert('1111', cert, TEST_KEY)
|
||||
# body, chain = get_cert_from_arn('arn:aws:iam::123456789012:server-certificate/AHB-dfdsflkj.net-NetflixInc-20140525-20150525')
|
||||
# self.assertTrue(body)
|
||||
#
|
||||
# @mock_sts
|
||||
# @mock_iam
|
||||
# def test_upload_server_cert(self):
|
||||
# from lemur.common.services.aws.iam import upload_cert
|
||||
# cert = Certificate(TEST_CERT)
|
||||
# response = upload_cert('1111', cert, TEST_KEY)
|
||||
# self.assertEquals(response['upload_server_certificate_response']['upload_server_certificate_result']['server_certificate_metadata']['server_certificate_name'], 'AHB-dfdsflkj.net-NetflixInc-20140525-20150525')
|
||||
#
|
||||
#
|
|
@ -1,6 +1,6 @@
|
|||
from lemur.tests import LemurTestCase
|
||||
# from lemur.tests import LemurTestCase
|
||||
|
||||
#class ManagerTestCase(LemurTestCase):
|
||||
# class ManagerTestCase(LemurTestCase):
|
||||
# def test_validate_authority(self):
|
||||
# pass
|
||||
#
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from json import dumps
|
||||
from lemur.roles.service import *
|
||||
from lemur.roles.views import *
|
||||
from lemur.roles.service import * # noqa
|
||||
from lemur.roles.views import * # noqa
|
||||
|
||||
|
||||
def test_crud(session):
|
||||
|
|
|
@ -84,5 +84,3 @@ class User(db.Model):
|
|||
|
||||
|
||||
listen(User, 'before_insert', hash_password)
|
||||
|
||||
|
||||
|
|
|
@ -145,5 +145,3 @@ def render(args):
|
|||
query = database.sort(query, User, sort_by, sort_dir)
|
||||
|
||||
return database.paginate(query, page, count)
|
||||
|
||||
|
||||
|
|
13
package.json
13
package.json
|
@ -28,7 +28,6 @@
|
|||
"gulp-imagemin": "^0.6.2",
|
||||
"gulp-inject": "~1.0.1",
|
||||
"gulp-jshint": "^1.10.0",
|
||||
"gulp-karma": "^0.0.4",
|
||||
"gulp-load-plugins": "^0.5.3",
|
||||
"gulp-minify-html": "~0.1.4",
|
||||
"gulp-ng-annotate": "~0.5.2",
|
||||
|
@ -51,7 +50,9 @@
|
|||
"main-bower-files": "^1.0.2",
|
||||
"require-dir": "~0.3.0",
|
||||
"streamqueue": "^0.1.1",
|
||||
"uglify-save-license": "^0.4.1"
|
||||
"uglify-save-license": "^0.4.1",
|
||||
"karma": "~0.13.2",
|
||||
"bower": "~1.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
@ -59,9 +60,13 @@
|
|||
"scripts": {
|
||||
"postinstall": "bower install --allow-root",
|
||||
"pretest": "npm install && npm run build_static",
|
||||
"build_static": "gulp dist",
|
||||
"build_static": "gulp build",
|
||||
"prelint": "npm install",
|
||||
"lint": "jshint app/",
|
||||
"lint": "jshint lemur/static/app/",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jshint": "^2.8.0",
|
||||
"karma-chrome-launcher": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
[pytest]
|
||||
python_files=test*.py
|
||||
addopts=--tb=native -p no:doctest
|
||||
norecursedirs=bin dist docs htmlcov script hooks node_modules .* {args}
|
||||
|
||||
[flake8]
|
||||
ignore = F999,E501,E128,E124,E402,W503,E731,F841
|
||||
max-line-length = 100
|
||||
exclude = .tox,.git,*/migrations/*,lemur/static/*,docs/*
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
42
setup.py
42
setup.py
|
@ -14,13 +14,14 @@ import os.path
|
|||
from distutils import log
|
||||
from distutils.core import Command
|
||||
from setuptools.command.develop import develop
|
||||
from setuptools.command.install import install
|
||||
from setuptools.command.sdist import sdist
|
||||
from setuptools import setup
|
||||
from subprocess import check_output
|
||||
|
||||
ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__)))
|
||||
|
||||
install_requires=[
|
||||
install_requires = [
|
||||
'Flask>=0.10.1',
|
||||
'Flask-RESTful>=0.3.3',
|
||||
'Flask-SQLAlchemy>=1.0.5',
|
||||
|
@ -37,7 +38,7 @@ install_requires=[
|
|||
'six>=1.9.0',
|
||||
'gunicorn>=19.3.0',
|
||||
'pycrypto>=2.6.1',
|
||||
'cryptography>=0.9',
|
||||
'cryptography>=1.0dev',
|
||||
'pyopenssl>=0.15.1',
|
||||
'pyjwt>=1.0.1',
|
||||
'xmltodict>=0.9.2'
|
||||
|
@ -45,10 +46,10 @@ install_requires=[
|
|||
|
||||
tests_require = [
|
||||
'pyflakes',
|
||||
'moto',
|
||||
'nose',
|
||||
'pytest',
|
||||
'pytest-flask'
|
||||
'moto==0.4.6',
|
||||
'nose==1.3.7',
|
||||
'pytest==2.7.2',
|
||||
'pytest-flask==0.8.1'
|
||||
]
|
||||
|
||||
docs_require = [
|
||||
|
@ -56,6 +57,26 @@ docs_require = [
|
|||
'sphinxcontrib-httpdomain'
|
||||
]
|
||||
|
||||
dev_requires = [
|
||||
'flake8>=2.0,<2.1',
|
||||
]
|
||||
|
||||
|
||||
class SmartInstall(install):
|
||||
"""
|
||||
Installs Lemur into the Python environment.
|
||||
If the package indicator is missing, this will also force a run of
|
||||
`build_static` which is required for JavaScript assets and other things.
|
||||
"""
|
||||
def _needs_static(self):
|
||||
return not os.path.exists(os.path.join(ROOT, 'lemur-package.json'))
|
||||
|
||||
def run(self):
|
||||
if self._needs_static():
|
||||
self.run_command('build_static')
|
||||
install.run(self)
|
||||
|
||||
|
||||
class DevelopWithBuildStatic(develop):
|
||||
def install_for_development(self):
|
||||
self.run_command('build_static')
|
||||
|
@ -79,7 +100,7 @@ class BuildStatic(Command):
|
|||
log.info("running [npm install --quiet]")
|
||||
check_output(['npm', 'install', '--quiet'], cwd=ROOT)
|
||||
|
||||
log.info("running [gulp buld]")
|
||||
log.info("running [gulp build]")
|
||||
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'build'], cwd=ROOT)
|
||||
log.info("running [gulp package]")
|
||||
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'package'], cwd=ROOT)
|
||||
|
@ -96,12 +117,15 @@ setup(
|
|||
install_requires=install_requires,
|
||||
extras_require={
|
||||
'tests': tests_require,
|
||||
'docs': docs_require
|
||||
'docs': docs_require,
|
||||
'dev': dev_requires,
|
||||
},
|
||||
cmdclass={
|
||||
'build_static': BuildStatic,
|
||||
'develop': DevelopWithBuildStatic,
|
||||
'sdist': SdistWithBuildStatic
|
||||
'sdist': SdistWithBuildStatic,
|
||||
'install': SmartInstall
|
||||
|
||||
},
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
|
Loading…
Reference in New Issue