Merge branch 'master' of https://github.com/rmoesbergen/lemur into rmoesbergen-master

This commit is contained in:
Hossein Shafagh 2019-01-29 17:17:30 -08:00
commit 81e5abd23d
16 changed files with 199 additions and 67 deletions

View File

@ -227,6 +227,10 @@ class Certificate(db.Model):
def location(self): def location(self):
return defaults.location(self.parsed_cert) return defaults.location(self.parsed_cert)
@property
def distinguished_name(self):
return self.parsed_cert.subject.rfc4514_string()
@property @property
def key_type(self): def key_type(self):
if isinstance(self.parsed_cert.public_key(), rsa.RSAPublicKey): if isinstance(self.parsed_cert.public_key(), rsa.RSAPublicKey):

View File

@ -206,6 +206,7 @@ class CertificateOutputSchema(LemurOutputSchema):
cn = fields.String() cn = fields.String()
common_name = fields.String(attribute='cn') common_name = fields.String(attribute='cn')
distinguished_name = fields.String()
not_after = fields.DateTime() not_after = fields.DateTime()
validity_end = ArrowDateTime(attribute='not_after') validity_end = ArrowDateTime(attribute='not_after')

View File

@ -6,6 +6,7 @@
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com> .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
""" """
import base64 import base64
import arrow
from builtins import str from builtins import str
from flask import Blueprint, make_response, jsonify, g from flask import Blueprint, make_response, jsonify, g
@ -660,6 +661,51 @@ class Certificates(AuthenticatedResource):
log_service.create(g.current_user, 'update_cert', certificate=cert) log_service.create(g.current_user, 'update_cert', certificate=cert)
return cert return cert
def delete(self, certificate_id, data=None):
"""
.. http:delete:: /certificates/1
Delete a certificate
**Example request**:
.. sourcecode:: http
DELETE /certificates/1 HTTP/1.1
Host: example.com
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
:reqheader Authorization: OAuth token to authenticate
:statuscode 204: no error
:statuscode 403: unauthenticated
:statusoode 404: certificate not found
"""
cert = service.get(certificate_id)
if not cert:
return dict(message="Cannot find specified certificate"), 404
# allow creators
if g.current_user != cert.user:
owner_role = role_service.get_by_name(cert.owner)
permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
if not permission.can():
return dict(message='You are not authorized to delete this certificate'), 403
if arrow.get(cert.not_after) > arrow.utcnow():
return dict(message='Certificate is still valid, only expired certificates can be deleted'), 412
service.update(certificate_id, deleted=True)
log_service.create(g.current_user, 'delete_cert', certificate=cert)
return '', 204
class NotificationCertificatesList(AuthenticatedResource): class NotificationCertificatesList(AuthenticatedResource):
""" Defines the 'certificates' endpoint """ """ Defines the 'certificates' endpoint """

View File

@ -18,6 +18,6 @@ class Log(db.Model):
__tablename__ = 'logs' __tablename__ = 'logs'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
certificate_id = Column(Integer, ForeignKey('certificates.id')) certificate_id = Column(Integer, ForeignKey('certificates.id'))
log_type = Column(Enum('key_view', 'create_cert', 'update_cert', 'revoke_cert', name='log_type'), nullable=False) log_type = Column(Enum('key_view', 'create_cert', 'update_cert', 'revoke_cert', 'delete_cert', name='log_type'), nullable=False)
logged_at = Column(ArrowType(), PassiveDefault(func.now()), nullable=False) logged_at = Column(ArrowType(), PassiveDefault(func.now()), nullable=False)
user_id = Column(Integer, ForeignKey('users.id'), nullable=False) user_id = Column(Integer, ForeignKey('users.id'), nullable=False)

View File

