From 5108578c83846fdcf25ef0accc61d509accca968 Mon Sep 17 00:00:00 2001 From: Aaron Suarez Date: Sat, 13 Jun 2020 21:31:27 -0500 Subject: [PATCH] Add JWT in auth header as an authN option --- .dev/dev-jwt-key | 51 ++++++++++ .dev/dev-jwt-key.pub | 14 +++ Makefile | 5 + app/api/auth.py | 48 ++++++++- poetry.lock | 180 ++++++++++++++++++++++++++------- pyproject.toml | 2 + tests/unit/test_auth_jwt.py | 193 ++++++++++++++++++++++++++++++++++++ 7 files changed, 457 insertions(+), 36 deletions(-) create mode 100644 .dev/dev-jwt-key create mode 100644 .dev/dev-jwt-key.pub create mode 100644 tests/unit/test_auth_jwt.py diff --git a/.dev/dev-jwt-key b/.dev/dev-jwt-key new file mode 100644 index 00000000..5174e301 --- /dev/null +++ b/.dev/dev-jwt-key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/.dev/dev-jwt-key.pub b/.dev/dev-jwt-key.pub new file mode 100644 index 00000000..e0b27c97 --- /dev/null +++ b/.dev/dev-jwt-key.pub @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8DAocd7+LjrW0NucSPC +cBnO7Inu7soRVCmaOjt1HcQHdCV48WzPKAWxz/FQyVqHbUf+UZkw1ryi7CASf9n3 +5Aia/JrYnW5hG1ti455GhgEUcItvB7dpscK9N3DeeyNv4tk3FokdhiG/92LvujhM +xFPjO60jex0H2yieR7Osx/AwCEHNh6opct+EYNkoD1G2cXfCOCdZxpzBttU6jsvO +fX3lDykWrZHSC0yfYpZU+9M4qtnjZbpKK/Vpw4Ic5qTpYm9rBkF3rDbQeY2O5nw+ +3S996ckMR5jKXb4aRxnX2LawQ2MmKmmYTRKMc7KG/vXPOH2qDcr/+caP5ZP+epTo +5Rz4t88tuhTlj+KRefs1LM3dbq5rLnIbe7zmTuyzTSlA+qTMkmt42dZ4mAH0huEH +Nd931owRDlLvl/Py3by4D+RZo+er8D+wrkUQk4O+s6SYNqfdYphSIXgIbTeKny48 +E3Ph87fXQz4vgbJPFk1dGi9xm1dsNLrkMoapvZwdN7bSJ5zqjro71M4HnFRUAGdY +nM353W5uKwEHSmR6TcOUastQ7qJDY6DYNTKCte/XXQmgcResBtWRl2LVz7KepXHJ +rXjXcLv5OaJMRe9PklrWM3SOGpKfQ2CP3XNvDvu2x1kb3sikzjVdtl4glcEI4Ow6 +8Ani73dDyAIVcXPu2CtGyksCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/Makefile b/Makefile index ec2b9075..9c300447 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,11 @@ test: build test-coverage: ${DOCKER_COMPOSE} run ${RESOURCES_CONTAINER} py.test --cov-report html --cov=app/ tests/ +# Usage: make test-single tests/unit/test_api_key.py::test_get_api_key +.PHONY: test-single +test-single: build + ${DOCKER_COMPOSE} run ${RESOURCES_CONTAINER} /bin/bash -c "py.test --cov=app/ $(shell echo ${ARGS})" + .PHONY: lint lint: ${DOCKER_COMPOSE} run ${RESOURCES_CONTAINER} flake8 . --exclude migrations --statistics --count diff --git a/app/api/auth.py b/app/api/auth.py index 175d997f..ca971587 100644 --- a/app/api/auth.py +++ b/app/api/auth.py @@ -1,11 +1,17 @@ import uuid +import os from enum import Enum import requests +from app import db from app.models import Key from app.utils import setup_logger, standardize_response from flask import g, request +from jwt import decode, InvalidSignatureError, ExpiredSignatureError + +PUBLIC_KEY = os.environ.get('JWT_PUBLIC_KEY', open(".dev/dev-jwt-key.pub").read()) + auth_logger = setup_logger('auth_logger') create_logger = setup_logger('create_auth_logger') update_logger = setup_logger('update_auth_logger') @@ -85,10 +91,50 @@ def rotate_key(key, session): return None +def jwt_to_key(): + auth_header = request.headers.get("authorization") + if not auth_header: + return None + auth_parts = auth_header.split(" ") + if len(auth_parts) != 2: + return None + _, token = auth_parts + try: + decoded_token = decode( + token, PUBLIC_KEY, algorithms="RS256" + ) + request.decoded_token = decoded_token + except InvalidSignatureError: + return None + except ExpiredSignatureError: + return None + if 'exp' not in decoded_token: + return None + return get_api_key_from_authenticated_email(decoded_token['email']) + + +# NOTE: this function assumes the email has already been authenticated +def get_api_key_from_authenticated_email(email): + apikey = Key.query.filter_by(email=email).first() + + if apikey and apikey.blacklisted: + return None + + if not apikey: + apikey = create_new_apikey(email, db.session) + if not apikey: + raise Exception + return apikey + + def authenticate(func): def wrapper(*args, **kwargs): apikey = request.headers.get('x-apikey') - key = Key.query.filter_by(apikey=apikey, blacklisted=False).first() + try: + filters = {'apikey': apikey, 'blacklisted': False} + key = Key.query.filter_by(**filters).first() if apikey else jwt_to_key() + except Exception: + return standardize_response(status_code=500) if not key: return standardize_response(status_code=401) diff --git a/poetry.lock b/poetry.lock index 0a64d1ef..768c71d7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -66,7 +66,18 @@ description = "Python package for providing Mozilla's CA Bundle." name = "certifi" optional = false python-versions = "*" -version = "2020.4.5.1" +version = "2020.4.5.2" + +[[package]] +category = "main" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" +optional = false +python-versions = "*" +version = "1.14.0" + +[package.dependencies] +pycparser = "*" [[package]] category = "main" @@ -104,6 +115,25 @@ version = "5.1" [package.extras] toml = ["toml"] +[[package]] +category = "main" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +name = "cryptography" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +version = "2.9.2" + +[package.dependencies] +cffi = ">=1.8,<1.11.3 || >1.11.3" +six = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"] +docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +idna = ["idna (>=2.1)"] +pep8test = ["flake8", "flake8-import-order", "pep8-naming"] +test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] + [[package]] category = "main" description = "the modular source code checker: pep8 pyflakes and co" @@ -184,7 +214,7 @@ description = "Adds SQLAlchemy support to your Flask application." name = "flask-sqlalchemy" optional = false python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*" -version = "2.4.1" +version = "2.4.3" [package.dependencies] Flask = ">=0.10" @@ -207,7 +237,7 @@ description = "Python Git Library" name = "gitpython" optional = false python-versions = ">=3.4" -version = "3.1.2" +version = "3.1.3" [package.dependencies] gitdb = ">=4.0.1,<5" @@ -227,14 +257,14 @@ marker = "python_version < \"3.8\"" name = "importlib-metadata" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.6.0" +version = "1.6.1" [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["sphinx", "rst.linker"] -testing = ["packaging", "importlib-resources"] +testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] category = "main" @@ -275,7 +305,7 @@ description = "A super-fast templating language that borrows the best ideas fro name = "mako" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.2" +version = "1.1.3" [package.dependencies] MarkupSafe = ">=0.9.2" @@ -306,7 +336,7 @@ description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" optional = false python-versions = ">=3.5" -version = "8.3.0" +version = "8.4.0" [[package]] category = "main" @@ -369,7 +399,7 @@ description = "library with cross-python path, ini-parsing, io, code, log facili name = "py" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.8.1" +version = "1.8.2" [[package]] category = "main" @@ -390,6 +420,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.6.0" +[[package]] +category = "main" +description = "C parser in Python" +name = "pycparser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.20" + [[package]] category = "main" description = "passive checker of Python programs" @@ -398,6 +436,19 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.2.0" +[[package]] +category = "main" +description = "JSON Web Token implementation in Python" +name = "pyjwt" +optional = false +python-versions = "*" +version = "1.7.1" + +[package.extras] +crypto = ["cryptography (>=1.4)"] +flake8 = ["flake8", "flake8-import-order", "pep8-naming"] +test = ["pytest (>=4.0.1,<5.0.0)", "pytest-cov (>=2.6.0,<3.0.0)", "pytest-runner (>=4.2,<5.0.0)"] + [[package]] category = "main" description = "Python parsing module" @@ -512,7 +563,7 @@ description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.14.0" +version = "1.15.0" [[package]] category = "main" @@ -574,12 +625,11 @@ category = "main" description = "Manage dynamic plugins for Python applications" name = "stevedore" optional = false -python-versions = "*" -version = "1.32.0" +python-versions = ">=3.6" +version = "2.0.0" [package.dependencies] pbr = ">=2.0.0,<2.1.0 || >2.1.0" -six = ">=1.10.0" [[package]] category = "main" @@ -604,11 +654,11 @@ version = "2.0.19.1" [[package]] category = "main" -description = "Measures number of Terminal column cells of wide-character codes" +description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" optional = false python-versions = "*" -version = "0.1.9" +version = "0.2.4" [[package]] category = "main" @@ -636,7 +686,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "37ff64995c1d2e6a5dadd584155b787536a6e05028ee7e67041c39ce74ce7f17" +content-hash = "1a1901ff9a7ced1fdf090675aec42b090b87794b3fe770f72a776cd44f5c704b" python-versions = "^3.7" [metadata.files] @@ -660,8 +710,38 @@ bandit = [ {file = "bandit-1.5.1.tar.gz", hash = "sha256:9413facfe9de1e1bd291d525c784e1beb1a55c9916b51dae12979af63a69ba4c"}, ] certifi = [ - {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, - {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, + {file = "certifi-2020.4.5.2-py2.py3-none-any.whl", hash = "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc"}, + {file = "certifi-2020.4.5.2.tar.gz", hash = "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"}, +] +cffi = [ + {file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"}, + {file = "cffi-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30"}, + {file = "cffi-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"}, + {file = "cffi-1.14.0-cp27-cp27m-win32.whl", hash = "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78"}, + {file = "cffi-1.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793"}, + {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e"}, + {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a"}, + {file = "cffi-1.14.0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff"}, + {file = "cffi-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f"}, + {file = "cffi-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa"}, + {file = "cffi-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5"}, + {file = "cffi-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4"}, + {file = "cffi-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d"}, + {file = "cffi-1.14.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc"}, + {file = "cffi-1.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac"}, + {file = "cffi-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f"}, + {file = "cffi-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b"}, + {file = "cffi-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3"}, + {file = "cffi-1.14.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66"}, + {file = "cffi-1.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0"}, + {file = "cffi-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f"}, + {file = "cffi-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26"}, + {file = "cffi-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd"}, + {file = "cffi-1.14.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55"}, + {file = "cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2"}, + {file = "cffi-1.14.0-cp38-cp38-win32.whl", hash = "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8"}, + {file = "cffi-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b"}, + {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"}, ] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, @@ -708,6 +788,27 @@ coverage = [ {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, ] +cryptography = [ + {file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"}, + {file = "cryptography-2.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b"}, + {file = "cryptography-2.9.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365"}, + {file = "cryptography-2.9.2-cp27-cp27m-win32.whl", hash = "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"}, + {file = "cryptography-2.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55"}, + {file = "cryptography-2.9.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270"}, + {file = "cryptography-2.9.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf"}, + {file = "cryptography-2.9.2-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d"}, + {file = "cryptography-2.9.2-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785"}, + {file = "cryptography-2.9.2-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b"}, + {file = "cryptography-2.9.2-cp35-cp35m-win32.whl", hash = "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae"}, + {file = "cryptography-2.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b"}, + {file = "cryptography-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6"}, + {file = "cryptography-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3"}, + {file = "cryptography-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b"}, + {file = "cryptography-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e"}, + {file = "cryptography-2.9.2-cp38-cp38-win32.whl", hash = "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0"}, + {file = "cryptography-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5"}, + {file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"}, +] flake8 = [ {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, @@ -730,24 +831,24 @@ flask-migrate = [ {file = "Flask_Migrate-2.5.3-py2.py3-none-any.whl", hash = "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732"}, ] flask-sqlalchemy = [ - {file = "Flask-SQLAlchemy-2.4.1.tar.gz", hash = "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"}, - {file = "Flask_SQLAlchemy-2.4.1-py2.py3-none-any.whl", hash = "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327"}, + {file = "Flask-SQLAlchemy-2.4.3.tar.gz", hash = "sha256:0b656fbf87c5f24109d859bafa791d29751fabbda2302b606881ae5485b557a5"}, + {file = "Flask_SQLAlchemy-2.4.3-py2.py3-none-any.whl", hash = "sha256:fcfe6df52cd2ed8a63008ca36b86a51fa7a4b70cef1c39e5625f722fca32308e"}, ] gitdb = [ {file = "gitdb-4.0.5-py3-none-any.whl", hash = "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac"}, {file = "gitdb-4.0.5.tar.gz", hash = "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"}, ] gitpython = [ - {file = "GitPython-3.1.2-py3-none-any.whl", hash = "sha256:da3b2cf819974789da34f95ac218ef99f515a928685db141327c09b73dd69c09"}, - {file = "GitPython-3.1.2.tar.gz", hash = "sha256:864a47472548f3ba716ca202e034c1900f197c0fb3a08f641c20c3cafd15ed94"}, + {file = "GitPython-3.1.3-py3-none-any.whl", hash = "sha256:ef1d60b01b5ce0040ad3ec20bc64f783362d41fa0822a2742d3586e1f49bb8ac"}, + {file = "GitPython-3.1.3.tar.gz", hash = "sha256:e107af4d873daed64648b4f4beb89f89f0cfbe3ef558fc7821ed2331c2f8da1a"}, ] idna = [ {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, ] importlib-metadata = [ - {file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, - {file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, + {file = "importlib_metadata-1.6.1-py2.py3-none-any.whl", hash = "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"}, + {file = "importlib_metadata-1.6.1.tar.gz", hash = "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545"}, ] itsdangerous = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, @@ -762,8 +863,8 @@ limits = [ {file = "limits-1.5.1.tar.gz", hash = "sha256:f0c3319f032c4bfad68438ed1325c0fac86dac64582c7c25cddc87a0b658fa20"}, ] mako = [ - {file = "Mako-1.1.2-py2.py3-none-any.whl", hash = "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9"}, - {file = "Mako-1.1.2.tar.gz", hash = "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d"}, + {file = "Mako-1.1.3-py2.py3-none-any.whl", hash = "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"}, + {file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -805,8 +906,8 @@ mccabe = [ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] more-itertools = [ - {file = "more-itertools-8.3.0.tar.gz", hash = "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be"}, - {file = "more_itertools-8.3.0-py3-none-any.whl", hash = "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"}, + {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, + {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, ] packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, @@ -857,8 +958,8 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"}, ] py = [ - {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, - {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, + {file = "py-1.8.2-py2.py3-none-any.whl", hash = "sha256:a673fa23d7000440cc885c17dbd34fafcb7d7a6e230b29f6766400de36a33c44"}, + {file = "py-1.8.2.tar.gz", hash = "sha256:f3b3a4c36512a4c4f024041ab51866f11761cc169670204b235f6b20523d4e6b"}, ] py-healthcheck = [ {file = "py-healthcheck-1.10.1.tar.gz", hash = "sha256:60bbaab729a89098f0e6723ba5b6ab4ca8bde79b1a1bdb324f2b9e39df33780d"}, @@ -867,10 +968,18 @@ pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, ] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] pyflakes = [ {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, ] +pyjwt = [ + {file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"}, + {file = "PyJWT-1.7.1.tar.gz", hash = "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"}, +] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, @@ -912,12 +1021,13 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] requests = [ + {file = "requests-2.23.0-py2.7.egg", hash = "sha256:5d2d0ffbb515f39417009a46c14256291061ac01ba8f875b90cad137de83beb4"}, {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, ] six = [ - {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, - {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] smmap = [ {file = "smmap-3.0.4-py2.py3-none-any.whl", hash = "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4"}, @@ -957,8 +1067,8 @@ sqlalchemy-utils = [ {file = "SQLAlchemy-Utils-0.36.6.tar.gz", hash = "sha256:7a7fab14bed80df065412bbf71a0a9b0bfeb4b7c111c2d9bffe57283082f3a6b"}, ] stevedore = [ - {file = "stevedore-1.32.0-py2.py3-none-any.whl", hash = "sha256:a4e7dc759fb0f2e3e2f7d8ffe2358c19d45b9b8297f393ef1256858d82f69c9b"}, - {file = "stevedore-1.32.0.tar.gz", hash = "sha256:18afaf1d623af5950cc0f7e75e70f917784c73b652a34a12d90b309451b5500b"}, + {file = "stevedore-2.0.0-py3-none-any.whl", hash = "sha256:471c920412265cc809540ae6fb01f3f02aba89c79bbc7091372f4745a50f9691"}, + {file = "stevedore-2.0.0.tar.gz", hash = "sha256:001e90cd704be6470d46cc9076434e2d0d566c1379187e7013eb296d3a6032d9"}, ] urllib3 = [ {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"}, @@ -968,8 +1078,8 @@ uwsgi = [ {file = "uWSGI-2.0.19.1.tar.gz", hash = "sha256:faa85e053c0b1be4d5585b0858d3a511d2cd10201802e8676060fd0a109e5869"}, ] wcwidth = [ - {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, - {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, + {file = "wcwidth-0.2.4-py2.py3-none-any.whl", hash = "sha256:79375666b9954d4a1a10739315816324c3e73110af9d0e102d906fdb0aec009f"}, + {file = "wcwidth-0.2.4.tar.gz", hash = "sha256:8c6b5b6ee1360b842645f336d9e5d68c55817c26d3050f46b235ef2bc650e48f"}, ] werkzeug = [ {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, diff --git a/pyproject.toml b/pyproject.toml index adc98a6f..068d961a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,8 @@ sqlalchemy = "1.3.17" SQLAlchemy-Utils = "0.36.6" uWSGI = "2.0.19.1" Werkzeug = "1.0.1" +pyjwt = "^1.7.1" +cryptography = "^2.9.2" [tool.poetry.dev-dependencies] diff --git a/tests/unit/test_auth_jwt.py b/tests/unit/test_auth_jwt.py new file mode 100644 index 00000000..c384c4de --- /dev/null +++ b/tests/unit/test_auth_jwt.py @@ -0,0 +1,193 @@ +from app.api.auth import authenticate +from app.models import Key +from datetime import datetime, timedelta +from jwt import encode +from unittest.mock import patch + +BOGUS_KEY = """-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEfuFA5sEB92QE +86hggQBRTqqCb3ADOAg4vjXDJ+CXhqwASVD1ORnqr6B+tvFDOgmmR3QKu4rmn159 +hJOfF6xfKQPD3yJKWKwHniA7n3ISBhanbOtYHJmABbdbTucmxrsrJsp1uHQtO1Y5 +pW9dWXPGCfzvNAXL5xnFBgqBHEVXm75ZuYHpMYaIOtUWP8MAnFgZFqMQcPUcLmPM +n5DMA1jHStxVrG8b0Bolo39zJYG+JnQEFlpw3IYjcvV0gDG45LSbjFv4a9d2soh7 +PkPZ+OTv0NNAH6zl9fTEtS29ZumZ8n7Ies+T4noQJQozpG0dy4MwyMakfbIFDxFI +UgxMQDNRAgMBAAECggEAc1I/4zZKqlvGIL3b4R90z0NLARhj9g+pi5ves7Wws11/ +jv94eyNTGZCPsi6uNBVY2nTvHulooOeBrkrj1KgxKvxIUGmhl16pQCNGqZdvfDKE +kyhyixjl4eP486bANNrHuJCgnCxwSqebeGOmk3PPZbgw8TZn/H6aei6Mda/g5oPi +2zfyvG+DyrqWrOiNESfGRs7BxVZn7/jJfAXnQDDkDuvoEDyjupMMgR3l9xY6rOZP ++aVL0qJizt5H9BLtHYweoU2GRqnyVnsBNyL3AXpqAOaRFJpZXoXn5mJb/fZFhYE1 +f7+dwkBq1lxstq85GmZNcK9IZJ6mGXlSxIBJ/hovkQKBgQDpyLKm6wM7zCShwljj +2CA1RRCkxW9skRchZLoyFoOSFDZLa0Y7Xl1SjmU1y02+NsTRzvWjmtd1vQozUDkc +MvgM1fjdo4D8XA48RG5K7l1/56CPk/0QIGVYaDWlv0nfGwfPLvUJvptBtdLzyXCi +9TurctJ7Rox/WUSr2oGR4W9o/QKBgQDXKw/hhQZLyOIYvBbi7WBTUW/7dP70rT5K +1VIOdcNDh4FhiEulWiqC0pA0w2qSOOIyZd2zdkaFOFMAfYnoEWF4HSNcOfXMQ/wp +iWcA7Jv2voFwHkxiUK51gM21BfV0v+YKSotwqsRLWYv997J/Hg5xWaDoRStRWm2N +t6Vs78895QKBgQCwSMA+EXSMwLDWsP/qPux6fqvAM4iDqxxv985XOpbXrhoK4MdC +uTNRr0IuQDFNP2tGcfLT/Ux+4Z8xdkq6MszMkQRpzILUyG2LkGZCZl9mtThjS8pF +QMhq05mwc/+2FmHbHqNzR6E2+W4qmjkvCBCIhbqlbls/JAceN1QAtqcV2QKBgGHh +d/76Wavd/WSNI8glff1I/a0hQt4hdUXrlsF3NtWgbe3lZ6wXwWDz0p/+CZvs/pE4 +n8sE0f3GapO9iB+m0HUopC5PO46pmqt2kwHroON1NELBtbO/yi0v4+QmisuKhGZI +FPiy5kr0uGdW579F+AH+aOFgnd0LSuz+DuXojZk1AoGBANLTm9oujykwW/4lSBep +O8rd/SNpeaz4BwZiNg6yRNImeyQMQFqQUEoDohEhHkd9Y7wNW21I7jR+VA7qPWTk +lgfxGIOU16EyMffrBIIbpH+YMABG+MAxKrGbRVXr24VU6zAZqE9mEvujSYxnZW/C +DJQHadGUXFAGcrQKpxHv7QA0 +-----END PRIVATE KEY-----""" + +FAKE_EMAIL = 'test@example.org' +FAKE_APIKEY = 'abcdef1234567890' +SECRET_KEY = open(".dev/dev-jwt-key").read() +EXP = datetime.utcnow() + timedelta(seconds=10) +delta = timedelta(seconds=-11) + +GOOD_AUTH = "Bearer " + encode({'email': FAKE_EMAIL, 'exp': EXP}, + SECRET_KEY, algorithm='RS256').decode('utf-8') +BAD_AUTH = "Bearer " + encode({'email': FAKE_EMAIL, 'exp': EXP}, + BOGUS_KEY, algorithm='RS256').decode('utf-8') +EXP_AUTH = "Bearer " + encode({'email': FAKE_EMAIL, 'exp': EXP + delta}, + SECRET_KEY, algorithm='RS256').decode('utf-8') +NO_EXP_AUTH = "Bearer " + encode({'email': FAKE_EMAIL}, + SECRET_KEY, algorithm='RS256').decode('utf-8') + + +def create_fake_key(session, **kwargs): + kwargs['email'] = kwargs.get('email', FAKE_EMAIL) + kwargs['apikey'] = kwargs.get('apikey', FAKE_APIKEY) + key = Key(**kwargs) + session.add(key) + session.commit() + return key + + +def test_missing_auth_header(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = {} + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_empty_auth_header(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': '' + } + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_invalid_auth_header(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': 'bogus' + } + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_no_expiration(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': NO_EXP_AUTH + } + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_expired_token(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': EXP_AUTH + } + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_invalid_signature_failure(module_client): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': BAD_AUTH + } + result = wrapper() + + # Assert + assert result[1] == 401 + + +def test_authenticate_success(module_client, function_empty_db): + # Arrange + def callback(*args, **kwargs): + return 1 + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': GOOD_AUTH + } + result = wrapper() + + # Assert + assert result == 1 + + +def test_blacklisted_apikey(module_client, function_empty_db): + # Arrange + def callback(*args, **kwargs): + return 1 + create_fake_key(function_empty_db.session, blacklisted=True) + + # Act + wrapper = authenticate(callback) + with patch('app.api.auth.request') as fake_request: + fake_request.headers = { + 'authorization': GOOD_AUTH + } + result = wrapper() + + # Assert + assert result[1] == 401