Some unit tests
This commit is contained in:
parent
51d2990eb9
commit
f0f2092fb4
|
@ -135,7 +135,6 @@ def unwrap_pagination(data, output_schema):
|
||||||
marshaled_data = {'total': len(data)}
|
marshaled_data = {'total': len(data)}
|
||||||
marshaled_data['items'] = output_schema.dump(data, many=True).data
|
marshaled_data['items'] = output_schema.dump(data, many=True).data
|
||||||
return marshaled_data
|
return marshaled_data
|
||||||
|
|
||||||
return output_schema.dump(data).data
|
return output_schema.dump(data).data
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.dns_providers.models import DnsProviders
|
from lemur.dns_providers.models import DnsProviders
|
||||||
|
|
||||||
|
@ -31,3 +34,10 @@ def delete(dns_provider_id):
|
||||||
:param dns_provider_id: Lemur assigned ID
|
:param dns_provider_id: Lemur assigned ID
|
||||||
"""
|
"""
|
||||||
database.delete(get(dns_provider_id))
|
database.delete(get(dns_provider_id))
|
||||||
|
|
||||||
|
|
||||||
|
def get_types():
|
||||||
|
provider_config = current_app.config.get('ACME_DNS_PROVIDER_TYPES')
|
||||||
|
if not provider_config:
|
||||||
|
raise Exception("No DNS Provider configuration specified.")
|
||||||
|
return provider_config
|
||||||
|
|
|
@ -78,10 +78,24 @@ class DnsProvidersList(AuthenticatedResource):
|
||||||
args['user'] = g.user
|
args['user'] = g.user
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
|
|
||||||
@admin_permission.require(http_exception=403)
|
@admin_permission.require(http_exception=403)
|
||||||
def delete(self, dns_provider_id):
|
def delete(self, dns_provider_id):
|
||||||
service.delete(dns_provider_id)
|
service.delete(dns_provider_id)
|
||||||
return {'result': True}
|
return {'result': True}
|
||||||
|
|
||||||
|
|
||||||
|
class DnsProviderTypes(AuthenticatedResource):
|
||||||
|
""" Defines the 'dns_provider_types' endpoint """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(DnsProviderTypes, self).__init__()
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return service.get_types()
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(DnsProvidersList, '/dns_providers', endpoint='dns_providers')
|
api.add_resource(DnsProvidersList, '/dns_providers', endpoint='dns_providers')
|
||||||
|
api.add_resource(DnsProvidersList, '/dns_providers/<int:dns_provider_id>', endpoint='dns_provider')
|
||||||
|
api.add_resource(DnsProviderTypes, '/dns_provider_types', endpoint='dns_provider_types')
|
||||||
|
|
|
@ -113,13 +113,11 @@ def setup_acme_client(authority):
|
||||||
if not authority.options:
|
if not authority.options:
|
||||||
raise Exception("Invalid authority. Options not set")
|
raise Exception("Invalid authority. Options not set")
|
||||||
options = {}
|
options = {}
|
||||||
for o in json.loads(authority.options):
|
authority_options = json.loads(authority.options)
|
||||||
print(o)
|
options[authority_options.get("name")] = authority_options.get("value")
|
||||||
options[o.get("name")] = o.get("value")
|
email = authority_options.get('email', current_app.config.get('ACME_EMAIL'))
|
||||||
email = options.get('email', current_app.config.get('ACME_EMAIL'))
|
tel = authority_options.get('telephone', current_app.config.get('ACME_TEL'))
|
||||||
tel = options.get('telephone', current_app.config.get('ACME_TEL'))
|
directory_url = authority_options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))
|
||||||
directory_url = options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))
|
|
||||||
contact = ('mailto:{}'.format(email), 'tel:{}'.format(tel))
|
|
||||||
|
|
||||||
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
|
key = jose.JWKRSA(key=generate_private_key('RSA2048'))
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,119 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
def test_get_certificates(app):
|
from acme import challenges
|
||||||
|
|
||||||
|
from lemur.plugins.lemur_acme import plugin, route53, cloudflare
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
p = plugins.get('acme-issuer')
|
from mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
|
|
||||||
|
class TestAcme(unittest.TestCase):
|
||||||
|
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.len', return_value=1)
|
||||||
|
def test_find_dns_challenge(self, mock_len):
|
||||||
|
assert mock_len
|
||||||
|
|
||||||
|
from acme import challenges
|
||||||
|
c = challenges.DNS01()
|
||||||
|
|
||||||
|
mock_authz = Mock()
|
||||||
|
mock_authz.body.resolved_combinations = []
|
||||||
|
mock_entry = Mock()
|
||||||
|
mock_entry.chall = c
|
||||||
|
mock_authz.body.resolved_combinations.append(mock_entry)
|
||||||
|
result = yield plugin.find_dns_challenge(mock_authz)
|
||||||
|
self.assertEqual(result, mock_entry)
|
||||||
|
|
||||||
|
def test_authz_record(self):
|
||||||
|
a = plugin.AuthorizationRecord("host", "authz", "challenge", "id")
|
||||||
|
self.assertEqual(type(a), plugin.AuthorizationRecord)
|
||||||
|
|
||||||
|
@patch('acme.client.Client')
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.len', return_value=1)
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.find_dns_challenge')
|
||||||
|
def test_start_dns_challenge(self, mock_find_dns_challenge, mock_len, mock_app, mock_acme):
|
||||||
|
assert mock_len
|
||||||
|
mock_app.logger.debug = Mock()
|
||||||
|
mock_authz = Mock()
|
||||||
|
mock_authz.body.resolved_combinations = []
|
||||||
|
mock_entry = MagicMock()
|
||||||
|
from acme import challenges
|
||||||
|
c = challenges.DNS01()
|
||||||
|
mock_entry.chall = c
|
||||||
|
mock_authz.body.resolved_combinations.append(mock_entry)
|
||||||
|
mock_acme.request_domain_challenges = Mock(return_value=mock_authz)
|
||||||
|
mock_dns_provider = Mock()
|
||||||
|
mock_dns_provider.create_txt_record = Mock(return_value=1)
|
||||||
|
|
||||||
|
values = [mock_entry]
|
||||||
|
iterable = mock_find_dns_challenge.return_value
|
||||||
|
iterator = iter(values)
|
||||||
|
iterable.__iter__.return_value = iterator
|
||||||
|
result = plugin.start_dns_challenge(mock_acme, "accountid", "host", mock_dns_provider)
|
||||||
|
self.assertEqual(type(result), plugin.AuthorizationRecord)
|
||||||
|
|
||||||
|
@patch('acme.client.Client')
|
||||||
|
def test_complete_dns_challenge_success(self, mock_acme):
|
||||||
|
mock_dns_provider = Mock()
|
||||||
|
mock_dns_provider.wait_for_dns_change = Mock(return_value=True)
|
||||||
|
|
||||||
|
mock_authz = Mock()
|
||||||
|
mock_authz.dns_challenge.response = Mock()
|
||||||
|
mock_authz.dns_challenge.response.simple_verify = Mock(return_value=True)
|
||||||
|
|
||||||
|
plugin.complete_dns_challenge(mock_acme, "accountid", mock_authz, mock_dns_provider)
|
||||||
|
|
||||||
|
@patch('acme.client.Client')
|
||||||
|
def test_complete_dns_challenge_fail(self, mock_acme):
|
||||||
|
mock_dns_provider = Mock()
|
||||||
|
mock_dns_provider.wait_for_dns_change = Mock(return_value=True)
|
||||||
|
|
||||||
|
mock_authz = Mock()
|
||||||
|
mock_authz.dns_challenge.response = Mock()
|
||||||
|
mock_authz.dns_challenge.response.simple_verify = Mock(return_value=False)
|
||||||
|
self.assertRaises(
|
||||||
|
ValueError,
|
||||||
|
plugin.complete_dns_challenge(mock_acme, "accountid", mock_authz, mock_dns_provider)
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch('acme.client.Client')
|
||||||
|
@patch('OpenSSL.crypto', return_value="mock_cert")
|
||||||
|
@patch('josepy.util.ComparableX509')
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.find_dns_challenge')
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||||
|
def test_request_certificate(self, mock_current_app, mock_find_dns_challenge, mock_jose, mock_crypto, mock_acme):
|
||||||
|
mock_cert_response = Mock()
|
||||||
|
mock_cert_response.body = "123"
|
||||||
|
mock_cert_response_full = [mock_cert_response, True]
|
||||||
|
mock_acme.poll_and_request_issuance = Mock(return_value=mock_cert_response_full)
|
||||||
|
mock_authz = []
|
||||||
|
mock_authz_record = MagicMock()
|
||||||
|
mock_authz_record.authz = Mock()
|
||||||
|
mock_authz.append(mock_authz_record)
|
||||||
|
mock_acme.fetch_chain = Mock(return_value="mock_chain")
|
||||||
|
mock_crypto.dump_certificate = Mock(return_value=b'chain')
|
||||||
|
|
||||||
|
plugin.request_certificate(mock_acme, [], "mock_csr")
|
||||||
|
|
||||||
|
def test_setup_acme_client_fail(self):
|
||||||
|
mock_authority = Mock()
|
||||||
|
mock_authority.options = []
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
plugin.setup_acme_client(mock_authority)
|
||||||
|
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.Client')
|
||||||
|
@patch('lemur.plugins.lemur_acme.plugin.current_app')
|
||||||
|
def test_setup_acme_client_success(self, mock_current_app, mock_acme):
|
||||||
|
mock_authority = Mock()
|
||||||
|
mock_authority.options = '{"o": "mock_name", "v": "mock_value"}'
|
||||||
|
mock_client = Mock()
|
||||||
|
mock_registration = Mock()
|
||||||
|
mock_registration.uri = "http://test.com"
|
||||||
|
mock_client.register = mock_registration
|
||||||
|
mock_client.agree_to_tos = Mock(return_value=True)
|
||||||
|
mock_acme.return_value = mock_client
|
||||||
|
result_client, result_registration = plugin.setup_acme_client(mock_authority)
|
||||||
|
assert result_client
|
||||||
|
assert result_registration
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ angular.module('lemur')
|
||||||
.controller('DnsProviderCreateController', function ($scope, $uibModalInstance, PluginService, DnsProviderService, LemurRestangular, toaster) {
|
.controller('DnsProviderCreateController', function ($scope, $uibModalInstance, PluginService, DnsProviderService, LemurRestangular, toaster) {
|
||||||
$scope.dns_provider = LemurRestangular.restangularizeElement(null, {}, 'dns_providers');
|
$scope.dns_provider = LemurRestangular.restangularizeElement(null, {}, 'dns_providers');
|
||||||
|
|
||||||
|
PluginService.getByName('acme-issuer').then(function (acme) {
|
||||||
|
$scope.acme = acme;
|
||||||
|
});
|
||||||
|
|
||||||
PluginService.getByType('dns_provider').then(function (plugins) {
|
PluginService.getByType('dns_provider').then(function (plugins) {
|
||||||
$scope.plugins = plugins;
|
$scope.plugins = plugins;
|
||||||
});
|
});
|
||||||
|
@ -45,28 +49,11 @@ angular.module('lemur')
|
||||||
DnsProviderApi.get(editId).then(function (dns_provider) {
|
DnsProviderApi.get(editId).then(function (dns_provider) {
|
||||||
$scope.dns_provider = dns_provider;
|
$scope.dns_provider = dns_provider;
|
||||||
|
|
||||||
PluginService.getByType('dns_provider').then(function (plugins) {
|
PluginService.getByName('acme-issuer').then(function (acme) {
|
||||||
$scope.plugins = plugins;
|
$scope.acme = acme;
|
||||||
|
|
||||||
_.each($scope.plugins, function (plugin) {
|
_.each($scope.acme, function (opts) {
|
||||||
if (plugin.slug === $scope.dns_provider.plugin.slug) {
|
console.log(opts);
|
||||||
plugin.pluginOptions = $scope.dns_provider.plugin.pluginOptions;
|
|
||||||
$scope.dns_provider.plugin = plugin;
|
|
||||||
_.each($scope.dns_provider.plugin.pluginOptions, function (option) {
|
|
||||||
if (option.type === 'export-plugin') {
|
|
||||||
PluginService.getByType('export').then(function (plugins) {
|
|
||||||
$scope.exportPlugins = plugins;
|
|
||||||
|
|
||||||
_.each($scope.exportPlugins, function (plugin) {
|
|
||||||
if (plugin.slug === option.value.slug) {
|
|
||||||
plugin.pluginOptions = option.value.pluginOptions;
|
|
||||||
option.value = plugin;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span>
|
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
<h3><span ng-show="!dns_provider.fromServer">Create</span><span ng-show="dns_provider.fromServer">Edit</span>
|
<h3><span ng-show="!dns_provider.fromServer">Create</span><span ng-show="dns_provider.fromServer">Edit</span>
|
||||||
DnsProvider <span class="text-muted"><small>oh the places you will go!</small></span></h3>
|
Dns Provider <span class="text-muted"><small>route all the things</small></span></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': createForm.label.$invalid, 'has-success': !createForm.label.$invalid&&createForm.label.$dirty}">
|
ng-class="{'has-error': createForm.label.$invalid, 'has-success': !createForm.label.$invalid&&createForm.label.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Label
|
Name
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="label" ng-model="dns_provider.label" placeholder="Label" class="form-control" required/>
|
<input name="name" ng-model="dns_provider.name" placeholder="Name" class="form-control" required/>
|
||||||
<p ng-show="createForm.label.$invalid && !createForm.label.$pristine" class="help-block">You must enter an
|
<p ng-show="createForm.label.$invalid && !createForm.label.$pristine" class="help-block">You must enter an
|
||||||
dns_provider label</p>
|
dns_provider name</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -22,16 +22,16 @@
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea name="comments" ng-model="dns_provider.description" placeholder="Something elegant"
|
<textarea name="description" ng-model="dns_provider.description" placeholder="Something elegant"
|
||||||
class="form-control"></textarea>
|
class="form-control"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Plugin
|
Provider Type
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" ng-model="dns_provider.plugin" ng-options="plugin.title for plugin in plugins"
|
<select class="form-control" ng-model="dns_provider.provider_type" ng-options="plugin for plugin in ['route53', 'cloudflare']"
|
||||||
required></select>
|
required></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,7 +50,7 @@ angular.module('lemur')
|
||||||
var uibModalInstance = $uibModal.open({
|
var uibModalInstance = $uibModal.open({
|
||||||
animation: true,
|
animation: true,
|
||||||
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
||||||
controller: 'DnsProvidersEditController',
|
controller: 'DnsProviderEditController',
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
resolve: {
|
resolve: {
|
||||||
|
@ -69,7 +69,7 @@ angular.module('lemur')
|
||||||
$scope.create = function () {
|
$scope.create = function () {
|
||||||
var uibModalInstance = $uibModal.open({
|
var uibModalInstance = $uibModal.open({
|
||||||
animation: true,
|
animation: true,
|
||||||
controller: 'DnsProvidersCreateController',
|
controller: 'DnsProviderCreateController',
|
||||||
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
templateUrl: '/angular/dns_providers/dns_provider/dns_provider.tpl.html',
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
backdrop: 'static'
|
backdrop: 'static'
|
||||||
|
|
|
@ -29,12 +29,6 @@
|
||||||
<li>{{ dns_provider.providerType }}</li>
|
<li>{{ dns_provider.providerType }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Domains'" sortable="'domains'" filter="{ 'domains': 'text' }">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>{{ dns_provider.domains }}</li>
|
|
||||||
<li><span class="text-muted">{{ dns_provider.description }}</span></li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
<div class="btn-group-vertical pull-right">
|
<div class="btn-group-vertical pull-right">
|
||||||
<button uib-tooltip="Edit DNS Provider" ng-click="edit(dns_provider.id)" class="btn btn-sm btn-info">
|
<button uib-tooltip="Edit DNS Provider" ng-click="edit(dns_provider.id)" class="btn btn-sm btn-info">
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
asn1crypto==0.24.0 # via cryptography
|
asn1crypto==0.24.0 # via cryptography
|
||||||
attrs==17.4.0 # via pytest
|
attrs==17.4.0 # via pytest
|
||||||
aws-xray-sdk==0.95 # via moto
|
aws-xray-sdk==0.95 # via moto
|
||||||
boto3==1.7.6 # via moto
|
boto3==1.7.8 # via moto
|
||||||
boto==2.48.0 # via moto
|
boto==2.48.0 # via moto
|
||||||
botocore==1.10.6 # via boto3, moto, s3transfer
|
botocore==1.10.8 # via boto3, moto, s3transfer
|
||||||
certifi==2018.4.16 # via requests
|
certifi==2018.4.16 # via requests
|
||||||
cffi==1.11.5 # via cryptography
|
cffi==1.11.5 # via cryptography
|
||||||
chardet==3.0.4 # via requests
|
chardet==3.0.4 # via requests
|
||||||
|
@ -43,7 +43,7 @@ pycparser==2.18 # via cffi
|
||||||
pyflakes==1.6.0
|
pyflakes==1.6.0
|
||||||
pytest-flask==0.10.0
|
pytest-flask==0.10.0
|
||||||
pytest-mock==1.9.0
|
pytest-mock==1.9.0
|
||||||
pytest==3.5.0
|
pytest==3.5.1
|
||||||
python-dateutil==2.6.1 # via botocore, faker, freezegun, moto
|
python-dateutil==2.6.1 # via botocore, faker, freezegun, moto
|
||||||
pytz==2018.4 # via moto
|
pytz==2018.4 # via moto
|
||||||
pyyaml==3.12 # via pyaml
|
pyyaml==3.12 # via pyaml
|
||||||
|
|
|
@ -4,6 +4,7 @@ acme
|
||||||
alembic-autogenerate-enums
|
alembic-autogenerate-enums
|
||||||
arrow
|
arrow
|
||||||
boto3
|
boto3
|
||||||
|
CloudFlare
|
||||||
cryptography
|
cryptography
|
||||||
Flask-Bcrypt==0.7.1
|
Flask-Bcrypt==0.7.1
|
||||||
Flask-Mail==0.9.1
|
Flask-Mail==0.9.1
|
||||||
|
|
|
@ -12,10 +12,11 @@ arrow==0.12.1
|
||||||
asn1crypto==0.24.0 # via cryptography
|
asn1crypto==0.24.0 # via cryptography
|
||||||
bcrypt==3.1.4 # via flask-bcrypt, paramiko
|
bcrypt==3.1.4 # via flask-bcrypt, paramiko
|
||||||
blinker==1.4 # via flask-mail, flask-principal, raven
|
blinker==1.4 # via flask-mail, flask-principal, raven
|
||||||
boto3==1.7.6
|
boto3==1.7.8
|
||||||
botocore==1.10.6 # via boto3, s3transfer
|
botocore==1.10.8 # via boto3, s3transfer
|
||||||
cffi==1.11.5 # via bcrypt, cryptography, pynacl
|
cffi==1.11.5 # via bcrypt, cryptography, pynacl
|
||||||
click==6.7 # via flask
|
click==6.7 # via flask
|
||||||
|
cloudflare==2.1.0
|
||||||
cryptography==2.2.2
|
cryptography==2.2.2
|
||||||
docutils==0.14 # via botocore
|
docutils==0.14 # via botocore
|
||||||
flask-bcrypt==0.7.1
|
flask-bcrypt==0.7.1
|
||||||
|
@ -35,13 +36,14 @@ itsdangerous==0.24 # via flask
|
||||||
jinja2==2.10
|
jinja2==2.10
|
||||||
jmespath==0.9.3 # via boto3, botocore
|
jmespath==0.9.3 # via boto3, botocore
|
||||||
josepy==1.1.0 # via acme
|
josepy==1.1.0 # via acme
|
||||||
|
jsonlines==1.2.0 # via cloudflare
|
||||||
lockfile==0.12.2
|
lockfile==0.12.2
|
||||||
mako==1.0.7 # via alembic
|
mako==1.0.7 # via alembic
|
||||||
markupsafe==1.0 # via jinja2, mako
|
markupsafe==1.0 # via jinja2, mako
|
||||||
marshmallow-sqlalchemy==0.13.2
|
marshmallow-sqlalchemy==0.13.2
|
||||||
marshmallow==2.15.0
|
marshmallow==2.15.0
|
||||||
mock==2.0.0 # via acme
|
mock==2.0.0 # via acme
|
||||||
ndg-httpsclient==0.4.4
|
ndg-httpsclient==0.5.0
|
||||||
paramiko==2.4.1
|
paramiko==2.4.1
|
||||||
pbr==4.0.2 # via mock
|
pbr==4.0.2 # via mock
|
||||||
pem==17.1.0
|
pem==17.1.0
|
||||||
|
@ -57,6 +59,7 @@ python-dateutil==2.7.2 # via alembic, arrow, botocore
|
||||||
python-editor==1.0.3 # via alembic
|
python-editor==1.0.3 # via alembic
|
||||||
python-ldap==3.0.0
|
python-ldap==3.0.0
|
||||||
pytz==2018.4 # via acme, flask-restful, pyrfc3339
|
pytz==2018.4 # via acme, flask-restful, pyrfc3339
|
||||||
|
pyyaml==3.12 # via cloudflare
|
||||||
raven[flask]==6.7.0
|
raven[flask]==6.7.0
|
||||||
requests[security]==2.11.1
|
requests[security]==2.11.1
|
||||||
retrying==1.3.3
|
retrying==1.3.3
|
||||||
|
|
Loading…
Reference in New Issue