@ -273,10 +273,11 @@ class CreateUser(Command):
Option('-u', '--username', dest='username', required=True), Option('-u', '--username', dest='username', required=True),
Option('-e', '--email', dest='email', required=True), Option('-e', '--email', dest='email', required=True),
Option('-a', '--active', dest='active', default=True), Option('-a', '--active', dest='active', default=True),
Option('-r', '--roles', dest='roles', action='append', default=[]) Option('-r', '--roles', dest='roles', action='append', default=[]),
Option('-p', '--password', dest='password', default=None)
) )
def run(self, username, email, active, roles): def run(self, username, email, active, roles, password):
role_objs = [] role_objs = []
for r in roles: for r in roles:
role_obj = role_service.get_by_name(r) role_obj = role_service.get_by_name(r)
@ -286,14 +287,16 @@ class CreateUser(Command):
sys.stderr.write("[!] Cannot find role {0}\n".format(r)) sys.stderr.write("[!] Cannot find role {0}\n".format(r))
sys.exit(1) sys.exit(1)
password1 = prompt_pass("Password") if not password:
password2 = prompt_pass("Confirm Password") password1 = prompt_pass("Password")
password2 = prompt_pass("Confirm Password")
password = password1
if password1 != password2: if password1 != password2:
sys.stderr.write("[!] Passwords do not match!\n") sys.stderr.write("[!] Passwords do not match!\n")
sys.exit(1) sys.exit(1)
user_service.create(username, password1, email, active, None, role_objs) user_service.create(username, password, email, active, None, role_objs)
sys.stdout.write("[+] Created new user: {0}\n".format(username)) sys.stdout.write("[+] Created new user: {0}\n".format(username))

View File

@ -0,0 +1,22 @@
""" Add delete_cert to log_type enum
Revision ID: 9f79024fe67b
Revises: ee827d1e1974
Create Date: 2019-01-03 15:36:59.181911
"""
# revision identifiers, used by Alembic.
revision = '9f79024fe67b'
down_revision = 'ee827d1e1974'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.sync_enum_values('public', 'log_type', ['create_cert', 'key_view', 'revoke_cert', 'update_cert'], ['create_cert', 'delete_cert', 'key_view', 'revoke_cert', 'update_cert'])
def downgrade():
op.sync_enum_values('public', 'log_type', ['create_cert', 'delete_cert', 'key_view', 'revoke_cert', 'update_cert'], ['create_cert', 'key_view', 'revoke_cert', 'update_cert'])

View File

@ -5,7 +5,7 @@ import dns.exception
import dns.name import dns.name
import dns.query import dns.query
import dns.resolver import dns.resolver
from dyn.tm.errors import DynectCreateError from dyn.tm.errors import DynectCreateError, DynectGetError
from dyn.tm.session import DynectSession from dyn.tm.session import DynectSession
from dyn.tm.zones import Node, Zone, get_all_zones from dyn.tm.zones import Node, Zone, get_all_zones
from flask import current_app from flask import current_app
@ -119,7 +119,11 @@ def delete_txt_record(change_id, account_number, domain, token):
zone = Zone(zone_name) zone = Zone(zone_name)
node = Node(zone_name, fqdn) node = Node(zone_name, fqdn)
all_txt_records = node.get_all_records_by_type('TXT') try:
all_txt_records = node.get_all_records_by_type('TXT')
except DynectGetError:
# No Text Records remain or host is not in the zone anymore because all records have been deleted.
return
for txt_record in all_txt_records: for txt_record in all_txt_records:
if txt_record.txtdata == ("{}".format(token)): if txt_record.txtdata == ("{}".format(token)):
current_app.logger.debug("Deleting TXT record name: {0}".format(fqdn)) current_app.logger.debug("Deleting TXT record name: {0}".format(fqdn))

View File

