Some unit tests

This commit is contained in:
Curtis Castrapel 2018-04-25 11:19:34 -07:00
parent 51d2990eb9
commit f0f2092fb4
12 changed files with 175 additions and 54 deletions

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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'))

View File

@ -1,4 +1,119 @@
import unittest
from acme import challenges
from lemur.plugins.lemur_acme import plugin, route53, cloudflare
from lemur.plugins.base import plugins
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
def test_get_certificates(app):
from lemur.plugins.base import plugins
p = plugins.get('acme-issuer')

View File

@ -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,30 +49,13 @@ 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;
}
});
});
}
});
}
}); });
});
}); });
$scope.save = function (dns_provider) { $scope.save = function (dns_provider) {

View File

@ -2,19 +2,19 @@
<button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">&times;</span> <button type="button" class="close" ng-click="cancel()" aria-label="Close"><span aria-hidden="true">&times;</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>

View File

@ -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'

View File

@ -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">

View File

@ -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

View File

@ -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

View File

@ -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