From aa0c23bb0737584a605399a41d3d45314679b673 Mon Sep 17 00:00:00 2001 From: nargis-sultani <102271080+nargis-sultani@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:53:59 -0600 Subject: [PATCH] =?UTF-8?q?Updated=20src/main,=20because=20on=20changing?= =?UTF-8?q?=20the=20FastAPI=20version=20on=5Fevent(st=E2=80=A6=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …artup) was depecated in the new version. Incorporated ragtech_commons_api and deleted the old ones --------- Co-authored-by: Nargis Sultani --- poetry.lock | 236 +++++++++------------ pyproject.toml | 3 +- src/config.py | 46 +--- src/entities/models/__init__.py | 2 - src/entities/models/dto.py | 46 +--- src/main.py | 27 ++- src/oauth2/__init__.py | 4 - src/oauth2/oauth2_admin.py | 93 -------- src/oauth2/oauth2_backend.py | 46 ---- src/routers/admin.py | 10 +- src/routers/institutions.py | 9 +- src/util/__init__.py | 2 - src/util/router_wrapper.py | 22 -- tests/api/conftest.py | 5 +- tests/api/routers/test_admin_api.py | 12 +- tests/api/routers/test_institutions_api.py | 6 +- tests/app/test_config.py | 22 -- 17 files changed, 156 insertions(+), 435 deletions(-) delete mode 100644 src/oauth2/__init__.py delete mode 100644 src/oauth2/oauth2_admin.py delete mode 100644 src/oauth2/oauth2_backend.py delete mode 100644 src/util/router_wrapper.py diff --git a/poetry.lock b/poetry.lock index 8de770d..91fa1dc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiosqlite" version = "0.19.0" description = "asyncio bridge to the standard sqlite3 module" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -20,7 +19,6 @@ docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"] name = "alembic" version = "1.13.1" description = "A database migration tool for SQLAlchemy." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -40,7 +38,6 @@ tz = ["backports.zoneinfo"] name = "annotated-types" version = "0.6.0" description = "Reusable constraint types to use with typing.Annotated" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -52,7 +49,6 @@ files = [ name = "anyio" version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -73,7 +69,6 @@ trio = ["trio (<0.22)"] name = "asyncpg" version = "0.27.0" description = "An asyncio PostgreSQL driver" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -124,7 +119,6 @@ test = ["flake8 (>=5.0.4,<5.1.0)", "uvloop (>=0.15.3)"] name = "black" version = "23.7.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -169,7 +163,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -181,7 +174,6 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -281,7 +273,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -296,7 +287,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -308,7 +298,6 @@ files = [ name = "coverage" version = "7.4.0" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -373,7 +362,6 @@ toml = ["tomli"] name = "deprecation" version = "2.1.0" description = "A library to handle automated deprecations" -category = "main" optional = false python-versions = "*" files = [ @@ -388,7 +376,6 @@ packaging = "*" name = "ecdsa" version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -405,21 +392,20 @@ gmpy2 = ["gmpy2"] [[package]] name = "fastapi" -version = "0.103.2" +version = "0.104.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "fastapi-0.103.2-py3-none-any.whl", hash = "sha256:3270de872f0fe9ec809d4bd3d4d890c6d5cc7b9611d721d6438f9dacc8c4ef2e"}, - {file = "fastapi-0.103.2.tar.gz", hash = "sha256:75a11f6bfb8fc4d2bec0bd710c2d5f2829659c0e8c0afd5560fdda6ce25ec653"}, + {file = "fastapi-0.104.1-py3-none-any.whl", hash = "sha256:752dc31160cdbd0436bb93bad51560b57e525cbb1d4bbf6f4904ceee75548241"}, + {file = "fastapi-0.104.1.tar.gz", hash = "sha256:e5e4540a7c5e1dcfbbcf5b903c234feddcdcd881f191977a1c5dfd917487e7ae"}, ] [package.dependencies] anyio = ">=3.7.1,<4.0.0" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" starlette = ">=0.27.0,<0.28.0" -typing-extensions = ">=4.5.0" +typing-extensions = ">=4.8.0" [package.extras] all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] @@ -428,7 +414,6 @@ all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" name = "greenlet" version = "3.0.3" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -500,7 +485,6 @@ test = ["objgraph", "psutil"] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -512,7 +496,6 @@ files = [ name = "httpcore" version = "0.17.3" description = "A minimal low-level HTTP client." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -524,17 +507,16 @@ files = [ anyio = ">=3.0,<5.0" certifi = "*" h11 = ">=0.13,<0.15" -sniffio = ">=1.0.0,<2.0.0" +sniffio = "==1.*" [package.extras] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "httpx" version = "0.24.1" description = "The next generation HTTP client." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -550,15 +532,14 @@ sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -570,7 +551,6 @@ files = [ name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -580,14 +560,13 @@ files = [ [[package]] name = "mako" -version = "1.3.0" +version = "1.3.1" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.0-py3-none-any.whl", hash = "sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9"}, - {file = "Mako-1.3.0.tar.gz", hash = "sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"}, + {file = "Mako-1.3.1-py3-none-any.whl", hash = "sha256:463f03e04559689adaee25e0967778d6ad41285ed607dc1e7df0dd4e4df81f9e"}, + {file = "Mako-1.3.1.tar.gz", hash = "sha256:baee30b9c61718e093130298e678abed0dbfa1b411fcc4c1ab4df87cd631a0f2"}, ] [package.dependencies] @@ -600,79 +579,77 @@ testing = ["pytest"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.4" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, ] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -684,7 +661,6 @@ files = [ name = "packaging" version = "23.2" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -696,7 +672,6 @@ files = [ name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -708,7 +683,6 @@ files = [ name = "platformdirs" version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -722,14 +696,13 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -740,7 +713,6 @@ testing = ["pytest", "pytest-benchmark"] name = "psycopg2-binary" version = "2.9.9" description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -819,7 +791,6 @@ files = [ name = "pyasn1" version = "0.5.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ @@ -831,7 +802,6 @@ files = [ name = "pydantic" version = "2.5.3" description = "Data validation using Python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -851,7 +821,6 @@ email = ["email-validator (>=2.0.0)"] name = "pydantic-core" version = "2.14.6" description = "" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -969,7 +938,6 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" name = "pydantic-settings" version = "2.1.0" description = "Settings management using Pydantic" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -985,7 +953,6 @@ python-dotenv = ">=0.21.0" name = "pytest" version = "7.4.4" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1006,7 +973,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-alembic" version = "0.10.7" description = "A pytest plugin for verifying alembic migrations." -category = "dev" optional = false python-versions = ">=3.6,<4" files = [ @@ -1023,7 +989,6 @@ sqlalchemy = "*" name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1042,7 +1007,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1061,7 +1025,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-env" version = "1.1.3" description = "pytest plugin that allows you to add environment variables." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1079,7 +1042,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "pytest-mock (>=3.12)"] name = "pytest-mock" version = "3.12.0" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1095,14 +1057,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] @@ -1112,7 +1073,6 @@ cli = ["click (>=5.0)"] name = "python-jose" version = "3.3.0" description = "JOSE implementation in Python" -category = "main" optional = false python-versions = "*" files = [ @@ -1134,7 +1094,6 @@ pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] name = "python-keycloak" version = "3.7.0" description = "python-keycloak is a Python package providing access to the Keycloak API." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1151,11 +1110,32 @@ requests-toolbelt = ">=0.6.0" [package.extras] docs = ["Sphinx (>=6.1.0,<7.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (>=0.9.1,<0.10.0)", "m2r2 (>=0.3.2,<0.4.0)", "mock (>=4.0.3,<5.0.0)", "readthedocs-sphinx-ext (>=2.1.9,<3.0.0)", "recommonmark (>=0.7.1,<0.8.0)", "sphinx-autoapi (>=3.0.0,<4.0.0)", "sphinx-rtd-theme (>=1.0.0,<2.0.0)"] +[[package]] +name = "regtech-api-commons" +version = "0.1.0" +description = "" +optional = false +python-versions = "^3.11" +files = [] +develop = false + +[package.dependencies] +fastapi = "^0.104.1" +pydantic-settings = "^2.1.0" +python-jose = "^3.3.0" +python-keycloak = "^3.7.0" +requests = "^2.31.0" + +[package.source] +type = "git" +url = "https://github.com/cfpb/regtech-api-commons.git" +reference = "HEAD" +resolved_reference = "741f6c6e956276f59924a497df8a03609eb82a62" + [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1177,7 +1157,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-toolbelt" version = "1.0.0" description = "A utility belt for advanced users of python-requests" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1192,7 +1171,6 @@ requests = ">=2.0.1,<3.0.0" name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -category = "main" optional = false python-versions = ">=3.6,<4" files = [ @@ -1207,7 +1185,6 @@ pyasn1 = ">=0.1.3" name = "ruff" version = "0.0.278" description = "An extremely fast Python linter, written in Rust." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1234,7 +1211,6 @@ files = [ name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1246,7 +1222,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1258,7 +1233,6 @@ files = [ name = "sqlalchemy" version = "2.0.25" description = "Database Abstraction Library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1346,7 +1320,6 @@ sqlcipher = ["sqlcipher3_binary"] name = "starlette" version = "0.27.0" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1364,7 +1337,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1376,7 +1348,6 @@ files = [ name = "urllib3" version = "2.1.0" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1393,7 +1364,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "uvicorn" version = "0.22.0" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1411,4 +1381,4 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "b9ded9d72cb84a1c5f995a5831620fb3ca3fb479571747dc5376cd505b695fe6" +content-hash = "1e4184a8b9065f00fa5188c700345df2a8ede68b623742663343805463426982" diff --git a/pyproject.toml b/pyproject.toml index b260890..29347d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ packages = [{ include = "regtech-user-fi-management" }] [tool.poetry.dependencies] python = "^3.11" -fastapi = "^0.103.1" +fastapi = "^0.104.1" uvicorn = "^0.22.0" python-dotenv = "^1.0.0" python-keycloak = "^3.0.0" @@ -19,6 +19,7 @@ requests = "^2.31.0" asyncpg = "^0.27.0" alembic = "^1.12.0" pydantic-settings = "^2.0.3" +regtech-api-commons = {git = "https://github.com/cfpb/regtech-api-commons.git"} [tool.poetry.group.dev.dependencies] ruff = "0.0.278" diff --git a/src/config.py b/src/config.py index f11e602..6b8cae6 100644 --- a/src/config.py +++ b/src/config.py @@ -1,12 +1,13 @@ import os from urllib import parse -from typing import Dict, Any +from typing import Any -from pydantic import TypeAdapter, field_validator, ValidationInfo -from pydantic.networks import HttpUrl, PostgresDsn -from pydantic.types import SecretStr +from pydantic import field_validator, ValidationInfo +from pydantic.networks import PostgresDsn from pydantic_settings import BaseSettings, SettingsConfigDict +from regtech_api_commons.oauth2.config import KeycloakSettings + JWT_OPTS_PREFIX = "jwt_opts_" @@ -23,20 +24,9 @@ class Settings(BaseSettings): inst_db_host: str inst_db_scheme: str = "postgresql+asyncpg" inst_conn: PostgresDsn | None = None - auth_client: str - auth_url: HttpUrl - token_url: HttpUrl - certs_url: HttpUrl - kc_url: HttpUrl - kc_realm: str - kc_admin_client_id: str - kc_admin_client_secret: SecretStr - kc_realm_url: HttpUrl - jwt_opts: Dict[str, bool | int] = {} def __init__(self, **data): super().__init__(**data) - self.set_jwt_opts() @field_validator("inst_conn", mode="before") @classmethod @@ -50,31 +40,9 @@ def build_postgres_dsn(cls, postgres_dsn, info: ValidationInfo) -> Any: ) return str(postgres_dsn) - def set_jwt_opts(self) -> None: - """ - Converts `jwt_opts_` prefixed settings, and env vars into JWT options dictionary. - all options are boolean, with exception of 'leeway' being int - valid options can be found here: - https://github.com/mpdavis/python-jose/blob/4b0701b46a8d00988afcc5168c2b3a1fd60d15d8/jose/jwt.py#L81 - - Because we're using model_extra to load in jwt_opts as a dynamic dictionary, - normal env overrides does not take place on top of dotenv files, - so we're merging settings.model_extra with environment variables. - """ - jwt_opts_adapter = TypeAdapter(int | bool) - self.jwt_opts = { - **self.parse_jwt_vars(jwt_opts_adapter, self.model_extra.items()), - **self.parse_jwt_vars(jwt_opts_adapter, os.environ.items()), - } - - def parse_jwt_vars(self, type_adapter: TypeAdapter, setting_variables: Dict[str, Any]) -> Dict[str, bool | int]: - return { - key.lower().replace(JWT_OPTS_PREFIX, ""): type_adapter.validate_python(value) - for (key, value) in setting_variables - if key.lower().startswith(JWT_OPTS_PREFIX) - } - model_config = SettingsConfigDict(env_file=env_files_to_load, extra="allow") settings = Settings() + +kc_settings = KeycloakSettings(_env_file=env_files_to_load) diff --git a/src/entities/models/__init__.py b/src/entities/models/__init__.py index 64feb9b..d61563a 100644 --- a/src/entities/models/__init__.py +++ b/src/entities/models/__init__.py @@ -10,7 +10,6 @@ "DeniedDomainDao", "DeniedDomainDto", "UserProfile", - "AuthenticatedUser", "FederalRegulatorDao", "HMDAInstitutionTypeDao", "SBLInstitutionTypeDao", @@ -42,7 +41,6 @@ FinanicialInstitutionAssociationDto, DeniedDomainDto, UserProfile, - AuthenticatedUser, FederalRegulatorDto, InstitutionTypeDto, AddressStateDto, diff --git a/src/entities/models/dto.py b/src/entities/models/dto.py index 04ac4dc..90e98da 100644 --- a/src/entities/models/dto.py +++ b/src/entities/models/dto.py @@ -1,6 +1,5 @@ -from typing import List, Dict, Any, Set +from typing import List, Set from pydantic import BaseModel, model_validator -from starlette.authentication import BaseUser class FinancialInsitutionDomainBase(BaseModel): @@ -132,46 +131,3 @@ class FinancialInstitutionWithRelationsDto(FinancialInstitutionDto): class FinanicialInstitutionAssociationDto(FinancialInstitutionWithRelationsDto): approved: bool - - -class AuthenticatedUser(BaseUser, BaseModel): - claims: Dict[str, Any] - name: str - username: str - email: str - id: str - institutions: List[str] - - @classmethod - def from_claim(cls, claims: Dict[str, Any]) -> "AuthenticatedUser": - return cls( - claims=claims, - name=claims.get("name", ""), - username=claims.get("preferred_username", ""), - email=claims.get("email", ""), - id=claims.get("sub", ""), - institutions=cls.parse_institutions(claims.get("institutions")), - ) - - @classmethod - def parse_institutions(cls, institutions: List[str] | None) -> List[str]: - """ - Parse out the list of institutions returned by Keycloak - - Args: - institutions(List[str]): list of full institution paths provided by keycloak, - it is possible to have nested paths, though we may not use the feature. - e.g. ["/ROOT_INSTITUTION/CHILD_INSTITUTION/GRAND_CHILD_INSTITUTION"] - - Returns: - List[str]: List of cleaned up institutions. - e.g. ["GRAND_CHILD_INSTITUTION"] - """ - if institutions: - return [institution.split("/")[-1] for institution in institutions] - else: - return [] - - @property - def is_authenticated(self) -> bool: - return True diff --git a/src/main.py b/src/main.py index 0b6fba8..2dffd0e 100644 --- a/src/main.py +++ b/src/main.py @@ -1,3 +1,4 @@ +from contextlib import asynccontextmanager import os import logging from http import HTTPStatus @@ -11,17 +12,15 @@ from routers import admin_router, institutions_router -from oauth2 import BearerTokenAuthBackend +from regtech_api_commons.oauth2.oauth2_backend import BearerTokenAuthBackend +from regtech_api_commons.oauth2.oauth2_admin import OAuth2Admin -from config import settings +from config import kc_settings log = logging.getLogger() -app = FastAPI() - -@app.on_event("startup") -async def app_start(): +def run_migrations(): file_dir = os.path.dirname(os.path.realpath(__file__)) alembic_cfg = Config(f"{file_dir}/../alembic.ini") alembic_cfg.set_main_option("script_location", f"{file_dir}/../db_revisions") @@ -29,6 +28,18 @@ async def app_start(): command.upgrade(alembic_cfg, "head") +@asynccontextmanager +async def lifespan(app_: FastAPI): + log.info("Starting up...") + log.info("run alembic upgrade head...") + run_migrations() + yield + log.info("Shutting down...") + + +app = FastAPI(lifespan=lifespan) + + @app.exception_handler(HTTPException) async def http_exception_handler(request: Request, exception: HTTPException) -> JSONResponse: log.error(exception, exc_info=True, stack_info=True) @@ -45,10 +56,10 @@ async def general_exception_handler(request: Request, exception: Exception) -> J oauth2_scheme = OAuth2AuthorizationCodeBearer( - authorizationUrl=settings.auth_url.unicode_string(), tokenUrl=settings.token_url.unicode_string() + authorizationUrl=kc_settings.auth_url.unicode_string(), tokenUrl=kc_settings.token_url.unicode_string() ) -app.add_middleware(AuthenticationMiddleware, backend=BearerTokenAuthBackend(oauth2_scheme)) +app.add_middleware(AuthenticationMiddleware, backend=BearerTokenAuthBackend(oauth2_scheme, OAuth2Admin(kc_settings))) app.add_middleware( CORSMiddleware, allow_origins=["*"], diff --git a/src/oauth2/__init__.py b/src/oauth2/__init__.py deleted file mode 100644 index 94759fc..0000000 --- a/src/oauth2/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -__all__ = ["oauth2_admin", "BearerTokenAuthBackend"] - -from .oauth2_admin import oauth2_admin -from .oauth2_backend import BearerTokenAuthBackend diff --git a/src/oauth2/oauth2_admin.py b/src/oauth2/oauth2_admin.py deleted file mode 100644 index f608fd6..0000000 --- a/src/oauth2/oauth2_admin.py +++ /dev/null @@ -1,93 +0,0 @@ -from http import HTTPStatus -import logging -from typing import Dict, Any, Set - -import jose.jwt -import requests -from fastapi import HTTPException - -from keycloak import KeycloakAdmin, KeycloakOpenIDConnection, exceptions as kce - -from config import settings - -log = logging.getLogger(__name__) - - -class OAuth2Admin: - def __init__(self) -> None: - self._keys = None - conn = KeycloakOpenIDConnection( - server_url=settings.kc_url.unicode_string(), - realm_name=settings.kc_realm, - client_id=settings.kc_admin_client_id, - client_secret_key=settings.kc_admin_client_secret.get_secret_value(), - ) - self._admin = KeycloakAdmin(connection=conn) - - def get_claims(self, token: str) -> Dict[str, str] | None: - try: - return jose.jwt.decode( - token=token, - key=self._get_keys(), - issuer=settings.kc_realm_url.unicode_string(), - audience=settings.auth_client, - options=settings.jwt_opts, - ) - except jose.ExpiredSignatureError: - pass - - def _get_keys(self) -> Dict[str, Any]: - if self._keys is None: - response = requests.get(settings.certs_url) - self._keys = response.json() - return self._keys - - def update_user(self, user_id: str, payload: Dict[str, Any]) -> None: - try: - self._admin.update_user(user_id, payload) - except kce.KeycloakError as e: - log.exception("Failed to update user: %s", user_id, extra=payload) - raise HTTPException(status_code=e.response_code, detail="Failed to update user") - - def upsert_group(self, lei: str, name: str) -> str: - try: - group_payload = {"name": lei, "attributes": {"fi_name": [name]}} - group = self.get_group(lei) - if group is None: - return self._admin.create_group(group_payload) - else: - self._admin.update_group(group["id"], group_payload) - return group["id"] - except kce.KeycloakError as e: - log.exception("Failed to upsert group, lei: %s, name: %s", lei, name) - raise HTTPException(status_code=e.response_code, detail="Failed to upsert group") - - def get_group(self, lei: str) -> Dict[str, Any] | None: - try: - return self._admin.get_group_by_path(f"/{lei}") - except kce.KeycloakError: - return None - - def associate_to_group(self, user_id: str, group_id: str) -> None: - try: - self._admin.group_user_add(user_id, group_id) - except kce.KeycloakError as e: - log.exception("Failed to associate user %s to group %s", user_id, group_id) - raise HTTPException(status_code=e.response_code, detail="Failed to associate user to group") - - def associate_to_lei(self, user_id: str, lei: str) -> None: - group = self.get_group(lei) - if group is not None: - self.associate_to_group(user_id, group["id"]) - else: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail="No institution found for given LEI", - ) - - def associate_to_leis(self, user_id: str, leis: Set[str]): - for lei in leis: - self.associate_to_lei(user_id, lei) - - -oauth2_admin = OAuth2Admin() diff --git a/src/oauth2/oauth2_backend.py b/src/oauth2/oauth2_backend.py deleted file mode 100644 index 9bffc6d..0000000 --- a/src/oauth2/oauth2_backend.py +++ /dev/null @@ -1,46 +0,0 @@ -import logging -from typing import Coroutine, Any, Dict, List, Tuple -from fastapi import HTTPException -from starlette.authentication import ( - AuthCredentials, - AuthenticationBackend, - BaseUser, - UnauthenticatedUser, -) -from fastapi.security import OAuth2AuthorizationCodeBearer -from starlette.requests import HTTPConnection - -from entities.models import AuthenticatedUser - -from .oauth2_admin import oauth2_admin - -log = logging.getLogger(__name__) - - -class BearerTokenAuthBackend(AuthenticationBackend): - def __init__(self, token_bearer: OAuth2AuthorizationCodeBearer) -> None: - self.token_bearer = token_bearer - - async def authenticate(self, conn: HTTPConnection) -> Coroutine[Any, Any, Tuple[AuthCredentials, BaseUser] | None]: - try: - token = await self.token_bearer(conn) - claims = oauth2_admin.get_claims(token) - if claims is not None: - auths = ( - self.extract_nested(claims, "resource_access", "realm-management", "roles") - + self.extract_nested(claims, "resource_access", "account", "roles") - + ["authenticated"] - ) - return AuthCredentials(auths), AuthenticatedUser.from_claim(claims) - except HTTPException: - log.exception("failed to get claims") - return AuthCredentials("unauthenticated"), UnauthenticatedUser() - - def extract_nested(self, data: Dict[str, Any], *keys: str) -> List[str]: - _ele = data - try: - for key in keys: - _ele = _ele[key] - return _ele - except KeyError: - return [] diff --git a/src/routers/admin.py b/src/routers/admin.py index 744809a..62b3886 100644 --- a/src/routers/admin.py +++ b/src/routers/admin.py @@ -3,14 +3,18 @@ from fastapi import Depends, Request from starlette.authentication import requires from dependencies import check_domain -from util import Router + +from regtech_api_commons.api import Router from entities.models import UserProfile -from entities.models import AuthenticatedUser -from oauth2 import oauth2_admin +from regtech_api_commons.models.auth import AuthenticatedUser +from regtech_api_commons.oauth2.oauth2_admin import OAuth2Admin +from config import kc_settings router = Router() +oauth2_admin = OAuth2Admin(kc_settings) + @router.get("/me/", response_model=AuthenticatedUser) @requires("authenticated") diff --git a/src/routers/institutions.py b/src/routers/institutions.py index 63961ba..5083683 100644 --- a/src/routers/institutions.py +++ b/src/routers/institutions.py @@ -1,7 +1,8 @@ from fastapi import Depends, Request, HTTPException from http import HTTPStatus -from oauth2 import oauth2_admin -from util import Router +from regtech_api_commons.oauth2.oauth2_admin import OAuth2Admin +from config import kc_settings +from regtech_api_commons.api import Router from dependencies import check_domain, parse_leis, get_email_domain from typing import Annotated, List, Tuple, Literal from entities.engine import get_session @@ -13,12 +14,14 @@ FinancialInsitutionDomainCreate, FinanicialInstitutionAssociationDto, InstitutionTypeDto, - AuthenticatedUser, AddressStateDto, FederalRegulatorDto, ) from sqlalchemy.ext.asyncio import AsyncSession from starlette.authentication import requires +from regtech_api_commons.models.auth import AuthenticatedUser + +oauth2_admin = OAuth2Admin(kc_settings) InstitutionType = Literal["sbl", "hmda"] diff --git a/src/util/__init__.py b/src/util/__init__.py index 7ba9267..8b13789 100644 --- a/src/util/__init__.py +++ b/src/util/__init__.py @@ -1,3 +1 @@ -__all__ = ["Router"] -from .router_wrapper import Router diff --git a/src/util/router_wrapper.py b/src/util/router_wrapper.py deleted file mode 100644 index b3cc6f4..0000000 --- a/src/util/router_wrapper.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Any, Callable - -from fastapi import APIRouter -from fastapi.types import DecoratedCallable - - -class Router(APIRouter): - def api_route( - self, path: str, *, include_in_schema: bool = True, **kwargs: Any - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - if path.endswith("/"): - path = path[:-1] - - add_path = super().api_route(path, include_in_schema=include_in_schema, **kwargs) - - add_alt_path = super().api_route(f"{path}/", include_in_schema=False, **kwargs) - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - add_alt_path(func) - return add_path(func) - - return decorator diff --git a/tests/api/conftest.py b/tests/api/conftest.py index 19f4b14..0291c51 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -4,9 +4,8 @@ from fastapi import FastAPI from pytest_mock import MockerFixture from starlette.authentication import AuthCredentials, UnauthenticatedUser - +from regtech_api_commons.models.auth import AuthenticatedUser from entities.models import ( - AuthenticatedUser, FinancialInstitutionDao, FinancialInstitutionDomainDao, FederalRegulatorDao, @@ -32,7 +31,7 @@ def app_fixture(mocker: MockerFixture) -> FastAPI: @pytest.fixture def auth_mock(mocker: MockerFixture) -> Mock: - return mocker.patch("oauth2.oauth2_backend.BearerTokenAuthBackend.authenticate") + return mocker.patch("regtech_api_commons.oauth2.oauth2_backend.BearerTokenAuthBackend.authenticate") @pytest.fixture diff --git a/tests/api/routers/test_admin_api.py b/tests/api/routers/test_admin_api.py index 755625e..875e7c0 100644 --- a/tests/api/routers/test_admin_api.py +++ b/tests/api/routers/test_admin_api.py @@ -5,7 +5,7 @@ from pytest_mock import MockerFixture from starlette.authentication import AuthCredentials -from entities.models import AuthenticatedUser +from regtech_api_commons.models.auth import AuthenticatedUser class TestAdminApi: @@ -59,8 +59,8 @@ def test_update_me_no_permission(self, app_fixture: FastAPI, auth_mock: Mock): assert res.status_code == 403 def test_update_me(self, mocker: MockerFixture, app_fixture: FastAPI, authed_user_mock: Mock): - update_user_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.update_user") - associate_lei_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") + update_user_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.update_user") + associate_lei_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") update_user_mock.return_value = None associate_lei_mock.return_value = None client = TestClient(app_fixture) @@ -71,8 +71,8 @@ def test_update_me(self, mocker: MockerFixture, app_fixture: FastAPI, authed_use assert res.status_code == 202 def test_update_me_no_lei(self, mocker: MockerFixture, app_fixture: FastAPI, authed_user_mock: Mock): - update_user_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.update_user") - associate_lei_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") + update_user_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.update_user") + associate_lei_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") update_user_mock.return_value = None client = TestClient(app_fixture) res = client.put("/v1/admin/me", json={"first_name": "testFirst", "last_name": "testLast"}) @@ -81,7 +81,7 @@ def test_update_me_no_lei(self, mocker: MockerFixture, app_fixture: FastAPI, aut assert res.status_code == 202 def test_associate_institutions(self, mocker: MockerFixture, app_fixture: FastAPI, authed_user_mock: Mock): - associate_lei_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") + associate_lei_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.associate_to_leis") associate_lei_mock.return_value = None client = TestClient(app_fixture) res = client.put("/v1/admin/me/institutions", json=["testlei1", "testlei2"]) diff --git a/tests/api/routers/test_institutions_api.py b/tests/api/routers/test_institutions_api.py index e9dd030..59e58b7 100644 --- a/tests/api/routers/test_institutions_api.py +++ b/tests/api/routers/test_institutions_api.py @@ -4,7 +4,7 @@ from fastapi.testclient import TestClient from pytest_mock import MockerFixture from starlette.authentication import AuthCredentials -from oauth2.oauth2_backend import AuthenticatedUser +from regtech_api_commons.models.auth import AuthenticatedUser from entities.models import ( FinancialInstitutionDao, FinancialInstitutionDomainDao, @@ -62,7 +62,7 @@ def test_create_institution_authed(self, mocker: MockerFixture, app_fixture: Fas top_holder_legal_name="TOP HOLDER LEI 123", top_holder_rssd_id=123456, ) - upsert_group_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.upsert_group") + upsert_group_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.upsert_group") upsert_group_mock.return_value = "leiGroup" client = TestClient(app_fixture) res = client.post( @@ -106,7 +106,7 @@ def test_create_institution_only_required_fields( hq_address_state=AddressStateDao(code="VA", name="Virginia"), hq_address_zip="00000", ) - upsert_group_mock = mocker.patch("oauth2.oauth2_admin.OAuth2Admin.upsert_group") + upsert_group_mock = mocker.patch("regtech_api_commons.oauth2.oauth2_admin.OAuth2Admin.upsert_group") upsert_group_mock.return_value = "leiGroup" client = TestClient(app_fixture) res = client.post( diff --git a/tests/app/test_config.py b/tests/app/test_config.py index eeea71c..9937ce7 100644 --- a/tests/app/test_config.py +++ b/tests/app/test_config.py @@ -1,4 +1,3 @@ -import pytest from config import Settings @@ -12,24 +11,3 @@ def test_postgres_dsn_building(): } settings = Settings(**mock_config) assert str(settings.inst_conn) == "postgresql+asyncpg://user:%5Cz9-%2Ftgb76%23%40@test:5432/test" - - -def test_jwt_opts_valid_values(): - mock_config = { - "jwt_opts_test1": "true", - "jwt_opts_test2": "true", - "jwt_opts_test3": "12", - } - settings = Settings(**mock_config) - assert settings.jwt_opts == {"test1": True, "test2": True, "test3": 12} - - -def test_jwt_opts_invalid_values(): - mock_config = { - "jwt_opts_test1": "not a bool or int", - "jwt_opts_test2": "true", - "jwt_opts_test3": "12", - } - with pytest.raises(Exception) as e: - Settings(**mock_config) - assert "validation error" in str(e.value)