@ -10,6 +10,9 @@
import json import json
import requests import requests
import base64
import hmac
import hashlib
from flask import current_app from flask import current_app
@ -48,6 +51,21 @@ class CfsslIssuerPlugin(IssuerPlugin):
data = {'certificate_request': csr} data = {'certificate_request': csr}
data = json.dumps(data) data = json.dumps(data)
try:
hex_key = current_app.config.get('CFSSL_KEY')
key = bytes.fromhex(hex_key)
except (ValueError, NameError):
# unable to find CFSSL_KEY in config, continue using normal sign method
pass
else:
data = data.encode()
token = base64.b64encode(hmac.new(key, data, digestmod=hashlib.sha256).digest())
data = base64.b64encode(data)
data = json.dumps({'token': token.decode('utf-8'), 'request': data.decode('utf-8')})
url = "{0}{1}".format(current_app.config.get('CFSSL_URL'), '/api/v1/cfssl/authsign')
response = self.session.post(url, data=data.encode(encoding='utf_8', errors='strict')) response = self.session.post(url, data=data.encode(encoding='utf_8', errors='strict'))
if response.status_code > 399: if response.status_code > 399:
metrics.send('cfssl_create_certificate_failure', 'counter', 1) metrics.send('cfssl_create_certificate_failure', 'counter', 1)

View File

@ -83,6 +83,8 @@
</div> </div>
<!-- Certificate fields --> <!-- Certificate fields -->
<div class="list-group-item"> <div class="list-group-item">
<dt>Distinguished Name</dt>
<dd>{{ certificate.distinguishedName }}</dd>
<dt>Certificate Authority</dt> <dt>Certificate Authority</dt>
<dd>{{ certificate.authority ? certificate.authority.name : "Imported" }} <span class="text-muted">({{ certificate.issuer }})</span></dd> <dd>{{ certificate.authority ? certificate.authority.name : "Imported" }} <span class="text-muted">({{ certificate.issuer }})</span></dd>
<dt>Serial</dt> <dt>Serial</dt>

View File

@ -15,7 +15,8 @@ from lemur.tests.vectors import SAN_CERT_KEY, INTERMEDIATE_KEY
from .factories import ApiKeyFactory, AuthorityFactory, NotificationFactory, DestinationFactory, \ from .factories import ApiKeyFactory, AuthorityFactory, NotificationFactory, DestinationFactory, \
CertificateFactory, UserFactory, RoleFactory, SourceFactory, EndpointFactory, \ CertificateFactory, UserFactory, RoleFactory, SourceFactory, EndpointFactory, \
RotationPolicyFactory, PendingCertificateFactory, AsyncAuthorityFactory, CryptoAuthorityFactory RotationPolicyFactory, PendingCertificateFactory, AsyncAuthorityFactory, InvalidCertificateFactory, \
CryptoAuthorityFactory
def pytest_runtest_setup(item): def pytest_runtest_setup(item):
@ -168,6 +169,15 @@ def pending_certificate(session):
return p return p
@pytest.fixture
def invalid_certificate(session):
u = UserFactory()
a = AsyncAuthorityFactory()
i = InvalidCertificateFactory(user=u, authority=a)
session.commit()
return i
@pytest.fixture @pytest.fixture
def admin_user(session): def admin_user(session):
u = UserFactory() u = UserFactory()

View File

@ -20,7 +20,7 @@ from lemur.policies.models import RotationPolicy
from lemur.api_keys.models import ApiKey from lemur.api_keys.models import ApiKey
from .vectors import SAN_CERT_STR, SAN_CERT_KEY, CSR_STR, INTERMEDIATE_CERT_STR, ROOTCA_CERT_STR, INTERMEDIATE_KEY, \ from .vectors import SAN_CERT_STR, SAN_CERT_KEY, CSR_STR, INTERMEDIATE_CERT_STR, ROOTCA_CERT_STR, INTERMEDIATE_KEY, \
WILDCARD_CERT_KEY WILDCARD_CERT_KEY, INVALID_CERT_STR
class BaseFactory(SQLAlchemyModelFactory): class BaseFactory(SQLAlchemyModelFactory):
@ -137,6 +137,11 @@ class CACertificateFactory(CertificateFactory):
private_key = INTERMEDIATE_KEY private_key = INTERMEDIATE_KEY
class InvalidCertificateFactory(CertificateFactory):
body = INVALID_CERT_STR
private_key = ''
class AuthorityFactory(BaseFactory): class AuthorityFactory(BaseFactory):
"""Authority factory.""" """Authority factory."""
name = Sequence(lambda n: 'authority{0}'.format(n)) name = Sequence(lambda n: 'authority{0}'.format(n))

