commit
c04e23d3ea
|
@ -26,3 +26,5 @@ pip-log.txt
|
||||||
docs/_build
|
docs/_build
|
||||||
.editorconfig
|
.editorconfig
|
||||||
.idea
|
.idea
|
||||||
|
test.conf
|
||||||
|
lemur/tests/tmp
|
|
@ -1,4 +1,2 @@
|
||||||
tests/
|
lemur/static//dist/
|
||||||
lemur/static/lemur/scripts/lib/
|
lemur/static/app/vendor/
|
||||||
lemur/static/lemur/dist/
|
|
||||||
lemur/static/lemur/vendor/
|
|
|
@ -3,13 +3,13 @@
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"esnext": true,
|
"esnext": true,
|
||||||
"bitwise": true,
|
"bitwise": true,
|
||||||
"camelcase": true,
|
"camelcase": false,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eqeqeq": true,
|
"eqeqeq": true,
|
||||||
"immed": true,
|
"immed": true,
|
||||||
"indent": 2,
|
"indent": 2,
|
||||||
"latedef": true,
|
"latedef": true,
|
||||||
"newcap": true,
|
"newcap": false,
|
||||||
"noarg": true,
|
"noarg": true,
|
||||||
"quotmark": "single",
|
"quotmark": "single",
|
||||||
"regexp": true,
|
"regexp": true,
|
||||||
|
@ -19,6 +19,9 @@
|
||||||
"trailing": true,
|
"trailing": true,
|
||||||
"smarttabs": true,
|
"smarttabs": true,
|
||||||
"globals": {
|
"globals": {
|
||||||
"angular": false
|
"angular": false,
|
||||||
|
"moment": false,
|
||||||
|
"toaster": false,
|
||||||
|
"_": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
.travis.yml
31
.travis.yml
|
@ -1,7 +1,26 @@
|
||||||
language: node_js
|
sudo: false
|
||||||
node_js:
|
language: python
|
||||||
- '0.8'
|
addons:
|
||||||
- '0.10'
|
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:
|
before_script:
|
||||||
- 'npm install -g bower grunt-cli'
|
- psql -c "create database lemur;" -U postgres
|
||||||
- 'bower install'
|
- 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'),
|
imagemin = require('gulp-imagemin'),
|
||||||
minifyHtml = require('gulp-minify-html'),
|
minifyHtml = require('gulp-minify-html'),
|
||||||
bowerFiles = require('main-bower-files'),
|
bowerFiles = require('main-bower-files'),
|
||||||
|
karma = require('karma'),
|
||||||
replace = require('gulp-replace');
|
replace = require('gulp-replace');
|
||||||
|
|
||||||
|
|
||||||
gulp.task('default', ['clean'], function () {
|
gulp.task('default', ['clean'], function () {
|
||||||
gulp.start('fonts', 'styles');
|
gulp.start('fonts', 'styles');
|
||||||
});
|
});
|
||||||
|
@ -37,6 +37,15 @@ gulp.task('clean', function (cb) {
|
||||||
del(['.tmp', 'lemur/static/dist'], 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 () {
|
gulp.task('dev:fonts', function () {
|
||||||
var fileList = [
|
var fileList = [
|
||||||
'lemur/static/app/vendor/bower_components/bootstrap/dist/fonts/*',
|
'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(c.green + '-------------------------------------------' + c.reset);
|
||||||
console.log(Object.keys(gulp.tasks).sort().join('\n'));
|
console.log(Object.keys(gulp.tasks).sort().join('\n'));
|
||||||
console.log('');
|
console.log('');
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,14 +29,6 @@ def py_lint(files_modified):
|
||||||
return report.total_errors != 0
|
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():
|
def main():
|
||||||
from flake8.hooks import run
|
from flake8.hooks import run
|
||||||
|
|
||||||
|
@ -46,7 +38,7 @@ def main():
|
||||||
|
|
||||||
files_modified = filter(lambda x: os.path.exists(x), files_modified)
|
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 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ LEMUR_BLUEPRINTS = (
|
||||||
plugins_bp,
|
plugins_bp,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_app(config=None):
|
def create_app(config=None):
|
||||||
app = factory.create_app(app_name=__name__, blueprints=LEMUR_BLUEPRINTS, config=config)
|
app = factory.create_app(app_name=__name__, blueprints=LEMUR_BLUEPRINTS, config=config)
|
||||||
configure_hook(app)
|
configure_hook(app)
|
||||||
|
@ -61,4 +62,3 @@ def configure_hook(app):
|
||||||
response = {'message': 'You are not allow to access this resource'}
|
response = {'message': 'You are not allow to access this resource'}
|
||||||
response.status_code = 403
|
response.status_code = 403
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
#def analyze(endpoints, truststores):
|
# def analyze(endpoints, truststores):
|
||||||
# results = {"headings": ["Endpoint"],
|
# results = {"headings": ["Endpoint"],
|
||||||
# "results": [],
|
# "results": [],
|
||||||
# "time": datetime.now().strftime("#Y%m%d %H:%M:%S")}
|
# "time": datetime.now().strftime("#Y%m%d %H:%M:%S")}
|
||||||
|
@ -37,7 +37,9 @@
|
||||||
# log.debug(e)
|
# log.debug(e)
|
||||||
# if 'hostname' in str(e):
|
# if 'hostname' in str(e):
|
||||||
# tests.append('pass')
|
# 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):
|
# elif 'certificate verify failed' in str(e):
|
||||||
# tests.append('fail')
|
# tests.append('fail')
|
||||||
# result['details'].append("{}: This test failed to verify the SSL certificate".format(region))
|
# 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.users import service as user_service
|
||||||
from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed, \
|
from lemur.auth.permissions import CertificateOwnerNeed, CertificateCreatorNeed, \
|
||||||
AuthorityCreatorNeed, AuthorityOwnerNeed, ViewRoleCredentialsNeed
|
AuthorityCreatorNeed, ViewRoleCredentialsNeed
|
||||||
|
|
||||||
|
|
||||||
def base64url_decode(data):
|
def base64url_decode(data):
|
||||||
|
@ -143,7 +143,6 @@ def fetch_token_header(token):
|
||||||
raise jwt.DecodeError('Invalid header padding')
|
raise jwt.DecodeError('Invalid header padding')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@identity_loaded.connect
|
@identity_loaded.connect
|
||||||
def on_identity_loaded(sender, identity):
|
def on_identity_loaded(sender, identity):
|
||||||
"""
|
"""
|
||||||
|
@ -187,5 +186,3 @@ class AuthenticatedResource(Resource):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(AuthenticatedResource, self).__init__()
|
super(AuthenticatedResource, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,16 @@ import jwt
|
||||||
import base64
|
import base64
|
||||||
import requests
|
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.restful import reqparse, Resource, Api
|
||||||
from flask.ext.principal import Identity, identity_changed
|
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.users import service as user_service
|
||||||
from lemur.roles import service as role_service
|
from lemur.roles import service as role_service
|
||||||
from lemur.certificates import service as cert_service
|
from lemur.auth.service import create_token, fetch_token_header, get_rsa_public_key
|
||||||
from lemur.auth.service import AuthenticatedResource, create_token, fetch_token_header, get_rsa_public_key
|
|
||||||
|
|
||||||
|
|
||||||
mod = Blueprint('auth', __name__)
|
mod = Blueprint('auth', __name__)
|
||||||
|
@ -203,7 +203,7 @@ class Ping(Resource):
|
||||||
|
|
||||||
user = user_service.create(
|
user = user_service.create(
|
||||||
profile['email'],
|
profile['email'],
|
||||||
cert_service.create_challenge(),
|
get_psuedo_random_string(),
|
||||||
profile['email'],
|
profile['email'],
|
||||||
True,
|
True,
|
||||||
profile.get('thumbnailPhotoUrl'),
|
profile.get('thumbnailPhotoUrl'),
|
||||||
|
@ -222,7 +222,7 @@ class Ping(Resource):
|
||||||
profile['email'],
|
profile['email'],
|
||||||
profile['email'],
|
profile['email'],
|
||||||
True,
|
True,
|
||||||
profile.get('thumbnailPhotoUrl'), # incase profile isn't google+ enabled
|
profile.get('thumbnailPhotoUrl'), # Encase profile isn't google+ enabled
|
||||||
roles
|
roles
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -234,5 +234,3 @@ class Ping(Resource):
|
||||||
|
|
||||||
api.add_resource(Login, '/auth/login', endpoint='login')
|
api.add_resource(Login, '/auth/login', endpoint='login')
|
||||||
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import lemur.certificates.service as cert_service
|
||||||
|
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
|
||||||
def update(authority_id, active=None, roles=None):
|
def update(authority_id, active=None, roles=None):
|
||||||
"""
|
"""
|
||||||
Update a an authority with new values.
|
Update a an authority with new values.
|
||||||
|
@ -30,7 +31,7 @@ def update(authority_id, active=None, roles=None):
|
||||||
"""
|
"""
|
||||||
authority = get(authority_id)
|
authority = get(authority_id)
|
||||||
if roles:
|
if roles:
|
||||||
authority = database.update_list(authority, 'roles', Role, roles)
|
authority = database.update_list(authority, 'roles', Role, roles)
|
||||||
|
|
||||||
if active:
|
if active:
|
||||||
authority.active = active
|
authority.active = active
|
||||||
|
@ -62,9 +63,9 @@ def create(kwargs):
|
||||||
for r in issuer_roles:
|
for r in issuer_roles:
|
||||||
role = role_service.create(
|
role = role_service.create(
|
||||||
r['name'],
|
r['name'],
|
||||||
password=r['password'],
|
password=r['password'],
|
||||||
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
description="{0} auto generated role".format(kwargs.get('pluginName')),
|
||||||
username=r['username'])
|
username=r['username'])
|
||||||
|
|
||||||
# the user creating the authority should be able to administer it
|
# the user creating the authority should be able to administer it
|
||||||
if role.username == 'admin':
|
if role.username == 'admin':
|
||||||
|
@ -132,7 +133,7 @@ def get_authority_role(ca_name):
|
||||||
"""
|
"""
|
||||||
if g.current_user.is_admin:
|
if g.current_user.is_admin:
|
||||||
authority = get_by_name(ca_name)
|
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]
|
return authority.roles[0]
|
||||||
else:
|
else:
|
||||||
for role in g.current_user.roles:
|
for role in g.current_user.roles:
|
||||||
|
@ -156,7 +157,7 @@ def render(args):
|
||||||
|
|
||||||
if filt:
|
if filt:
|
||||||
terms = filt.split(';')
|
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])
|
query = query.filter(Authority.active == terms[1])
|
||||||
else:
|
else:
|
||||||
query = database.filter(query, Authority, terms)
|
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('caDescription', type=str, location='json', required=False)
|
||||||
self.reqparse.add_argument('ownerEmail', type=str, location='json', required=True)
|
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('caDN', type=dict, location='json', required=False)
|
||||||
self.reqparse.add_argument('validityStart', 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('validityEnd', type=str, location='json', required=False) # TODO validate
|
||||||
self.reqparse.add_argument('extensions', type=dict, location='json', required=False)
|
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('pluginName', type=str, location='json', required=True)
|
||||||
self.reqparse.add_argument('caType', type=str, location='json', required=False)
|
self.reqparse.add_argument('caType', type=str, location='json', required=False)
|
||||||
|
|
|
@ -53,6 +53,7 @@ class UnableToCreateCSR(LemurException):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.data['message'])
|
return repr(self.data['message'])
|
||||||
|
|
||||||
|
|
||||||
class UnableToCreatePrivateKey(LemurException):
|
class UnableToCreatePrivateKey(LemurException):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.code = 500
|
self.code = 500
|
||||||
|
@ -63,6 +64,7 @@ class UnableToCreatePrivateKey(LemurException):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.data['message'])
|
return repr(self.data['message'])
|
||||||
|
|
||||||
|
|
||||||
class MissingFiles(LemurException):
|
class MissingFiles(LemurException):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.code = 500
|
self.code = 500
|
||||||
|
@ -84,4 +86,3 @@ class NoPersistanceFound(LemurException):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.data['message'])
|
return repr(self.data['message'])
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ from lemur.database import db
|
||||||
|
|
||||||
from lemur.domains.models import Domain
|
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
|
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 '-'
|
# aws doesn't allow special chars except '-'
|
||||||
disallowed_chars = ''.join(c for c in map(chr, range(256)) if not c.isalnum())
|
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("-", "")
|
||||||
|
disallowed_chars = disallowed_chars.replace(".", "")
|
||||||
temp = temp.replace('*', "WILDCARD")
|
temp = temp.replace('*', "WILDCARD")
|
||||||
temp = temp.translate(None, disallowed_chars)
|
temp = temp.translate(None, disallowed_chars)
|
||||||
# white space is silly too
|
# white space is silly too
|
||||||
|
@ -76,7 +77,7 @@ def cert_get_domains(cert):
|
||||||
return the common name.
|
return the common name.
|
||||||
|
|
||||||
:param cert:
|
:param cert:
|
||||||
:return: List of domainss
|
:return: List of domains
|
||||||
"""
|
"""
|
||||||
domains = []
|
domains = []
|
||||||
try:
|
try:
|
||||||
|
@ -86,6 +87,7 @@ def cert_get_domains(cert):
|
||||||
domains.append(entry)
|
domains.append(entry)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
|
current_app.logger.warning("Failed to get SubjectAltName: {0}".format(e))
|
||||||
|
|
||||||
return domains
|
return domains
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ def cert_is_san(cert):
|
||||||
if len(cert_get_domains(cert)) > 1:
|
if len(cert_get_domains(cert)) > 1:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def cert_is_wildcard(cert):
|
def cert_is_wildcard(cert):
|
||||||
"""
|
"""
|
||||||
Determines if certificate is a wildcard certificate.
|
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] == "*":
|
if len(domains) == 1 and domains[0][0:1] == "*":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if cert.subject.get_attributes_for_oid(x509.OID_COMMON_NAME)[0].value[0:1] == "*":
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def cert_get_bitstrength(cert):
|
def cert_get_bitstrength(cert):
|
||||||
"""
|
"""
|
||||||
|
@ -197,8 +203,8 @@ class Certificate(db.Model):
|
||||||
owner = Column(String(128))
|
owner = Column(String(128))
|
||||||
body = Column(Text())
|
body = Column(Text())
|
||||||
private_key = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
private_key = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY')))
|
||||||
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY'))) # TODO deprecate
|
challenge = Column(EncryptedType(String, os.environ.get('LEMUR_ENCRYPTION_KEY'))) # TODO deprecate
|
||||||
csr_config = Column(Text()) # TODO deprecate
|
csr_config = Column(Text()) # TODO deprecate
|
||||||
status = Column(String(128))
|
status = Column(String(128))
|
||||||
deleted = Column(Boolean, index=True)
|
deleted = Column(Boolean, index=True)
|
||||||
name = Column(String(128))
|
name = Column(String(128))
|
||||||
|
@ -266,4 +272,3 @@ class Certificate(db.Model):
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
import arrow
|
import arrow
|
||||||
import string
|
|
||||||
import random
|
|
||||||
|
|
||||||
from sqlalchemy import func, or_
|
from sqlalchemy import func, or_
|
||||||
from flask import g, current_app
|
from flask import g, current_app
|
||||||
|
@ -27,7 +25,6 @@ from cryptography.hazmat.primitives import hashes, serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get(cert_id):
|
def get(cert_id):
|
||||||
"""
|
"""
|
||||||
Retrieves certificate by it's ID.
|
Retrieves certificate by it's ID.
|
||||||
|
@ -106,7 +103,6 @@ def mint(issuer_options):
|
||||||
|
|
||||||
csr, private_key = create_csr(issuer_options)
|
csr, private_key = create_csr(issuer_options)
|
||||||
|
|
||||||
issuer_options['challenge'] = create_challenge()
|
|
||||||
issuer_options['creator'] = g.user.email
|
issuer_options['creator'] = g.user.email
|
||||||
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
||||||
|
|
||||||
|
@ -212,8 +208,8 @@ def render(args):
|
||||||
time_range = args.pop('time_range')
|
time_range = args.pop('time_range')
|
||||||
destination_id = args.pop('destination_id')
|
destination_id = args.pop('destination_id')
|
||||||
show = args.pop('show')
|
show = args.pop('show')
|
||||||
owner = args.pop('owner')
|
# owner = args.pop('owner')
|
||||||
creator = args.pop('creator') # TODO we should enabling filtering by owner
|
# creator = args.pop('creator') # TODO we should enabling filtering by owner
|
||||||
|
|
||||||
filt = args.pop('filter')
|
filt = args.pop('filter')
|
||||||
|
|
||||||
|
@ -235,7 +231,7 @@ def render(args):
|
||||||
|
|
||||||
if 'destination' in terms:
|
if 'destination' in terms:
|
||||||
query = query.filter(Certificate.destinations.any(Destination.id == terms[1]))
|
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])
|
query = query.filter(Certificate.active == terms[1])
|
||||||
else:
|
else:
|
||||||
query = database.filter(query, Certificate, terms)
|
query = database.filter(query, Certificate, terms)
|
||||||
|
@ -288,7 +284,7 @@ def create_csr(csr_config):
|
||||||
x509.BasicConstraints(ca=False, path_length=None), critical=True,
|
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':
|
# if k == 'subAltNames':
|
||||||
# builder = builder.add_extension(
|
# builder = builder.add_extension(
|
||||||
# x509.SubjectAlternativeName([x509.DNSName(n) for n in v]), critical=True,
|
# x509.SubjectAlternativeName([x509.DNSName(n) for n in v]), critical=True,
|
||||||
|
@ -354,16 +350,6 @@ def create_csr(csr_config):
|
||||||
|
|
||||||
return csr, pem
|
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):
|
def stats(**kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -405,5 +391,3 @@ def stats(**kwargs):
|
||||||
values.append(count)
|
values.append(count)
|
||||||
|
|
||||||
return {'labels': keys, 'values': values}
|
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.base import plugins
|
||||||
from lemur.plugins.bases.source import SourcePlugin
|
from lemur.plugins.bases.source import SourcePlugin
|
||||||
|
|
||||||
|
|
||||||
def sync():
|
def sync():
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
new = 0
|
new = 0
|
||||||
|
@ -42,5 +43,4 @@ def sync():
|
||||||
|
|
||||||
# TODO associated cert with source
|
# TODO associated cert with source
|
||||||
# TODO update cert if found from different 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()
|
url, err = p1.communicate()
|
||||||
|
|
||||||
p2 = subprocess.Popen(['openssl', 'ocsp', '-issuer', issuer_chain_path,
|
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()
|
message, err = p2.communicate()
|
||||||
if 'error' in message or 'Error' in message:
|
if 'error' in message or 'Error' in message:
|
||||||
|
|
|
@ -51,7 +51,7 @@ def valid_authority(authority_options):
|
||||||
"""
|
"""
|
||||||
Defends against invalid authorities
|
Defends against invalid authorities
|
||||||
|
|
||||||
:param authority_name:
|
:param authority_options:
|
||||||
:return: :raise ValueError:
|
:return: :raise ValueError:
|
||||||
"""
|
"""
|
||||||
name = authority_options['name']
|
name = authority_options['name']
|
||||||
|
@ -76,7 +76,7 @@ def pem_str(value, name):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
x509.load_pem_x509_certificate(str(value), default_backend())
|
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))
|
raise ValueError("The parameter '{0}' needs to be a valid PEM string".format(name))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -91,12 +91,11 @@ def private_key_str(value, name):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
serialization.load_pem_private_key(str(value), None, backend=default_backend())
|
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))
|
raise ValueError("The parameter '{0}' needs to be a valid RSA private key".format(name))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CertificatesList(AuthenticatedResource):
|
class CertificatesList(AuthenticatedResource):
|
||||||
""" Defines the 'certificates' endpoint """
|
""" Defines the 'certificates' endpoint """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -274,8 +273,8 @@ class CertificatesList(AuthenticatedResource):
|
||||||
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('elbs', type=list, location='json')
|
self.reqparse.add_argument('elbs', type=list, location='json')
|
||||||
self.reqparse.add_argument('owner', type=str, 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('validityStart', type=str, location='json') # TODO validate
|
||||||
self.reqparse.add_argument('validityEnd', type=str, location='json') # parse date
|
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('authority', type=valid_authority, location='json')
|
||||||
self.reqparse.add_argument('description', type=str, location='json')
|
self.reqparse.add_argument('description', type=str, location='json')
|
||||||
self.reqparse.add_argument('country', 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 = Blueprint('healthCheck', __name__)
|
||||||
|
|
||||||
|
|
||||||
@mod.route('/healthcheck')
|
@mod.route('/healthcheck')
|
||||||
def health():
|
def health():
|
||||||
return 'ok'
|
return 'ok'
|
|
@ -8,6 +8,7 @@
|
||||||
"""
|
"""
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
|
|
||||||
# inspired by https://github.com/getsentry/sentry
|
# inspired by https://github.com/getsentry/sentry
|
||||||
class InstanceManager(object):
|
class InstanceManager(object):
|
||||||
def __init__(self, class_list=None, instances=True):
|
def __init__(self, class_list=None, instances=True):
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import current_app
|
|
||||||
import boto.ses
|
import boto.ses
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.templates.config import env
|
from lemur.templates.config import env
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ def send(subject, data, email_type, recipients):
|
||||||
:param recipients:
|
:param recipients:
|
||||||
"""
|
"""
|
||||||
conn = boto.connect_ses()
|
conn = boto.connect_ses()
|
||||||
#jinja template depending on type
|
# jinja template depending on type
|
||||||
template = env.get_template('{}.html'.format(email_type))
|
template = env.get_template('{}.html'.format(email_type))
|
||||||
body = template.render(**data)
|
body = template.render(**data)
|
||||||
conn.send_email(current_app.config.get("LEMUR_EMAIL"), subject, body, recipients, format='html')
|
conn.send_email(current_app.config.get("LEMUR_EMAIL"), subject, body, recipients, format='html')
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,28 @@
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
|
import string
|
||||||
|
import random
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from flask.ext.restful import marshal
|
from flask.ext.restful import marshal
|
||||||
from flask.ext.restful.reqparse import RequestParser
|
from flask.ext.restful.reqparse import RequestParser
|
||||||
|
|
||||||
from flask.ext.sqlalchemy import Pagination
|
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):
|
class marshal_items(object):
|
||||||
def __init__(self, fields, envelope=None):
|
def __init__(self, fields, envelope=None):
|
||||||
self.fields = fields
|
self.fields = fields
|
||||||
|
|
|
@ -6,5 +6,3 @@
|
||||||
SAN_NAMING_TEMPLATE = "SAN-{subject}-{issuer}-{not_before}-{not_after}"
|
SAN_NAMING_TEMPLATE = "SAN-{subject}-{issuer}-{not_before}-{not_after}"
|
||||||
DEFAULT_NAMING_TEMPLATE = "{subject}-{issuer}-{not_before}-{not_after}"
|
DEFAULT_NAMING_TEMPLATE = "{subject}-{issuer}-{not_before}-{not_after}"
|
||||||
NONSTANDARD_NAMING_TEMPLATE = "{issuer}-{not_before}-{not_after}"
|
NONSTANDARD_NAMING_TEMPLATE = "{issuer}-{not_before}-{not_after}"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,11 @@
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from sqlalchemy.sql import and_, or_
|
from sqlalchemy.sql import and_, or_
|
||||||
|
|
||||||
from lemur.extensions import db
|
from lemur.extensions import db
|
||||||
from lemur.exceptions import AttrNotFound, IntegrityError, DuplicateError
|
from lemur.exceptions import AttrNotFound, DuplicateError
|
||||||
|
|
||||||
|
|
||||||
def filter_none(kwargs):
|
def filter_none(kwargs):
|
||||||
|
@ -126,7 +124,7 @@ def get(model, value, field="id"):
|
||||||
query = session_query(model)
|
query = session_query(model)
|
||||||
try:
|
try:
|
||||||
return query.filter(getattr(model, field) == value).one()
|
return query.filter(getattr(model, field) == value).one()
|
||||||
except:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,8 +176,9 @@ def delete(model):
|
||||||
|
|
||||||
:param model:
|
:param model:
|
||||||
"""
|
"""
|
||||||
db.session.delete(model)
|
if model:
|
||||||
db.session.commit()
|
db.session.delete(model)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
def filter(query, model, terms):
|
def filter(query, model, terms):
|
||||||
|
@ -209,7 +208,7 @@ def sort(query, model, field, direction):
|
||||||
direction = getattr(field, direction)
|
direction = getattr(field, direction)
|
||||||
query = query.order_by(direction())
|
query = query.order_by(direction())
|
||||||
return query
|
return query
|
||||||
except AttributeError as e:
|
except AttributeError:
|
||||||
raise AttrNotFound(field)
|
raise AttrNotFound(field)
|
||||||
|
|
||||||
|
|
||||||
|
@ -274,6 +273,3 @@ def sort_and_page(query, model, args):
|
||||||
query = sort(query, model, sort_by, sort_dir)
|
query = sort(query, model, sort_by, sort_dir)
|
||||||
|
|
||||||
return paginate(query, page, count)
|
return paginate(query, page, count)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,10 @@ from flask import make_response, request, current_app
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# this is only used for dev
|
||||||
def crossdomain(origin=None, methods=None, headers=None,
|
def crossdomain(origin=None, methods=None, headers=None,
|
||||||
max_age=21600, attach_to_all=True,
|
max_age=21600, attach_to_all=True,
|
||||||
automatic_options=True):
|
automatic_options=True): # pragma: no cover
|
||||||
if methods is not None:
|
if methods is not None:
|
||||||
methods = ', '.join(sorted(x.upper() for x in methods))
|
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-Origin'] = origin
|
||||||
h['Access-Control-Allow-Methods'] = get_methods()
|
h['Access-Control-Allow-Methods'] = get_methods()
|
||||||
h['Access-Control-Max-Age'] = str(max_age)
|
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 "
|
||||||
h['Access-Control-Allow-Headers'] = "Origin, X-Requested-With, Content-Type, Accept, Authorization " # headers
|
|
||||||
h['Access-Control-Allow-Credentials'] = 'true'
|
h['Access-Control-Allow-Credentials'] = 'true'
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
f.provide_automatic_options = False
|
f.provide_automatic_options = False
|
||||||
return update_wrapper(wrapped_function, f)
|
return update_wrapper(wrapped_function, f)
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,13 @@ ADMINS = frozenset([''])
|
||||||
|
|
||||||
THREADS_PER_PAGE = 8
|
THREADS_PER_PAGE = 8
|
||||||
|
|
||||||
#############
|
# General
|
||||||
## General ##
|
|
||||||
#############
|
|
||||||
|
|
||||||
# These will need to be set to `True` if you are developing locally
|
# These will need to be set to `True` if you are developing locally
|
||||||
CORS = False
|
CORS = False
|
||||||
debug = False
|
debug = False
|
||||||
|
|
||||||
#############
|
# Logging
|
||||||
## Logging ##
|
|
||||||
#############
|
|
||||||
|
|
||||||
LOG_LEVEL = "DEBUG"
|
LOG_LEVEL = "DEBUG"
|
||||||
LOG_FILE = "lemur.log"
|
LOG_FILE = "lemur.log"
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from lemur.database import db
|
||||||
|
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
|
||||||
class Destination(db.Model):
|
class Destination(db.Model):
|
||||||
__tablename__ = 'destinations'
|
__tablename__ = 'destinations'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
|
|
|
@ -37,7 +37,7 @@ def update(destination_id, label, options, description):
|
||||||
destination = get(destination_id)
|
destination = get(destination_id)
|
||||||
|
|
||||||
destination.label = label
|
destination.label = label
|
||||||
description.options = options
|
destination.options = options
|
||||||
destination.description = description
|
destination.description = description
|
||||||
|
|
||||||
return database.update(destination)
|
return database.update(destination)
|
||||||
|
@ -107,4 +107,3 @@ def render(args):
|
||||||
query = database.sort(query, Destination, sort_by, sort_dir)
|
query = database.sort(query, Destination, sort_by, sort_dir)
|
||||||
|
|
||||||
return database.paginate(query, page, count)
|
return database.paginate(query, page, count)
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,6 @@ class Destinations(AuthenticatedResource):
|
||||||
return {'result': True}
|
return {'result': True}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CertificateDestinations(AuthenticatedResource):
|
class CertificateDestinations(AuthenticatedResource):
|
||||||
""" Defines the 'certificate/<int:certificate_id/destinations'' endpoint """
|
""" Defines the 'certificate/<int:certificate_id/destinations'' endpoint """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -274,5 +273,5 @@ class CertificateDestinations(AuthenticatedResource):
|
||||||
|
|
||||||
api.add_resource(DestinationsList, '/destinations', endpoint='destinations')
|
api.add_resource(DestinationsList, '/destinations', endpoint='destinations')
|
||||||
api.add_resource(Destinations, '/destinations/<int:destination_id>', endpoint='account')
|
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 = self.as_dict()
|
||||||
blob['certificates'] = [x.id for x in self.certificate]
|
blob['certificates'] = [x.id for x in self.certificate]
|
||||||
return blob
|
return blob
|
||||||
|
|
||||||
|
|
|
@ -61,4 +61,3 @@ def render(args):
|
||||||
query = database.sort(query, Domain, sort_by, sort_dir)
|
query = database.sort(query, Domain, sort_by, sort_dir)
|
||||||
|
|
||||||
return database.paginate(query, page, count)
|
return database.paginate(query, page, count)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. 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 sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from lemur.database import db
|
from lemur.database import db
|
||||||
|
@ -16,7 +16,7 @@ from lemur.listeners.models import Listener
|
||||||
class ELB(db.Model):
|
class ELB(db.Model):
|
||||||
__tablename__ = 'elbs'
|
__tablename__ = 'elbs'
|
||||||
id = Column(BigInteger, primary_key=True)
|
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))
|
region = Column(String(32))
|
||||||
name = Column(String(128))
|
name = Column(String(128))
|
||||||
vpc_id = Column(String(128))
|
vpc_id = Column(String(128))
|
||||||
|
|
|
@ -14,6 +14,7 @@ from lemur import database
|
||||||
from lemur.elbs.models import ELB
|
from lemur.elbs.models import ELB
|
||||||
from lemur.listeners.models import Listener
|
from lemur.listeners.models import Listener
|
||||||
|
|
||||||
|
|
||||||
def get_all(account_id, elb_name):
|
def get_all(account_id, elb_name):
|
||||||
"""
|
"""
|
||||||
Retrieves all ELBs in a given account
|
Retrieves all ELBs in a given account
|
||||||
|
@ -112,7 +113,7 @@ def stats(**kwargs):
|
||||||
|
|
||||||
if kwargs.get('active') == 'true':
|
if kwargs.get('active') == 'true':
|
||||||
query = query.join(ELB.listeners)
|
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()
|
items = query.group_by(attr).all()
|
||||||
|
|
||||||
|
@ -121,5 +122,3 @@ def stats(**kwargs):
|
||||||
if key:
|
if key:
|
||||||
results.append({"key": key, "y": count})
|
results.append({"key": key, "y": count})
|
||||||
return results
|
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
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
@ -15,4 +14,3 @@ bcrypt = Bcrypt()
|
||||||
|
|
||||||
from flask.ext.principal import Principal
|
from flask.ext.principal import Principal
|
||||||
principal = Principal()
|
principal = Principal()
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from lemur.common.health import mod as health
|
from lemur.common.health import mod as health
|
||||||
from lemur.exceptions import NoEncryptionKeyFound
|
|
||||||
from lemur.extensions import db, migrate, principal
|
from lemur.extensions import db, migrate, principal
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,7 +160,6 @@ def install_plugins(app):
|
||||||
try:
|
try:
|
||||||
plugin = ep.load()
|
plugin = ep.load()
|
||||||
except Exception:
|
except Exception:
|
||||||
import sys
|
|
||||||
import traceback
|
import traceback
|
||||||
app.logger.error("Failed to load plugin %r:\n%s\n" % (ep.name, traceback.format_exc()))
|
app.logger.error("Failed to load plugin %r:\n%s\n" % (ep.name, traceback.format_exc()))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -40,4 +40,3 @@ class Listener(db.Model):
|
||||||
blob = self.as_dict()
|
blob = self.as_dict()
|
||||||
del blob['date_created']
|
del blob['date_created']
|
||||||
return blob
|
return blob
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ from lemur.listeners.models import Listener
|
||||||
from lemur.elbs import service as elb_service
|
from lemur.elbs import service as elb_service
|
||||||
from lemur.certificates import service as certificate_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):
|
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)
|
cert = verify_attachment(certificate_id, account_number)
|
||||||
listener_tuple = (load_balancer_port, instance_port, load_balancer_protocol, cert.get_art(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'}
|
return {'message': 'Listener has been created'}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ def update(listener_id, **kwargs):
|
||||||
|
|
||||||
database.update(listener)
|
database.update(listener)
|
||||||
listener_tuple = (listener.load_balancer_port, listener.instance_port, listener.load_balancer_protocol, arn,)
|
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'}
|
return {'message': 'Listener has been updated'}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ def update(listener_id, **kwargs):
|
||||||
def delete(listener_id):
|
def delete(listener_id):
|
||||||
# first try to delete the listener in aws
|
# first try to delete the listener in aws
|
||||||
listener = get(listener_id)
|
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
|
# cleanup operation in lemur
|
||||||
database.delete(listener)
|
database.delete(listener)
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ def stats(**kwargs):
|
||||||
query = query.filter(ELB.account_id == kwargs.get('account_id'))
|
query = query.filter(ELB.account_id == kwargs.get('account_id'))
|
||||||
|
|
||||||
if kwargs.get('active') == 'true':
|
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()
|
items = query.group_by(attr).all()
|
||||||
results = []
|
results = []
|
||||||
|
@ -157,6 +157,3 @@ def stats(**kwargs):
|
||||||
if key:
|
if key:
|
||||||
results.append({"key": key, "y": count})
|
results.append({"key": key, "y": count})
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,19 +20,18 @@ from lemur.plugins.base import plugins
|
||||||
|
|
||||||
from lemur.certificates.verify import verify_string
|
from lemur.certificates.verify import verify_string
|
||||||
from lemur.certificates import sync
|
from lemur.certificates import sync
|
||||||
from lemur.elbs.sync import sync_all_elbs
|
|
||||||
|
|
||||||
from lemur import create_app
|
from lemur import create_app
|
||||||
|
|
||||||
# Needed to be imported so that SQLAlchemy create_all can find our models
|
# Needed to be imported so that SQLAlchemy create_all can find our models
|
||||||
from lemur.users.models import User
|
from lemur.users.models import User # noqa
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role # noqa
|
||||||
from lemur.authorities.models import Authority
|
from lemur.authorities.models import Authority # noqa
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate # noqa
|
||||||
from lemur.destinations.models import Destination
|
from lemur.destinations.models import Destination # noqa
|
||||||
from lemur.domains.models import Domain
|
from lemur.domains.models import Domain # noqa
|
||||||
from lemur.elbs.models import ELB
|
from lemur.elbs.models import ELB # noqa
|
||||||
from lemur.listeners.models import Listener
|
from lemur.listeners.models import Listener # noqa
|
||||||
|
|
||||||
manager = Manager(create_app)
|
manager = Manager(create_app)
|
||||||
manager.add_option('-c', '--config', dest='config')
|
manager.add_option('-c', '--config', dest='config')
|
||||||
|
@ -55,48 +54,42 @@ ADMINS = frozenset([''])
|
||||||
|
|
||||||
THREADS_PER_PAGE = 8
|
THREADS_PER_PAGE = 8
|
||||||
|
|
||||||
#############
|
# General
|
||||||
## General ##
|
|
||||||
#############
|
|
||||||
|
|
||||||
# These will need to be set to `True` if you are developing locally
|
# These will need to be set to `True` if you are developing locally
|
||||||
CORS = False
|
CORS = False
|
||||||
debug = 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
|
# You should consider storing these separately from your config
|
||||||
LEMUR_SECRET_TOKEN = '{secret_token}'
|
LEMUR_TOKEN_SECRET = '{secret_token}'
|
||||||
LEMUR_ENCRYPTION_KEY = '{encryption_key}'
|
LEMUR_ENCRYPTION_KEY = '{encryption_key}'
|
||||||
|
|
||||||
# this is a list of domains as regexes that only admins can issue
|
# this is a list of domains as regexes that only admins can issue
|
||||||
LEMUR_RESTRICTED_DOMAINS = []
|
LEMUR_RESTRICTED_DOMAINS = []
|
||||||
|
|
||||||
#################
|
# Mail Server
|
||||||
## Mail Server ##
|
|
||||||
#################
|
|
||||||
|
|
||||||
# Lemur currently only supports SES for sending email, this address
|
# Lemur currently only supports SES for sending email, this address
|
||||||
# needs to be verified
|
# needs to be verified
|
||||||
LEMUR_EMAIL = ''
|
LEMUR_EMAIL = ''
|
||||||
LEMUR_SECURITY_TEAM_EMAIL = []
|
LEMUR_SECURITY_TEAM_EMAIL = []
|
||||||
|
|
||||||
#############
|
# Logging
|
||||||
## Logging ##
|
|
||||||
#############
|
|
||||||
|
|
||||||
LOG_LEVEL = "DEBUG"
|
LOG_LEVEL = "DEBUG"
|
||||||
LOG_FILE = "lemur.log"
|
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
|
# Lemur will need STS assume role access to every destination you want to monitor
|
||||||
#AWS_ACCOUNT_MAPPINGS = {{
|
#AWS_ACCOUNT_MAPPINGS = {{
|
||||||
|
@ -129,6 +122,7 @@ SQLALCHEMY_DATABASE_URI = ''
|
||||||
#VERSIGN_EMAIL = ''
|
#VERSIGN_EMAIL = ''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@MigrateCommand.command
|
@MigrateCommand.command
|
||||||
def create():
|
def create():
|
||||||
database.db.create_all()
|
database.db.create_all()
|
||||||
|
@ -178,7 +172,8 @@ def generate_settings():
|
||||||
"""
|
"""
|
||||||
output = CONFIG_TEMPLATE.format(
|
output = CONFIG_TEMPLATE.format(
|
||||||
encryption_key=base64.b64encode(os.urandom(KEY_LENGTH)),
|
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
|
return output
|
||||||
|
@ -207,7 +202,7 @@ class Sync(Command):
|
||||||
sys.stdout.write("[!] Starting to sync with AWS!\n")
|
sys.stdout.write("[!] Starting to sync with AWS!\n")
|
||||||
try:
|
try:
|
||||||
sync.aws()
|
sync.aws()
|
||||||
#sync_all_elbs()
|
# sync_all_elbs()
|
||||||
sys.stdout.write("[+] Finished syncing with AWS!\n")
|
sys.stdout.write("[+] Finished syncing with AWS!\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stdout.write("[-] Syncing with AWS failed!\n")
|
sys.stdout.write("[-] Syncing with AWS failed!\n")
|
||||||
|
@ -480,8 +475,8 @@ def main():
|
||||||
manager.add_command("show_urls", ShowUrls())
|
manager.add_command("show_urls", ShowUrls())
|
||||||
manager.add_command("db", MigrateCommand)
|
manager.add_command("db", MigrateCommand)
|
||||||
manager.add_command("init", InitializeApp())
|
manager.add_command("init", InitializeApp())
|
||||||
manager.add_command('create_user', CreateUser())
|
manager.add_command("create_user", CreateUser())
|
||||||
manager.add_command('create_role', CreateRole())
|
manager.add_command("create_role", CreateRole())
|
||||||
manager.add_command("sync", Sync())
|
manager.add_command("sync", Sync())
|
||||||
manager.run()
|
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
|
from lemur.database import db
|
||||||
|
|
||||||
certificate_associations = db.Table('certificate_associations',
|
certificate_associations = db.Table('certificate_associations',
|
||||||
Column('domain_id', Integer, ForeignKey('domains.id')),
|
Column('domain_id', Integer, ForeignKey('domains.id')),
|
||||||
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
||||||
)
|
)
|
||||||
|
|
||||||
certificate_destination_associations = db.Table('certificate_destination_associations',
|
certificate_destination_associations = db.Table('certificate_destination_associations',
|
||||||
Column('destination_id', Integer, ForeignKey('destinations.id', ondelete='cascade')),
|
Column('destination_id', Integer,
|
||||||
Column('certificate_id', Integer, ForeignKey('certificates.id', ondelete='cascade'))
|
ForeignKey('destinations.id', ondelete='cascade')),
|
||||||
)
|
Column('certificate_id', Integer,
|
||||||
|
ForeignKey('certificates.id', ondelete='cascade'))
|
||||||
|
)
|
||||||
|
|
||||||
roles_users = db.Table('roles_users',
|
roles_users = db.Table('roles_users',
|
||||||
Column('user_id', Integer, ForeignKey('users.id')),
|
Column('user_id', Integer, ForeignKey('users.id')),
|
||||||
Column('role_id', Integer, ForeignKey('roles.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))
|
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.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'))
|
query = query.filter(Certificate.not_after >= arrow.utcnow().format('YYYY-MM-DD'))
|
||||||
ss_list.extend(query.all())
|
ss_list.extend(query.all())
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from lemur.plugins.base import * # NOQA
|
from lemur.plugins.base import * # noqa
|
||||||
from lemur.plugins.bases import * # NOQA
|
from lemur.plugins.bases import * # noqa
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
from __future__ import absolute_import, print_function
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
from lemur.plugins.base.manager import PluginManager
|
from lemur.plugins.base.manager import PluginManager
|
||||||
from lemur.plugins.base.v1 import * # NOQA
|
from lemur.plugins.base.v1 import * # noqa
|
||||||
|
|
||||||
plugins = PluginManager()
|
plugins = PluginManager()
|
||||||
register = plugins.register
|
register = plugins.register
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from lemur.common.managers import InstanceManager
|
from lemur.common.managers import InstanceManager
|
||||||
|
|
||||||
|
|
||||||
# inspired by https://github.com/getsentry/sentry
|
# inspired by https://github.com/getsentry/sentry
|
||||||
class PluginManager(InstanceManager):
|
class PluginManager(InstanceManager):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -57,4 +58,3 @@ class PluginManager(InstanceManager):
|
||||||
def unregister(self, cls):
|
def unregister(self, cls):
|
||||||
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
self.remove('%s.%s' % (cls.__module__, cls.__name__))
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"""
|
"""
|
||||||
from threading import local
|
from threading import local
|
||||||
|
|
||||||
|
|
||||||
# stolen from https://github.com/getsentry/sentry/
|
# stolen from https://github.com/getsentry/sentry/
|
||||||
class PluginMount(type):
|
class PluginMount(type):
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
from .destination import DestinationPlugin # NOQA
|
from .destination import DestinationPlugin # noqa
|
||||||
from .issuer import IssuerPlugin # NOQA
|
from .issuer import IssuerPlugin # noqa
|
||||||
from .source import SourcePlugin
|
from .source import SourcePlugin # noqa
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
"""
|
"""
|
||||||
from lemur.plugins.base import Plugin
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
|
||||||
class DestinationPlugin(Plugin):
|
class DestinationPlugin(Plugin):
|
||||||
type = 'destination'
|
type = 'destination'
|
||||||
|
|
||||||
def upload(self):
|
def upload(self):
|
||||||
raise NotImplemented
|
raise NotImplemented
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"""
|
"""
|
||||||
from lemur.plugins.base import Plugin
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
|
||||||
class IssuerPlugin(Plugin):
|
class IssuerPlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
This is the base class from which all of the supported
|
This is the base class from which all of the supported
|
||||||
|
@ -20,4 +21,3 @@ class IssuerPlugin(Plugin):
|
||||||
|
|
||||||
def create_authority(self):
|
def create_authority(self):
|
||||||
raise NotImplemented
|
raise NotImplemented
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"""
|
"""
|
||||||
from lemur.plugins.base import Plugin
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
|
||||||
class SourcePlugin(Plugin):
|
class SourcePlugin(Plugin):
|
||||||
type = 'source'
|
type = 'source'
|
||||||
|
|
||||||
|
@ -16,4 +17,3 @@ class SourcePlugin(Plugin):
|
||||||
|
|
||||||
def get_options(self):
|
def get_options(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ def is_valid(listener_tuple):
|
||||||
|
|
||||||
return listener_tuple
|
return listener_tuple
|
||||||
|
|
||||||
|
|
||||||
def get_all_regions():
|
def get_all_regions():
|
||||||
"""
|
"""
|
||||||
Retrieves all current EC2 regions.
|
Retrieves all current EC2 regions.
|
||||||
|
@ -49,6 +50,7 @@ def get_all_regions():
|
||||||
regions.append(r.name)
|
regions.append(r.name)
|
||||||
return regions
|
return regions
|
||||||
|
|
||||||
|
|
||||||
def get_all_elbs(account_number, region):
|
def get_all_elbs(account_number, region):
|
||||||
"""
|
"""
|
||||||
Fetches all elb objects for a given account and region.
|
Fetches all elb objects for a given account and region.
|
||||||
|
@ -74,7 +76,6 @@ def get_all_elbs(account_number, region):
|
||||||
# return elbs
|
# return elbs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def attach_certificate(account_number, region, name, port, certificate_id):
|
def attach_certificate(account_number, region, name, port, certificate_id):
|
||||||
"""
|
"""
|
||||||
Attaches a certificate to a listener, throws exception
|
Attaches a certificate to a listener, throws exception
|
||||||
|
@ -137,4 +138,3 @@ def delete_listeners(account_number, region, name, ports):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return assume_service(account_number, 'elb', region).delete_load_balancer_listeners(name, ports)
|
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
|
:platform: Unix
|
||||||
:synopsis: Contains helper functions for interactive with AWS IAM Apis.
|
:synopsis: Contains helper functions for interactive with AWS IAM Apis.
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
: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]
|
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):
|
def upload_cert(account_number, cert, private_key, cert_chain=None):
|
||||||
"""
|
"""
|
||||||
Upload a certificate to AWS
|
Upload a certificate to AWS
|
||||||
|
@ -44,7 +29,8 @@ def upload_cert(account_number, cert, private_key, cert_chain=None):
|
||||||
:param cert_chain:
|
:param cert_chain:
|
||||||
:return:
|
: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):
|
def delete_cert(account_number, cert):
|
||||||
|
@ -109,5 +95,3 @@ def digest_aws_cert_response(response):
|
||||||
chain = cert['certificate_chain']
|
chain = cert['certificate_chain']
|
||||||
|
|
||||||
return str(body), str(chain),
|
return str(body), str(chain),
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,11 @@ class AWSDestinationPlugin(DestinationPlugin):
|
||||||
'helpMessage': 'Must be a valid AWS account number!',
|
'helpMessage': 'Must be a valid AWS account number!',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
#'elb': {
|
# 'elb': {
|
||||||
# 'name': {'type': 'name'},
|
# 'name': {'type': 'name'},
|
||||||
# 'region': {'type': 'str'},
|
# 'region': {'type': 'str'},
|
||||||
# 'port': {'type': 'int'}
|
# 'port': {'type': 'int'}
|
||||||
#}
|
# }
|
||||||
|
|
||||||
def upload(self, cert, private_key, cert_chain, options, **kwargs):
|
def upload(self, cert, private_key, cert_chain, options, **kwargs):
|
||||||
iam.upload_cert(find_value('accountNumber', options), cert, private_key, cert_chain=cert_chain)
|
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 = 'Kevin Glisson'
|
||||||
author_url = 'https://github.com/netflix/lemur'
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
options = {
|
options = [
|
||||||
'accountNumber': {'type': 'int'},
|
{
|
||||||
'pollRate': {'type': 'int', 'default': '60'}
|
'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):
|
def get_certificates(self, **kwargs):
|
||||||
certs = []
|
certs = []
|
||||||
|
|
|
@ -25,17 +25,13 @@ def assume_service(account_number, service, region=None):
|
||||||
|
|
||||||
elif service in 'elb':
|
elif service in 'elb':
|
||||||
return boto.ec2.elb.connect_to_region(
|
return boto.ec2.elb.connect_to_region(
|
||||||
region,
|
region,
|
||||||
aws_access_key_id=role.credentials.access_key,
|
aws_access_key_id=role.credentials.access_key,
|
||||||
aws_secret_access_key=role.credentials.secret_key,
|
aws_secret_access_key=role.credentials.secret_key,
|
||||||
security_token=role.credentials.session_token)
|
security_token=role.credentials.session_token)
|
||||||
|
|
||||||
elif service in 'vpc':
|
elif service in 'vpc':
|
||||||
return boto.connect_vpc(
|
return boto.connect_vpc(
|
||||||
aws_access_key_id=role.credentials.access_key,
|
aws_access_key_id=role.credentials.access_key,
|
||||||
aws_secret_access_key=role.credentials.secret_key,
|
aws_secret_access_key=role.credentials.secret_key,
|
||||||
security_token=role.credentials.session_token)
|
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
|
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):
|
class CloudCAException(LemurException):
|
||||||
|
@ -72,7 +72,8 @@ def get_default_issuance(options):
|
||||||
if not options.get('validityStart') and not options.get('validityEnd'):
|
if not options.get('validityStart') and not options.get('validityEnd'):
|
||||||
start = arrow.utcnow()
|
start = arrow.utcnow()
|
||||||
options['validityStart'] = start.floor('second').isoformat()
|
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
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +96,8 @@ def convert_date_to_utc_time(date):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
d = arrow.get(date)
|
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):
|
def process_response(response):
|
||||||
|
@ -152,7 +154,9 @@ class CloudCA(object):
|
||||||
self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
|
self.session.cert = current_app.config.get('CLOUDCA_PEM_PATH')
|
||||||
self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
|
self.ca_bundle = current_app.config.get('CLOUDCA_BUNDLE')
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
current_app.logger.warning(
|
||||||
|
"No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA"
|
||||||
|
)
|
||||||
|
|
||||||
super(CloudCA, self).__init__(*args, **kwargs)
|
super(CloudCA, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -203,7 +207,7 @@ class CloudCA(object):
|
||||||
for ca in self.get(endpoint)['data']['caList']:
|
for ca in self.get(endpoint)['data']['caList']:
|
||||||
try:
|
try:
|
||||||
authorities.append(ca['caName'])
|
authorities.append(ca['caName'])
|
||||||
except AttributeError as e:
|
except AttributeError:
|
||||||
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
||||||
|
|
||||||
return authorities
|
return authorities
|
||||||
|
@ -235,7 +239,8 @@ class CloudCAIssuerPlugin(IssuerPlugin, CloudCA):
|
||||||
options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
|
options['validityStart'] = convert_date_to_utc_time(options['validityStart']).isoformat()
|
||||||
options['validityEnd'] = convert_date_to_utc_time(options['validityEnd']).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)
|
json = process_response(response)
|
||||||
roles = []
|
roles = []
|
||||||
|
@ -326,7 +331,8 @@ class CloudCASourcePlugin(SourcePlugin, CloudCA):
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
endpoint = '{0}/getCert'.format(API_ENDPOINT)
|
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)
|
raw = process_response(response)
|
||||||
|
|
||||||
certs = []
|
certs = []
|
||||||
|
|
|
@ -55,4 +55,3 @@ F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
|
||||||
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ from flask import current_app
|
||||||
from lemur.plugins.bases import IssuerPlugin
|
from lemur.plugins.bases import IssuerPlugin
|
||||||
from lemur.plugins import lemur_verisign as verisign
|
from lemur.plugins import lemur_verisign as verisign
|
||||||
from lemur.plugins.lemur_verisign import constants
|
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
|
# 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):
|
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:
|
:param content:
|
||||||
:return: :raise Exception:
|
:return: :raise Exception:
|
||||||
"""
|
"""
|
||||||
|
@ -99,29 +148,8 @@ class VerisignIssuerPlugin(IssuerPlugin):
|
||||||
"""
|
"""
|
||||||
url = current_app.config.get("VERISIGN_URL") + '/enroll'
|
url = current_app.config.get("VERISIGN_URL") + '/enroll'
|
||||||
|
|
||||||
data = {
|
data = process_options(issuer_options)
|
||||||
'csr': csr,
|
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")
|
|
||||||
|
|
||||||
current_app.logger.info("Requesting a new verisign certificate: {0}".format(data))
|
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'
|
url = current_app.config.get("VERISIGN_URL") + '/getTokens'
|
||||||
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
response = self.session.post(url, headers={'content-type': 'application/x-www-form-urlencoded'})
|
||||||
return handle_response(response.content)['Response']['Order']
|
return handle_response(response.content)['Response']['Order']
|
||||||
|
|
||||||
|
|
|
@ -137,4 +137,3 @@ class PluginsTypeList(AuthenticatedResource):
|
||||||
|
|
||||||
api.add_resource(PluginsList, '/plugins', endpoint='plugins')
|
api.add_resource(PluginsList, '/plugins', endpoint='plugins')
|
||||||
api.add_resource(PluginsTypeList, '/plugins/<plugin_type>', endpoint='pluginType')
|
api.add_resource(PluginsTypeList, '/plugins/<plugin_type>', endpoint='pluginType')
|
||||||
|
|
||||||
|
|
|
@ -36,4 +36,3 @@ class Role(db.Model):
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
blob = self.as_dict()
|
blob = self.as_dict()
|
||||||
return blob
|
return blob
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ from lemur import database
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
from lemur.users.models import User
|
from lemur.users.models import User
|
||||||
|
|
||||||
|
|
||||||
def update(role_id, name, description, users):
|
def update(role_id, name, description, users):
|
||||||
"""
|
"""
|
||||||
Update a role
|
Update a role
|
||||||
|
@ -122,4 +123,3 @@ def render(args):
|
||||||
query = database.sort(query, Role, sort_by, sort_dir)
|
query = database.sort(query, Role, sort_by, sort_dir)
|
||||||
|
|
||||||
return database.paginate(query, page, count)
|
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.setBaseUrl('http://localhost:5000/api/1');
|
||||||
RestangularConfigurer.setDefaultHttpFields({withCredentials: true});
|
RestangularConfigurer.setDefaultHttpFields({withCredentials: true});
|
||||||
|
|
||||||
RestangularConfigurer.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
|
RestangularConfigurer.addResponseInterceptor(function (data, operation) {
|
||||||
var extractedData;
|
var extractedData;
|
||||||
|
|
||||||
// .. to look for getList operations
|
// .. to look for getList operations
|
||||||
if (operation === "getList") {
|
if (operation === 'getList') {
|
||||||
// .. and handle the data and meta data
|
// .. and handle the data and meta data
|
||||||
extractedData = data.items;
|
extractedData = data.items;
|
||||||
extractedData.total = data.total;
|
extractedData.total = data.total;
|
||||||
|
@ -79,7 +79,7 @@ lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
||||||
return extractedData;
|
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
|
// We want to make sure the user is auth'd before any requests
|
||||||
if (!$auth.isAuthenticated()) {
|
if (!$auth.isAuthenticated()) {
|
||||||
$location.path('/login');
|
$location.path('/login');
|
||||||
|
@ -97,7 +97,7 @@ lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
||||||
newParams.sortDir = params[item];
|
newParams.sortDir = params[item];
|
||||||
} else if (item.indexOf(f) > -1) {
|
} else if (item.indexOf(f) > -1) {
|
||||||
var key = regExp.exec(item)[1];
|
var key = regExp.exec(item)[1];
|
||||||
newParams['filter'] = key + ";" + params[item];
|
newParams.filter = key + ';' + params[item];
|
||||||
} else {
|
} else {
|
||||||
newParams[item] = params[item];
|
newParams[item] = params[item];
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ angular.module('lemur')
|
||||||
AuthenticationService.authenticate = function (provider) {
|
AuthenticationService.authenticate = function (provider) {
|
||||||
$auth.authenticate(provider)
|
$auth.authenticate(provider)
|
||||||
.then(
|
.then(
|
||||||
function (user) {
|
function () {
|
||||||
UserService.getCurrentUser();
|
UserService.getCurrentUser();
|
||||||
$rootScope.$emit('user:login');
|
$rootScope.$emit('user:login');
|
||||||
$location.url('/certificates');
|
$location.url('/certificates');
|
||||||
|
@ -41,7 +41,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
AuthenticationService.logout = function () {
|
AuthenticationService.logout = function () {
|
||||||
if (!$auth.isAuthenticated()) {
|
if (!$auth.isAuthenticated()) {
|
||||||
|
@ -56,7 +56,7 @@ angular.module('lemur')
|
||||||
body: 'You have been successfully logged out.'
|
body: 'You have been successfully logged out.'
|
||||||
});
|
});
|
||||||
$location.path('/');
|
$location.path('/');
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,10 +19,10 @@ angular.module('lemur')
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.create = function (authority) {
|
$scope.create = function (authority) {
|
||||||
WizardHandler.wizard().context.loading = true;
|
WizardHandler.wizard().context.loading = true;
|
||||||
AuthorityService.create(authority).then(function (resposne) {
|
AuthorityService.create(authority).then(function () {
|
||||||
WizardHandler.wizard().context.loading = false;
|
WizardHandler.wizard().context.loading = false;
|
||||||
$modalInstance.close();
|
$modalInstance.close();
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
PluginService.get('issuer').then(function (plugins) {
|
PluginService.get('issuer').then(function (plugins) {
|
||||||
|
|
|
@ -35,7 +35,7 @@ angular.module('lemur')
|
||||||
|
|
||||||
$scope.getAuthorityStatus = function () {
|
$scope.getAuthorityStatus = function () {
|
||||||
var def = $q.defer();
|
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;
|
return def;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ angular.module('lemur')
|
||||||
|
|
||||||
$scope.create = function (certificate) {
|
$scope.create = function (certificate) {
|
||||||
WizardHandler.wizard().context.loading = true;
|
WizardHandler.wizard().context.loading = true;
|
||||||
CertificateService.create(certificate).then(function (response) {
|
CertificateService.create(certificate).then(function () {
|
||||||
WizardHandler.wizard().context.loading = false;
|
WizardHandler.wizard().context.loading = false;
|
||||||
$modalInstance.close();
|
$modalInstance.close();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/**
|
'use strict';
|
||||||
* Created by kglisson on 1/19/15.
|
|
||||||
*/
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
.service('CertificateApi', function (LemurRestangular, DomainService) {
|
.service('CertificateApi', function (LemurRestangular, DomainService) {
|
||||||
LemurRestangular.extendModel('certificates', function (obj) {
|
LemurRestangular.extendModel('certificates', function (obj) {
|
||||||
|
@ -102,7 +101,7 @@ angular.module('lemur')
|
||||||
CertificateService.create = function (certificate) {
|
CertificateService.create = function (certificate) {
|
||||||
certificate.attachSubAltName();
|
certificate.attachSubAltName();
|
||||||
return CertificateApi.post(certificate).then(
|
return CertificateApi.post(certificate).then(
|
||||||
function (response) {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: certificate.name,
|
title: certificate.name,
|
||||||
|
@ -132,8 +131,8 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateService.upload = function (certificate) {
|
CertificateService.upload = function (certificate) {
|
||||||
CertificateApi.customPOST(certificate, "upload").then(
|
CertificateApi.customPOST(certificate, 'upload').then(
|
||||||
function (response) {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: certificate.name,
|
title: certificate.name,
|
||||||
|
@ -163,7 +162,7 @@ angular.module('lemur')
|
||||||
certificate.privateKey = response.key;
|
certificate.privateKey = response.key;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function (response) {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: certificate.name,
|
title: certificate.name,
|
||||||
|
|
|
@ -48,7 +48,7 @@ angular.module('lemur')
|
||||||
|
|
||||||
$scope.getCertificateStatus = function () {
|
$scope.getCertificateStatus = function () {
|
||||||
var def = $q.defer();
|
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;
|
return def;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
angular.module('lemur').
|
angular.module('lemur').
|
||||||
filter('titleCase', function () {
|
filter('titleCase', function () {
|
||||||
return function (str) {
|
return function (str) {
|
||||||
|
|
|
@ -7,9 +7,8 @@ angular.module('lemur')
|
||||||
controller: 'DashboardController'
|
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');
|
var baseAccounts = LemurRestangular.all('accounts');
|
||||||
|
|
||||||
baseAccounts.getList()
|
baseAccounts.getList()
|
||||||
|
@ -78,16 +77,16 @@ angular.module('lemur')
|
||||||
|
|
||||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'issuer'})
|
LemurRestangular.all('certificates').customGET('stats', {metric: 'issuer'})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
$scope.issuers = data['items'];
|
$scope.issuers = data.items;
|
||||||
});
|
});
|
||||||
|
|
||||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'bits'})
|
LemurRestangular.all('certificates').customGET('stats', {metric: 'bits'})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
$scope.bits = data['items'];
|
$scope.bits = data.items;
|
||||||
});
|
});
|
||||||
|
|
||||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'not_after'})
|
LemurRestangular.all('certificates').customGET('stats', {metric: 'not_after'})
|
||||||
.then(function (data) {
|
.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 () {
|
DestinationService.update(destination).then(function () {
|
||||||
$modalInstance.close();
|
$modalInstance.close();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
$scope.cancel = function () {
|
$scope.cancel = function () {
|
||||||
$modalInstance.dismiss('cancel');
|
$modalInstance.dismiss('cancel');
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
.service('DomainApi', function (LemurRestangular) {
|
.service('DomainApi', function (LemurRestangular) {
|
||||||
return LemurRestangular.all('domains');
|
return LemurRestangular.all('domains');
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
.service('ELBApi', function (LemurRestangular, ListenerService) {
|
.service('ELBApi', function (LemurRestangular) {
|
||||||
LemurRestangular.extendModel('elbs', function (obj) {
|
LemurRestangular.extendModel('elbs', function (obj) {
|
||||||
return angular.extend(obj, {
|
return angular.extend(obj, {
|
||||||
attachListener: function (listener) {
|
attachListener: function (listener) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
.service('ListenerApi', function (LemurRestangular) {
|
.service('ListenerApi', function (LemurRestangular) {
|
||||||
return LemurRestangular.all('listeners');
|
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')
|
angular.module('lemur')
|
||||||
.service('RoleApi', function (LemurRestangular) {
|
.service('RoleApi', function (LemurRestangular) {
|
||||||
LemurRestangular.extendModel('roles', function (obj) {
|
LemurRestangular.extendModel('roles', function (obj) {
|
||||||
|
@ -108,7 +110,7 @@ angular.module('lemur')
|
||||||
role.username = response.username;
|
role.username = response.username;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function (response) {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: role.name,
|
title: role.name,
|
||||||
|
|
|
@ -18,7 +18,7 @@ angular.module('lemur')
|
||||||
UserService.update(user).then(function () {
|
UserService.update(user).then(function () {
|
||||||
$modalInstance.close();
|
$modalInstance.close();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
$scope.cancel = function () {
|
$scope.cancel = function () {
|
||||||
$modalInstance.dismiss('cancel');
|
$modalInstance.dismiss('cancel');
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
class LemurTestCase(unittest.TestCase):
|
class LemurTestCase(unittest.TestCase):
|
||||||
pass
|
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
|
import pytest
|
||||||
|
|
||||||
from lemur import create_app
|
from lemur import create_app
|
||||||
|
@ -33,14 +34,11 @@ def app():
|
||||||
Creates a new Flask application for a test duration.
|
Creates a new Flask application for a test duration.
|
||||||
Uses application factory `create_app`.
|
Uses application factory `create_app`.
|
||||||
"""
|
"""
|
||||||
app = create_app()
|
_app = create_app(os.path.dirname(os.path.realpath(__file__)) + '/conf.py')
|
||||||
app.config['TESTING'] = True
|
ctx = _app.app_context()
|
||||||
app.config['LEMUR_ENCRYPTION_KEY'] = 'test'
|
|
||||||
|
|
||||||
ctx = app.app_context()
|
|
||||||
ctx.push()
|
ctx.push()
|
||||||
|
|
||||||
yield app
|
yield _app
|
||||||
|
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
|
|
||||||
|
@ -73,4 +71,3 @@ def session(db, request):
|
||||||
@pytest.yield_fixture(scope="function")
|
@pytest.yield_fixture(scope="function")
|
||||||
def client(app, session, client):
|
def client(app, session, client):
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import pytest
|
from lemur.authorities.views import * # noqa
|
||||||
from lemur.authorities.views import *
|
|
||||||
|
|
||||||
#def test_crud(session):
|
# def test_crud(session):
|
||||||
# role = create('role1')
|
# role = create('role1')
|
||||||
# assert role.id > 0
|
# assert role.id > 0
|
||||||
#
|
#
|
||||||
|
@ -149,15 +148,3 @@ def test_admin_authorities_delete(client):
|
||||||
|
|
||||||
def test_admin_certificate_authorities_get(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
|
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
|
import pytest
|
||||||
from lemur.certificates.views import *
|
from lemur.certificates.views import * # noqa
|
||||||
|
|
||||||
def test_valid_authority(session):
|
|
||||||
assert 1 == 2
|
|
||||||
|
|
||||||
|
|
||||||
def test_pem_str():
|
def test_pem_str():
|
||||||
|
@ -40,18 +37,6 @@ def test_create_basic_csr():
|
||||||
assert name.value in csr_config.values()
|
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():
|
def test_cert_get_cn():
|
||||||
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
||||||
from lemur.certificates.models import cert_get_cn
|
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'
|
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.tests.certs import INTERNAL_VALID_SAN_CERT, INTERNAL_VALID_LONG_CERT
|
||||||
from lemur.certificates.models import cert_get_domains
|
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_LONG_CERT) == []
|
||||||
assert cert_get_domains(INTERNAL_VALID_SAN_CERT) == ['example2.long.com', 'example3.long.com', 'san.example.com']
|
assert cert_get_domains(INTERNAL_VALID_SAN_CERT) == ['example2.long.com', 'example3.long.com']
|
||||||
|
|
||||||
|
|
||||||
def test_cert_is_san():
|
def test_cert_is_san():
|
||||||
from lemur.tests.certs import INTERNAL_VALID_SAN_CERT, INTERNAL_VALID_LONG_CERT
|
from lemur.tests.certs import INTERNAL_VALID_SAN_CERT, INTERNAL_VALID_LONG_CERT
|
||||||
from lemur.certificates.models import cert_is_san
|
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
|
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.tests.certs import INTERNAL_VALID_WILDCARD_CERT, INTERNAL_VALID_LONG_CERT
|
||||||
from lemur.certificates.models import cert_is_wildcard
|
from lemur.certificates.models import cert_is_wildcard
|
||||||
assert cert_is_wildcard(INTERNAL_VALID_WILDCARD_CERT) == True
|
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():
|
def test_cert_get_bitstrength():
|
||||||
|
@ -87,6 +72,7 @@ def test_cert_get_bitstrength():
|
||||||
from lemur.certificates.models import cert_get_bitstrength
|
from lemur.certificates.models import cert_get_bitstrength
|
||||||
assert cert_get_bitstrength(INTERNAL_VALID_LONG_CERT) == 2048
|
assert cert_get_bitstrength(INTERNAL_VALID_LONG_CERT) == 2048
|
||||||
|
|
||||||
|
|
||||||
def test_cert_get_issuer():
|
def test_cert_get_issuer():
|
||||||
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
from lemur.tests.certs import INTERNAL_VALID_LONG_CERT
|
||||||
from lemur.certificates.models import cert_get_issuer
|
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):
|
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
|
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.service import * # noqa
|
||||||
from lemur.destinations.views import *
|
from lemur.destinations.views import * # noqa
|
||||||
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
|
||||||
|
|
||||||
def test_crud(session):
|
def test_crud(session):
|
||||||
destination = create('111111', 'destination1')
|
destination = create('testdest', 'aws-destination', {}, description='destination1')
|
||||||
assert destination.id > 0
|
assert destination.id > 0
|
||||||
|
|
||||||
destination = update(destination.id, 11111, 'destination2')
|
destination = update(destination.id, 'testdest2', {}, 'destination2')
|
||||||
assert destination.label == 'destination2'
|
assert destination.label == 'testdest2'
|
||||||
|
|
||||||
assert len(get_all()) == 1
|
assert len(get_all()) == 1
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ def test_destination_patch(client):
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
||||||
|
|
||||||
|
|
||||||
def test_auth_destination_get(client):
|
def test_auth_destination_get(client):
|
||||||
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
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 = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
||||||
|
|
||||||
|
|
||||||
def test_admin_destination_get(client):
|
def test_admin_destination_get(client):
|
||||||
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
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):
|
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):
|
def test_admin_destination_patch(client):
|
||||||
|
@ -119,13 +121,13 @@ def test_admin_destinations_get(client):
|
||||||
|
|
||||||
def test_admin_destinations_crud(client):
|
def test_admin_destinations_crud(client):
|
||||||
assert client.post(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||||
data = {'destinationNumber': 111, 'label': 'test', 'comments': 'test'}
|
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)
|
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 resp.status_code == 200
|
||||||
assert client.get(api.url_for(Destinations, destination_id=resp.json['id']), headers=VALID_ADMIN_HEADER_TOKEN).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)
|
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
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
|
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)
|
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
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):
|
def test_domain_get(client):
|
||||||
assert client.get(api.url_for(Domains, domain_id=1)).status_code == 401
|
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 = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
||||||
|
|
||||||
|
|
||||||
def test_auth_domain_get(client):
|
def test_auth_domain_get(client):
|
||||||
assert client.get(api.url_for(Domains, domain_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
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 = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
||||||
|
|
||||||
|
|
||||||
def test_admin_domain_get(client):
|
def test_admin_domain_get(client):
|
||||||
assert client.get(api.url_for(Domains, domain_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
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):
|
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
|
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):
|
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
|
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):
|
# def test_validate_authority(self):
|
||||||
# pass
|
# pass
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from json import dumps
|
from json import dumps
|
||||||
from lemur.roles.service import *
|
from lemur.roles.service import * # noqa
|
||||||
from lemur.roles.views import *
|
from lemur.roles.views import * # noqa
|
||||||
|
|
||||||
|
|
||||||
def test_crud(session):
|
def test_crud(session):
|
||||||
|
|
|
@ -84,5 +84,3 @@ class User(db.Model):
|
||||||
|
|
||||||
|
|
||||||
listen(User, 'before_insert', hash_password)
|
listen(User, 'before_insert', hash_password)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -145,5 +145,3 @@ def render(args):
|
||||||
query = database.sort(query, User, sort_by, sort_dir)
|
query = database.sort(query, User, sort_by, sort_dir)
|
||||||
|
|
||||||
return database.paginate(query, page, count)
|
return database.paginate(query, page, count)
|
||||||
|
|
||||||
|
|
||||||
|
|
13
package.json
13
package.json
|
@ -28,7 +28,6 @@
|
||||||
"gulp-imagemin": "^0.6.2",
|
"gulp-imagemin": "^0.6.2",
|
||||||
"gulp-inject": "~1.0.1",
|
"gulp-inject": "~1.0.1",
|
||||||
"gulp-jshint": "^1.10.0",
|
"gulp-jshint": "^1.10.0",
|
||||||
"gulp-karma": "^0.0.4",
|
|
||||||
"gulp-load-plugins": "^0.5.3",
|
"gulp-load-plugins": "^0.5.3",
|
||||||
"gulp-minify-html": "~0.1.4",
|
"gulp-minify-html": "~0.1.4",
|
||||||
"gulp-ng-annotate": "~0.5.2",
|
"gulp-ng-annotate": "~0.5.2",
|
||||||
|
@ -51,7 +50,9 @@
|
||||||
"main-bower-files": "^1.0.2",
|
"main-bower-files": "^1.0.2",
|
||||||
"require-dir": "~0.3.0",
|
"require-dir": "~0.3.0",
|
||||||
"streamqueue": "^0.1.1",
|
"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": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
@ -59,9 +60,13 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "bower install --allow-root",
|
"postinstall": "bower install --allow-root",
|
||||||
"pretest": "npm install && npm run build_static",
|
"pretest": "npm install && npm run build_static",
|
||||||
"build_static": "gulp dist",
|
"build_static": "gulp build",
|
||||||
"prelint": "npm install",
|
"prelint": "npm install",
|
||||||
"lint": "jshint app/",
|
"lint": "jshint lemur/static/app/",
|
||||||
"test": "gulp test"
|
"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 import log
|
||||||
from distutils.core import Command
|
from distutils.core import Command
|
||||||
from setuptools.command.develop import develop
|
from setuptools.command.develop import develop
|
||||||
|
from setuptools.command.install import install
|
||||||
from setuptools.command.sdist import sdist
|
from setuptools.command.sdist import sdist
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
from subprocess import check_output
|
from subprocess import check_output
|
||||||
|
|
||||||
ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__)))
|
ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__)))
|
||||||
|
|
||||||
install_requires=[
|
install_requires = [
|
||||||
'Flask>=0.10.1',
|
'Flask>=0.10.1',
|
||||||
'Flask-RESTful>=0.3.3',
|
'Flask-RESTful>=0.3.3',
|
||||||
'Flask-SQLAlchemy>=1.0.5',
|
'Flask-SQLAlchemy>=1.0.5',
|
||||||
|
@ -37,7 +38,7 @@ install_requires=[
|
||||||
'six>=1.9.0',
|
'six>=1.9.0',
|
||||||
'gunicorn>=19.3.0',
|
'gunicorn>=19.3.0',
|
||||||
'pycrypto>=2.6.1',
|
'pycrypto>=2.6.1',
|
||||||
'cryptography>=0.9',
|
'cryptography>=1.0dev',
|
||||||
'pyopenssl>=0.15.1',
|
'pyopenssl>=0.15.1',
|
||||||
'pyjwt>=1.0.1',
|
'pyjwt>=1.0.1',
|
||||||
'xmltodict>=0.9.2'
|
'xmltodict>=0.9.2'
|
||||||
|
@ -45,10 +46,10 @@ install_requires=[
|
||||||
|
|
||||||
tests_require = [
|
tests_require = [
|
||||||
'pyflakes',
|
'pyflakes',
|
||||||
'moto',
|
'moto==0.4.6',
|
||||||
'nose',
|
'nose==1.3.7',
|
||||||
'pytest',
|
'pytest==2.7.2',
|
||||||
'pytest-flask'
|
'pytest-flask==0.8.1'
|
||||||
]
|
]
|
||||||
|
|
||||||
docs_require = [
|
docs_require = [
|
||||||
|
@ -56,6 +57,26 @@ docs_require = [
|
||||||
'sphinxcontrib-httpdomain'
|
'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):
|
class DevelopWithBuildStatic(develop):
|
||||||
def install_for_development(self):
|
def install_for_development(self):
|
||||||
self.run_command('build_static')
|
self.run_command('build_static')
|
||||||
|
@ -79,7 +100,7 @@ class BuildStatic(Command):
|
||||||
log.info("running [npm install --quiet]")
|
log.info("running [npm install --quiet]")
|
||||||
check_output(['npm', 'install', '--quiet'], cwd=ROOT)
|
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)
|
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'build'], cwd=ROOT)
|
||||||
log.info("running [gulp package]")
|
log.info("running [gulp package]")
|
||||||
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'package'], cwd=ROOT)
|
check_output([os.path.join(ROOT, 'node_modules', '.bin', 'gulp'), 'package'], cwd=ROOT)
|
||||||
|
@ -96,12 +117,15 @@ setup(
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
extras_require={
|
extras_require={
|
||||||
'tests': tests_require,
|
'tests': tests_require,
|
||||||
'docs': docs_require
|
'docs': docs_require,
|
||||||
|
'dev': dev_requires,
|
||||||
},
|
},
|
||||||
cmdclass={
|
cmdclass={
|
||||||
'build_static': BuildStatic,
|
'build_static': BuildStatic,
|
||||||
'develop': DevelopWithBuildStatic,
|
'develop': DevelopWithBuildStatic,
|
||||||
'sdist': SdistWithBuildStatic
|
'sdist': SdistWithBuildStatic,
|
||||||
|
'install': SmartInstall
|
||||||
|
|
||||||
},
|
},
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
|
Loading…
Reference in New Issue