View File

@ -619,6 +619,12 @@ def test_certificate_get_body(client):
response_body = client.get(api.url_for(Certificates, certificate_id=1), headers=VALID_USER_HEADER_TOKEN).json response_body = client.get(api.url_for(Certificates, certificate_id=1), headers=VALID_USER_HEADER_TOKEN).json
assert response_body['serial'] == '211983098819107449768450703123665283596' assert response_body['serial'] == '211983098819107449768450703123665283596'
assert response_body['serialHex'] == '9F7A75B39DAE4C3F9524C68B06DA6A0C' assert response_body['serialHex'] == '9F7A75B39DAE4C3F9524C68B06DA6A0C'
assert response_body['distinguishedName'] == ('CN=LemurTrust Unittests Class 1 CA 2018,'
'O=LemurTrust Enterprises Ltd,'
'OU=Unittesting Operations Center,'
'C=EE,'
'ST=N/A,'
'L=Earth')
@pytest.mark.parametrize("token,status", [ @pytest.mark.parametrize("token,status", [
@ -647,15 +653,26 @@ def test_certificate_put_with_data(client, certificate, issuer_plugin):
@pytest.mark.parametrize("token,status", [ @pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 405), (VALID_USER_HEADER_TOKEN, 403),
(VALID_ADMIN_HEADER_TOKEN, 405), (VALID_ADMIN_HEADER_TOKEN, 412),
(VALID_ADMIN_API_TOKEN, 405), (VALID_ADMIN_API_TOKEN, 412),
('', 405) ('', 401)
]) ])
def test_certificate_delete(client, token, status): def test_certificate_delete(client, token, status):
assert client.delete(api.url_for(Certificates, certificate_id=1), headers=token).status_code == status assert client.delete(api.url_for(Certificates, certificate_id=1), headers=token).status_code == status
@pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 403),
(VALID_ADMIN_HEADER_TOKEN, 204),
(VALID_ADMIN_API_TOKEN, 204),
('', 401)
])
def test_invalid_certificate_delete(client, invalid_certificate, token, status):
assert client.delete(
api.url_for(Certificates, certificate_id=invalid_certificate.id), headers=token).status_code == status
@pytest.mark.parametrize("token,status", [ @pytest.mark.parametrize("token,status", [
(VALID_USER_HEADER_TOKEN, 405), (VALID_USER_HEADER_TOKEN, 405),
(VALID_ADMIN_HEADER_TOKEN, 405), (VALID_ADMIN_HEADER_TOKEN, 405),

View File

@ -4,14 +4,14 @@
# #
# pip-compile --no-index --output-file requirements-dev.txt requirements-dev.in # pip-compile --no-index --output-file requirements-dev.txt requirements-dev.in
# #
aspy.yaml==1.1.1 # via pre-commit aspy.yaml==1.1.2 # via pre-commit
bleach==3.1.0 # via readme-renderer bleach==3.1.0 # via readme-renderer
certifi==2018.11.29 # via requests certifi==2018.11.29 # via requests
cfgv==1.4.0 # via pre-commit cfgv==1.4.0 # via pre-commit
chardet==3.0.4 # via requests chardet==3.0.4 # via requests
docutils==0.14 # via readme-renderer docutils==0.14 # via readme-renderer
flake8==3.5.0 flake8==3.5.0
identify==1.1.8 # via pre-commit identify==1.2.1 # via pre-commit
idna==2.8 # via requests idna==2.8 # via requests
importlib-metadata==0.8 # via pre-commit importlib-metadata==0.8 # via pre-commit
importlib-resources==1.0.2 # via pre-commit importlib-resources==1.0.2 # via pre-commit
@ -19,19 +19,19 @@ invoke==1.2.0
mccabe==0.6.1 # via flake8 mccabe==0.6.1 # via flake8
nodeenv==1.3.3 nodeenv==1.3.3
pkginfo==1.5.0.1 # via twine pkginfo==1.5.0.1 # via twine
pre-commit==1.14.0 pre-commit==1.14.2
pycodestyle==2.3.1 # via flake8 pycodestyle==2.3.1 # via flake8
pyflakes==1.6.0 # via flake8 pyflakes==1.6.0 # via flake8
pygments==2.3.1 # via readme-renderer pygments==2.3.1 # via readme-renderer
pyyaml==3.13 # via aspy.yaml, pre-commit pyyaml==3.13 # via aspy.yaml, pre-commit
readme-renderer==24.0 # via twine readme-renderer==24.0 # via twine
requests-toolbelt==0.8.0 # via twine requests-toolbelt==0.9.0 # via twine
requests==2.21.0 # via requests-toolbelt, twine requests==2.21.0 # via requests-toolbelt, twine
six==1.12.0 # via bleach, cfgv, pre-commit, readme-renderer six==1.12.0 # via bleach, cfgv, pre-commit, readme-renderer
toml==0.10.0 # via pre-commit toml==0.10.0 # via pre-commit
tqdm==4.29.0 # via twine tqdm==4.30.0 # via twine
twine==1.12.1 twine==1.12.1
urllib3==1.24.1 # via requests urllib3==1.24.1 # via requests
virtualenv==16.2.0 # via pre-commit virtualenv==16.3.0 # via pre-commit
webencodings==0.5.1 # via bleach webencodings==0.5.1 # via bleach
zipp==0.3.3 # via importlib-metadata zipp==0.3.3 # via importlib-metadata

View File

@ -4,28 +4,28 @@
# #
# pip-compile --no-index --output-file requirements-docs.txt requirements-docs.in # pip-compile --no-index --output-file requirements-docs.txt requirements-docs.in
# #
acme==0.30.0 acme==0.30.2
alabaster==0.7.12 # via sphinx alabaster==0.7.12 # via sphinx
alembic-autogenerate-enums==0.0.2 alembic-autogenerate-enums==0.0.2
alembic==1.0.5 alembic==1.0.7
amqp==2.3.2 amqp==2.4.0
aniso8601==4.1.0 aniso8601==4.1.0
arrow==0.13.0 arrow==0.13.0
asn1crypto==0.24.0 asn1crypto==0.24.0
asyncpool==1.0 asyncpool==1.0
babel==2.6.0 # via sphinx babel==2.6.0 # via sphinx
bcrypt==3.1.5 bcrypt==3.1.6
billiard==3.5.0.5 billiard==3.5.0.5
blinker==1.4 blinker==1.4
boto3==1.9.76 boto3==1.9.86
botocore==1.12.76 botocore==1.12.86
celery[redis]==4.2.1 celery[redis]==4.2.1
certifi==2018.11.29 certifi==2018.11.29
cffi==1.11.5 cffi==1.11.5
chardet==3.0.4 chardet==3.0.4
click==7.0 click==7.0
cloudflare==2.1.0 cloudflare==2.1.0
cryptography==2.4.2 cryptography==2.5
dnspython3==1.15.0 dnspython3==1.15.0
dnspython==1.15.0 dnspython==1.15.0
docutils==0.14 docutils==0.14
@ -54,22 +54,22 @@ lockfile==0.12.2
mako==1.0.7 mako==1.0.7
markupsafe==1.1.0 markupsafe==1.1.0
marshmallow-sqlalchemy==0.15.0 marshmallow-sqlalchemy==0.15.0
marshmallow==2.17.0 marshmallow==2.18.0
mock==2.0.0 mock==2.0.0
ndg-httpsclient==0.5.1 ndg-httpsclient==0.5.1
packaging==18.0 # via sphinx packaging==19.0 # via sphinx
paramiko==2.4.2 paramiko==2.4.2
pbr==5.1.1 pbr==5.1.1
pem==18.2.0 pem==18.2.0
psycopg2==2.7.6.1 psycopg2==2.7.7
pyasn1-modules==0.2.3 pyasn1-modules==0.2.4
pyasn1==0.4.5 pyasn1==0.4.5
pycparser==2.19 pycparser==2.19
pygments==2.3.1 # via sphinx pygments==2.3.1 # via sphinx
pyjwt==1.7.1 pyjwt==1.7.1
pynacl==1.3.0 pynacl==1.3.0
pyopenssl==18.0.0 pyopenssl==19.0.0
pyparsing==2.3.0 # via packaging pyparsing==2.3.1 # via packaging
pyrfc3339==1.1 pyrfc3339==1.1
python-dateutil==2.7.5 python-dateutil==2.7.5
python-editor==1.0.3 python-editor==1.0.3
@ -77,7 +77,7 @@ pytz==2018.9
pyyaml==3.13 pyyaml==3.13
raven[flask]==6.10.0 raven[flask]==6.10.0
redis==2.10.6 redis==2.10.6
requests-toolbelt==0.8.0 requests-toolbelt==0.9.0
requests[security]==2.21.0 requests[security]==2.21.0
retrying==1.3.3 retrying==1.3.3
s3transfer==0.1.13 s3transfer==0.1.13
@ -87,9 +87,9 @@ sphinx-rtd-theme==0.4.2
sphinx==1.8.3 sphinx==1.8.3
sphinxcontrib-httpdomain==1.7.0 sphinxcontrib-httpdomain==1.7.0
sphinxcontrib-websupport==1.1.0 # via sphinx sphinxcontrib-websupport==1.1.0 # via sphinx
sqlalchemy-utils==0.33.10 sqlalchemy-utils==0.33.11
sqlalchemy==1.2.15 sqlalchemy==1.2.17
tabulate==0.8.2 tabulate==0.8.3
urllib3==1.24.1 urllib3==1.24.1
vine==1.2.0 vine==1.2.0
werkzeug==0.14.1 werkzeug==0.14.1

View File

@ -8,30 +8,30 @@ asn1crypto==0.24.0 # via cryptography
atomicwrites==1.2.1 # via pytest atomicwrites==1.2.1 # via pytest
attrs==18.2.0 # via pytest attrs==18.2.0 # via pytest
aws-xray-sdk==0.95 # via moto aws-xray-sdk==0.95 # via moto
boto3==1.9.76 # via moto boto3==1.9.86 # via moto
boto==2.49.0 # via moto boto==2.49.0 # via moto
botocore==1.12.76 # via boto3, moto, s3transfer botocore==1.12.86 # via boto3, moto, s3transfer
certifi==2018.11.29 # via requests certifi==2018.11.29 # 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
click==7.0 # via flask click==7.0 # via flask
coverage==4.5.2 coverage==4.5.2
cryptography==2.4.2 # via moto cryptography==2.5 # via moto
docker-pycreds==0.4.0 # via docker docker-pycreds==0.4.0 # via docker
docker==3.6.0 # via moto docker==3.7.0 # via moto
docutils==0.14 # via botocore docutils==0.14 # via botocore
ecdsa==0.13 # via python-jose ecdsa==0.13 # via python-jose
factory-boy==2.11.1 factory-boy==2.11.1
faker==1.0.1 faker==1.0.2
flask==1.0.2 # via pytest-flask flask==1.0.2 # via pytest-flask
freezegun==0.3.11 freezegun==0.3.11
future==0.17.1 # via python-jose future==0.17.1 # via python-jose
idna==2.8 # via cryptography, requests idna==2.8 # via requests
itsdangerous==1.1.0 # via flask itsdangerous==1.1.0 # via flask
jinja2==2.10 # via flask, moto jinja2==2.10 # via flask, moto
jmespath==0.9.3 # via boto3, botocore jmespath==0.9.3 # via boto3, botocore
jsondiff==1.1.1 # via moto jsondiff==1.1.1 # via moto
jsonpickle==1.0 # via aws-xray-sdk jsonpickle==1.1 # via aws-xray-sdk
markupsafe==1.1.0 # via jinja2 markupsafe==1.1.0 # via jinja2
mock==2.0.0 # via moto mock==2.0.0 # via moto
more-itertools==5.0.0 # via pytest more-itertools==5.0.0 # via pytest
@ -42,11 +42,11 @@ pluggy==0.8.1 # via pytest
py==1.7.0 # via pytest py==1.7.0 # via pytest
pyaml==18.11.0 # via moto pyaml==18.11.0 # via moto
pycparser==2.19 # via cffi pycparser==2.19 # via cffi
pycryptodome==3.7.2 # via python-jose pycryptodome==3.7.3 # via python-jose
pyflakes==2.0.0 pyflakes==2.1.0
pytest-flask==0.14.0 pytest-flask==0.14.0
pytest-mock==1.10.0 pytest-mock==1.10.0
pytest==4.1.0 pytest==4.1.1
python-dateutil==2.7.5 # via botocore, faker, freezegun, moto python-dateutil==2.7.5 # via botocore, faker, freezegun, moto
python-jose==2.0.2 # via moto python-jose==2.0.2 # via moto
pytz==2018.9 # via moto pytz==2018.9 # via moto
@ -60,5 +60,5 @@ text-unidecode==1.2 # via faker
urllib3==1.24.1 # via botocore, requests urllib3==1.24.1 # via botocore, requests
websocket-client==0.54.0 # via docker websocket-client==0.54.0 # via docker
werkzeug==0.14.1 # via flask, moto, pytest-flask werkzeug==0.14.1 # via flask, moto, pytest-flask
wrapt==1.11.0 # via aws-xray-sdk wrapt==1.11.1 # via aws-xray-sdk
xmltodict==0.11.0 # via moto xmltodict==0.11.0 # via moto

View File

@ -4,26 +4,26 @@
# #
# pip-compile --no-index --output-file requirements.txt requirements.in # pip-compile --no-index --output-file requirements.txt requirements.in
# #
acme==0.30.0 acme==0.30.2
alembic-autogenerate-enums==0.0.2 alembic-autogenerate-enums==0.0.2
alembic==1.0.5 # via flask-migrate alembic==1.0.7 # via flask-migrate
amqp==2.3.2 # via kombu amqp==2.4.0 # via kombu
aniso8601==4.1.0 # via flask-restful aniso8601==4.1.0 # via flask-restful
arrow==0.13.0 arrow==0.13.0
asn1crypto==0.24.0 # via cryptography asn1crypto==0.24.0 # via cryptography
asyncpool==1.0 asyncpool==1.0
bcrypt==3.1.5 # via flask-bcrypt, paramiko bcrypt==3.1.6 # via flask-bcrypt, paramiko
billiard==3.5.0.5 # via celery billiard==3.5.0.5 # via celery
blinker==1.4 # via flask-mail, flask-principal, raven blinker==1.4 # via flask-mail, flask-principal, raven
boto3==1.9.76 boto3==1.9.86
botocore==1.12.76 botocore==1.12.86
celery[redis]==4.2.1 celery[redis]==4.2.1
certifi==2018.11.29 certifi==2018.11.29
cffi==1.11.5 # via bcrypt, cryptography, pynacl cffi==1.11.5 # via bcrypt, cryptography, pynacl
chardet==3.0.4 # via requests chardet==3.0.4 # via requests
click==7.0 # via flask click==7.0 # via flask
cloudflare==2.1.0 cloudflare==2.1.0
cryptography==2.4.2 cryptography==2.5
dnspython3==1.15.0 dnspython3==1.15.0
dnspython==1.15.0 # via dnspython3 dnspython==1.15.0 # via dnspython3
docutils==0.14 # via botocore docutils==0.14 # via botocore
@ -39,7 +39,7 @@ flask-sqlalchemy==2.3.2
flask==1.0.2 flask==1.0.2
future==0.17.1 future==0.17.1
gunicorn==19.9.0 gunicorn==19.9.0
idna==2.8 # via cryptography, requests idna==2.8 # via requests
inflection==0.3.1 inflection==0.3.1
itsdangerous==1.1.0 # via flask itsdangerous==1.1.0 # via flask
jinja2==2.10 jinja2==2.10
@ -51,19 +51,19 @@ lockfile==0.12.2
mako==1.0.7 # via alembic mako==1.0.7 # via alembic
markupsafe==1.1.0 # via jinja2, mako markupsafe==1.1.0 # via jinja2, mako
marshmallow-sqlalchemy==0.15.0 marshmallow-sqlalchemy==0.15.0
marshmallow==2.17.0 marshmallow==2.18.0
mock==2.0.0 # via acme mock==2.0.0 # via acme
ndg-httpsclient==0.5.1 ndg-httpsclient==0.5.1
paramiko==2.4.2 paramiko==2.4.2
pbr==5.1.1 # via mock pbr==5.1.1 # via mock
pem==18.2.0 pem==18.2.0
psycopg2==2.7.6.1 psycopg2==2.7.7
pyasn1-modules==0.2.3 # via python-ldap pyasn1-modules==0.2.4 # via python-ldap
pyasn1==0.4.5 # via ndg-httpsclient, paramiko, pyasn1-modules, python-ldap pyasn1==0.4.5 # via ndg-httpsclient, paramiko, pyasn1-modules, python-ldap
pycparser==2.19 # via cffi pycparser==2.19 # via cffi
pyjwt==1.7.1 pyjwt==1.7.1
pynacl==1.3.0 # via paramiko pynacl==1.3.0 # via paramiko
pyopenssl==18.0.0 pyopenssl==19.0.0
pyrfc3339==1.1 # via acme pyrfc3339==1.1 # via acme
python-dateutil==2.7.5 # via alembic, arrow, botocore python-dateutil==2.7.5 # via alembic, arrow, botocore
python-editor==1.0.3 # via alembic python-editor==1.0.3 # via alembic
@ -72,14 +72,14 @@ pytz==2018.9 # via acme, celery, flask-restful, pyrfc3339
pyyaml==3.13 # via cloudflare pyyaml==3.13 # via cloudflare
raven[flask]==6.10.0 raven[flask]==6.10.0
redis==2.10.6 redis==2.10.6
requests-toolbelt==0.8.0 # via acme requests-toolbelt==0.9.0 # via acme
requests[security]==2.21.0 requests[security]==2.21.0
retrying==1.3.3 retrying==1.3.3
s3transfer==0.1.13 # via boto3 s3transfer==0.1.13 # via boto3
six==1.12.0 six==1.12.0
sqlalchemy-utils==0.33.10 sqlalchemy-utils==0.33.11
sqlalchemy==1.2.15 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils sqlalchemy==1.2.17 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
tabulate==0.8.2 tabulate==0.8.3
urllib3==1.24.1 # via botocore, requests urllib3==1.24.1 # via botocore, requests
vine==1.2.0 # via amqp vine==1.2.0 # via amqp
werkzeug==0.14.1 # via flask werkzeug==0.14.1 # via flask