From 21a7f2255cc8dcba735f61693d8b336b661f07c4 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 11:01:20 -0400 Subject: [PATCH 01/26] Add Authlib package dependency --- requirements/dev.txt | 129 ++++++++++++++++++------------------------ requirements/main.in | 1 + requirements/main.txt | 89 +++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 79 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index c117a40..6eab160 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -4,9 +4,9 @@ # # pip-compile --generate-hashes --output-file=requirements/dev.txt requirements/dev.in # -anyio==3.3.1 \ - --hash=sha256:85913b4e2fec030e8c72a8f9f98092eeb9e25847a6e00d567751b77e34f856fe \ - --hash=sha256:d7c604dd491eca70e19c78664d685d5e4337612d574419d503e76f5d7d1590bd +anyio==3.3.2 \ + --hash=sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436 \ + --hash=sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b # via # -c requirements/main.txt # httpcore @@ -50,59 +50,40 @@ click==8.0.1 \ # via # -c requirements/main.txt # uvicorn -coverage[toml]==5.5 \ - --hash=sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c \ - --hash=sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6 \ - --hash=sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45 \ - --hash=sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a \ - --hash=sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03 \ - --hash=sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529 \ - --hash=sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a \ - --hash=sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a \ - --hash=sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2 \ - --hash=sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6 \ - --hash=sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759 \ - --hash=sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53 \ - --hash=sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a \ - --hash=sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4 \ - --hash=sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff \ - --hash=sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502 \ - --hash=sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793 \ - --hash=sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb \ - --hash=sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905 \ - --hash=sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821 \ - --hash=sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b \ - --hash=sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81 \ - --hash=sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0 \ - --hash=sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b \ - --hash=sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3 \ - --hash=sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184 \ - --hash=sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701 \ - --hash=sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a \ - --hash=sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82 \ - --hash=sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638 \ - --hash=sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5 \ - --hash=sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083 \ - --hash=sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6 \ - --hash=sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90 \ - --hash=sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465 \ - --hash=sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a \ - --hash=sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3 \ - --hash=sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e \ - --hash=sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066 \ - --hash=sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf \ - --hash=sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b \ - --hash=sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae \ - --hash=sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669 \ - --hash=sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873 \ - --hash=sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b \ - --hash=sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6 \ - --hash=sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb \ - --hash=sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160 \ - --hash=sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c \ - --hash=sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079 \ - --hash=sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d \ - --hash=sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6 +coverage[toml]==6.0.1 \ + --hash=sha256:07efe1fbd72e67df026ad5109bcd216acbbd4a29d5208b3dab61779bae6b7b26 \ + --hash=sha256:0898d6948b31df13391cd40568de8f35fa5901bc922c5ae05cf070587cb9c666 \ + --hash=sha256:0a7e55cc9f7efa22d5cc9966276ec7a40a8803676f6ccbfdc06a486fba9aa9ee \ + --hash=sha256:17426808e8e0824f864876312d41961223bf5e503bf8f1f846735279a60ea345 \ + --hash=sha256:1770d24f45f1f2daeae34cfa3b33fcb29702153544cd2ad40d58399dd4ff53b5 \ + --hash=sha256:1864bdf9b2ccb43e724051bc23a1c558daf101ad4488ede1945f2a8be1facdad \ + --hash=sha256:2c5f39d1556e75fc3c4fb071f9e7cfa618895a999a0de763a541d730775d0d5f \ + --hash=sha256:3490ff6dbf3f7accf0750136ed60ae1f487bccc1f097740e3b21262bc9c89854 \ + --hash=sha256:353a50f123f0185cdb7a1e1e3e2cfb9d1fd7e293cfaf68eedaf5bd8e02e3ec32 \ + --hash=sha256:3edbb3ec580c73e5a264f5d04f30245bc98eff1a26765d46c5c65134f0a0e2f7 \ + --hash=sha256:4eb9cd910ca8e243f930243a9940ea1a522e32435d15668445753d087c30ee12 \ + --hash=sha256:5b06f4f1729e2963281d9cd6e65e6976bf27b44d4c07ac5b47223ce45f822cec \ + --hash=sha256:5b1ceacb86e0a9558061dcc6baae865ed25933ea57effea644f21657cdce19bc \ + --hash=sha256:65da6e3e8325291f012921bbf71fea0a97824e1c573981871096aac6e2cf0ec5 \ + --hash=sha256:66fe33e9e0df58675e08e83fe257f89e7f625e7633ea93d0872154e09cce2724 \ + --hash=sha256:6873f3f954d3e3ab8b1881f4e5307cc19f70c9f931c41048d9f7e6fd946eabe7 \ + --hash=sha256:73880a80fad0597eca43e213e5e1711bf6c0fcdb7eb6b01b3b17841ebe5a7f8d \ + --hash=sha256:7600fac458f74c68b097379f76f3a6e3a630493fc7fc94b6508fedd9d498c194 \ + --hash=sha256:83682b73785d2e078e0b5f63410b8125b122e1a22422640c57edd4011c950f3e \ + --hash=sha256:83faa3692e8306b20293889714fdf573d10ef5efc5843bd7c7aea6971487bd6a \ + --hash=sha256:9c416ba03844608f45661a5b48dc59c6b5e89956efe388564dd138ca8caf540b \ + --hash=sha256:9d242a2434801ef5125330deddb4cddba8990c9a49b3dec99dca17dd7eefba5a \ + --hash=sha256:a2e15ab5afbee34abf716fece80ea33ea09a82e7450512f022723b1a82ec9a4e \ + --hash=sha256:abe8207dfb8a61ded9cd830d26c1073c8218fc0ae17eb899cfe8ec0fafae6e22 \ + --hash=sha256:ad7182a82843f9f85487f44567c8c688f16c906bdb8d0e44ae462aed61cb8f1b \ + --hash=sha256:b45f89a8ef65c29195f8f28dbe215f44ccb29d934f3e862d2a5c12e38698a793 \ + --hash=sha256:b81a4e667c45b13658b84f9b8f1d32ef86d5405fabcbd181b76b9e51d295f397 \ + --hash=sha256:c9c413c4397d4cdc7ca89286158d240ce524f9667b52c9a64dd7e13d16cf8815 \ + --hash=sha256:e11cca9eb5c9b3eaad899728ee2ce916138399ee8cbbccaadc1871fecb750827 \ + --hash=sha256:e66c50f0ab445fec920a9f084914ea1776a809e3016c3738519048195f851bbb \ + --hash=sha256:ea452a2d83964d08232ade470091015e7ab9b8f53acbec10f2210fbab4ce7e43 \ + --hash=sha256:f398d38e6ebc2637863db1d7be3d4f9c5174e7d24bb3b0716cdb1f204669cbcf \ + --hash=sha256:f82a17f2a77958f3eef40ad385fc82d4c6ba9a77a51a174efe03ce75daebbc16 # via # -r requirements/dev.in # pytest-cov @@ -110,9 +91,9 @@ distlib==0.3.3 \ --hash=sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31 \ --hash=sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05 # via virtualenv -filelock==3.0.12 \ - --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ - --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 +filelock==3.3.0 \ + --hash=sha256:8c7eab13dc442dc249e95158bcc12dec724465919bdc9831fdbf0660f03d1785 \ + --hash=sha256:bbc6a0382fe8ec4744ecdf6683a2e07f65eb10ff1aff53fc02a202565446cde0 # via virtualenv h11==0.12.0 \ --hash=sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6 \ @@ -152,9 +133,9 @@ httpx==0.19.0 \ # via # -c requirements/main.txt # -r requirements/dev.in -identify==2.2.15 \ - --hash=sha256:528a88021749035d5a39fe2ba67c0642b8341aaf71889da0e1ed669a429b87f0 \ - --hash=sha256:de83a84d774921669774a2000bf87ebba46b4d1c04775f4a5d37deff0cf39f73 +identify==2.3.0 \ + --hash=sha256:d1e82c83d063571bb88087676f81261a4eae913c492dafde184067c584bc7c05 \ + --hash=sha256:fd08c97f23ceee72784081f1ce5125c8f53a02d3f2716dde79a6ab8f1039fea5 # via pre-commit idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ @@ -204,9 +185,9 @@ packaging==21.0 \ --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 # via pytest -platformdirs==2.3.0 \ - --hash=sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f \ - --hash=sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648 +platformdirs==2.4.0 \ + --hash=sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2 \ + --hash=sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d # via virtualenv pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ @@ -235,9 +216,9 @@ pytest-asyncio==0.15.1 \ --hash=sha256:2564ceb9612bbd560d19ca4b41347b54e7835c2f792c504f698e05395ed63f6f \ --hash=sha256:3042bcdf1c5d978f6b74d96a151c4cfb9dcece65006198389ccd7e6c60eb1eea # via -r requirements/dev.in -pytest-cov==2.12.1 \ - --hash=sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a \ - --hash=sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7 +pytest-cov==3.0.0 \ + --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ + --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 # via -r requirements/dev.in python-dotenv==0.19.0 \ --hash=sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1 \ @@ -302,11 +283,13 @@ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f # via - # coverage # mypy # pre-commit # pytest - # pytest-cov +tomli==1.2.1 \ + --hash=sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f \ + --hash=sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442 + # via coverage typing-extensions==3.10.0.2 \ --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ @@ -340,9 +323,9 @@ uvloop==0.16.0 \ # via # -c requirements/main.txt # uvicorn -virtualenv==20.8.0 \ - --hash=sha256:4da4ac43888e97de9cf4fdd870f48ed864bbfd133d2c46cbdec941fed4a25aef \ - --hash=sha256:a4b987ec31c3c9996cf1bc865332f967fe4a0512c41b39652d6224f696e69da5 +virtualenv==20.8.1 \ + --hash=sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300 \ + --hash=sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8 # via pre-commit watchgod==0.7 \ --hash=sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29 \ diff --git a/requirements/main.in b/requirements/main.in index b924a84..7c5b138 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -16,3 +16,4 @@ safir httpx aws-request-signer python-dotenv +Authlib diff --git a/requirements/main.txt b/requirements/main.txt index 735d95f..788b4a1 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -4,14 +4,18 @@ # # pip-compile --generate-hashes --output-file=requirements/main.txt requirements/main.in # -anyio==3.3.1 \ - --hash=sha256:85913b4e2fec030e8c72a8f9f98092eeb9e25847a6e00d567751b77e34f856fe \ - --hash=sha256:d7c604dd491eca70e19c78664d685d5e4337612d574419d503e76f5d7d1590bd +anyio==3.3.2 \ + --hash=sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436 \ + --hash=sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b # via httpcore asgiref==3.4.1 \ --hash=sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9 \ --hash=sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214 # via uvicorn +authlib==0.15.4 \ + --hash=sha256:37df3a2554bc6fe0da3cc6848c44fac2ae40634a7f8fc72543947f4330b26464 \ + --hash=sha256:d9fe5edb59801b16583faa86f88d798d99d952979b9616d5c735b9170b41ae2c + # via -r requirements/main.in aws-request-signer==1.1.0 \ --hash=sha256:5e04f30bf815c58f4e776a659ce6009f11ff97d66af1352e8794bddc0b3a1e0c \ --hash=sha256:da60cfeb19410243550b2a441c014f8543c639c6aec4c047cc35807ecd259e31 @@ -20,6 +24,53 @@ certifi==2021.5.30 \ --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 # via httpx +cffi==1.14.6 \ + --hash=sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d \ + --hash=sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771 \ + --hash=sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872 \ + --hash=sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c \ + --hash=sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc \ + --hash=sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762 \ + --hash=sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202 \ + --hash=sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5 \ + --hash=sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548 \ + --hash=sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a \ + --hash=sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f \ + --hash=sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20 \ + --hash=sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218 \ + --hash=sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c \ + --hash=sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e \ + --hash=sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56 \ + --hash=sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224 \ + --hash=sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a \ + --hash=sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2 \ + --hash=sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a \ + --hash=sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819 \ + --hash=sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346 \ + --hash=sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b \ + --hash=sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e \ + --hash=sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534 \ + --hash=sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb \ + --hash=sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0 \ + --hash=sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156 \ + --hash=sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd \ + --hash=sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87 \ + --hash=sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc \ + --hash=sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195 \ + --hash=sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33 \ + --hash=sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f \ + --hash=sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d \ + --hash=sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd \ + --hash=sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728 \ + --hash=sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7 \ + --hash=sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca \ + --hash=sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99 \ + --hash=sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf \ + --hash=sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e \ + --hash=sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c \ + --hash=sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5 \ + --hash=sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69 + # via cryptography charset-normalizer==2.0.6 \ --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f @@ -28,9 +79,31 @@ click==8.0.1 \ --hash=sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a \ --hash=sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6 # via uvicorn -fastapi==0.68.1 \ - --hash=sha256:644bb815bae326575c4b2842469fb83053a4b974b82fa792ff9283d17fbbd99d \ - --hash=sha256:94d2820906c36b9b8303796fb7271337ec89c74223229e3cfcf056b5a7d59e23 +cryptography==35.0.0 \ + --hash=sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6 \ + --hash=sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6 \ + --hash=sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c \ + --hash=sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999 \ + --hash=sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e \ + --hash=sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992 \ + --hash=sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d \ + --hash=sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588 \ + --hash=sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa \ + --hash=sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d \ + --hash=sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd \ + --hash=sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d \ + --hash=sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953 \ + --hash=sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2 \ + --hash=sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8 \ + --hash=sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6 \ + --hash=sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9 \ + --hash=sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6 \ + --hash=sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad \ + --hash=sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76 + # via authlib +fastapi==0.68.2 \ + --hash=sha256:36bcdd3dbea87c586061005e4a40b9bd0145afd766655b4e0ec1d8870b32555c \ + --hash=sha256:38526fc46bda73f7ec92033952677323c16061e70a91d15c95f18b11895da494 # via # -r requirements/main.in # safir @@ -77,6 +150,10 @@ idna==3.2 \ # via # anyio # rfc3986 +pycparser==2.20 \ + --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ + --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 + # via cffi pydantic==1.8.2 \ --hash=sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd \ --hash=sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739 \ From e3eca8bb4319d2f9782695b6eb2f21a850caaaf0 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 13:47:26 -0400 Subject: [PATCH 02/26] Add credential configs for GitHub OAuth client --- src/ltdproxy/config.py | 6 ++++++ tox.ini | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index 3d51158..8f2e1bf 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -53,6 +53,12 @@ class Configuration(BaseSettings): ..., env="LTDPROXY_AWS_SECRET_ACCESS_KEY" ) + github_oauth_client_id: str = Field(env="LTDPROXY_GITHUB_OAUTH_ID") + + github_oauth_client_secret: SecretStr = Field( + env="LTDPROXY_GITHUB_OAUTH_SECRET" + ) + config = Configuration(_env_file=os.getenv("LTD_PROXY_ENV")) """Configuration for ltd-proxy.""" diff --git a/tox.ini b/tox.ini index a62c17a..8b9f968 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,8 @@ deps = setenv = LTDPROXY_AWS_ACCESS_KEY_ID = foo LTDPROXY_AWS_SECRET_ACCESS_KEY = bar + LTDPROXY_GITHUB_OAUTH_ID = foo + LTDPROXY_GITHUB_OAUTH_SECRET = bar commands = pytest --cov=ltdproxy --cov-branch --cov-report= {posargs} From 4d69bf8fa589a8fcab645268c6e4da4ccb6fb7e1 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 14:50:47 -0400 Subject: [PATCH 03/26] Add itsdangeorus for sessions middleware --- requirements/main.in | 1 + requirements/main.txt | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/requirements/main.in b/requirements/main.in index 7c5b138..d602a74 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -17,3 +17,4 @@ httpx aws-request-signer python-dotenv Authlib +itsdangerous diff --git a/requirements/main.txt b/requirements/main.txt index 788b4a1..1092ef0 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -7,7 +7,9 @@ anyio==3.3.2 \ --hash=sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436 \ --hash=sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b - # via httpcore + # via + # httpcore + # starlette asgiref==3.4.1 \ --hash=sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9 \ --hash=sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214 @@ -101,9 +103,9 @@ cryptography==35.0.0 \ --hash=sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad \ --hash=sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76 # via authlib -fastapi==0.68.2 \ - --hash=sha256:36bcdd3dbea87c586061005e4a40b9bd0145afd766655b4e0ec1d8870b32555c \ - --hash=sha256:38526fc46bda73f7ec92033952677323c16061e70a91d15c95f18b11895da494 +fastapi==0.70.0 \ + --hash=sha256:66da43cfe5185ea1df99552acffd201f1832c6b364e0f4136c0a99f933466ced \ + --hash=sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c # via # -r requirements/main.in # safir @@ -150,6 +152,10 @@ idna==3.2 \ # via # anyio # rfc3986 +itsdangerous==2.0.1 \ + --hash=sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c \ + --hash=sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0 + # via -r requirements/main.in pycparser==2.20 \ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 @@ -232,9 +238,9 @@ sniffio==1.2.0 \ # anyio # httpcore # httpx -starlette==0.14.2 \ - --hash=sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed \ - --hash=sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa +starlette==0.16.0 \ + --hash=sha256:38eb24bf705a2c317e15868e384c1b8a12ca396e5a3c3a003db7e667c43f939f \ + --hash=sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870 # via # -r requirements/main.in # fastapi From d0d648bf3e69b6c04ffa41eb09f550c28fba5607 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 14:51:21 -0400 Subject: [PATCH 04/26] Fix env var name for S3 prefix config --- src/ltdproxy/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index 8f2e1bf..c0443fc 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -43,7 +43,7 @@ class Configuration(BaseSettings): s3_bucket: str = Field("test", env="LTDPROXY_S3_BUCKET") - s3_bucket_prefix: str = Field("", env="LTD_PROXY_S3_PREFIX") + s3_bucket_prefix: str = Field("", env="LTDPROXY_S3_PREFIX") aws_region: str = Field("us-central-1", env="LTDPROXY_AWS_REGION") From acd3424cd41c2f6a0fc424a285f605efab37bb91 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 14:52:24 -0400 Subject: [PATCH 05/26] Add configuration for session key --- src/ltdproxy/config.py | 2 ++ tox.ini | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index c0443fc..4afdff6 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -59,6 +59,8 @@ class Configuration(BaseSettings): env="LTDPROXY_GITHUB_OAUTH_SECRET" ) + session_key: SecretStr = Field(env="LTDPROXY_SESSION_KEY") + config = Configuration(_env_file=os.getenv("LTD_PROXY_ENV")) """Configuration for ltd-proxy.""" diff --git a/tox.ini b/tox.ini index 8b9f968..9c7b93b 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,7 @@ setenv = LTDPROXY_AWS_SECRET_ACCESS_KEY = bar LTDPROXY_GITHUB_OAUTH_ID = foo LTDPROXY_GITHUB_OAUTH_SECRET = bar + LTDPROXY_SESSION_KEY = 1234 commands = pytest --cov=ltdproxy --cov-branch --cov-report= {posargs} From 4fef6b409ca856fde07656f86bbb0970d8a07c8c Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 7 Oct 2021 14:53:12 -0400 Subject: [PATCH 06/26] Add sessions middleware This is needed for persistent sessions in the oauth flow. --- src/ltdproxy/main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ltdproxy/main.py b/src/ltdproxy/main.py index feaa735..f494806 100644 --- a/src/ltdproxy/main.py +++ b/src/ltdproxy/main.py @@ -13,6 +13,7 @@ from safir.dependencies.http_client import http_client_dependency from safir.logging import configure_logging from safir.middleware.x_forwarded import XForwardedMiddleware +from starlette.middleware.sessions import SessionMiddleware from .config import config from .handlers.external import external_router @@ -32,16 +33,19 @@ # Define the external routes in a subapp so that it will serve its own OpenAPI # interface definition and documentation URLs under the external URL. -_subapp = FastAPI( +external_app = FastAPI( title="ltd-proxy", description=metadata("ltd-proxy").get("Summary", ""), version=metadata("ltd-proxy").get("Version", "0.0.0"), ) -_subapp.include_router(external_router) +external_app.include_router(external_router) +external_app.add_middleware( + SessionMiddleware, secret_key=config.session_key.get_secret_value() +) # Attach the internal routes and subapp to the main application. app.include_router(internal_router) -app.mount(f"/{config.name}", _subapp) +app.mount(f"/{config.name}", external_app) @app.on_event("startup") From 21b021a51af452f536997f44e6f438258c264feb Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Tue, 12 Oct 2021 13:51:34 -0400 Subject: [PATCH 07/26] Improve S3 bucket dependency naming This clarifies what is the dependency, and what is the class providing the singleton. --- src/ltdproxy/s3.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ltdproxy/s3.py b/src/ltdproxy/s3.py index 28c35ef..8dcf242 100644 --- a/src/ltdproxy/s3.py +++ b/src/ltdproxy/s3.py @@ -58,7 +58,11 @@ async def stream_object( ) -class BucketDependency: +class ConfiguredBucket: + """This class maintains a configured Bucket instance; an instance of this + class can be used as a FastAPI path dependency. + """ + def __init__(self) -> None: self.bucket = Bucket( bucket=config.s3_bucket, @@ -67,8 +71,10 @@ def __init__(self) -> None: secret_access_key=config.aws_secret_access_key.get_secret_value(), ) - def __call__(self) -> Bucket: + async def __call__(self) -> Bucket: + # This method is async so that FastAPI does not create an extra thread + # when calling this. return self.bucket -bucket_dependency = BucketDependency() +bucket_dependency = ConfiguredBucket() From b23cb371bc23e6ce90173d6acf925fc96cc98796 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Tue, 12 Oct 2021 13:53:17 -0400 Subject: [PATCH 08/26] Demonstrate GitHub OAuth We're using authlib via their starlette integration to implement a GitHub OAuth client. - GitHubOAuth configures/registers the client, and github_oauth_dependency provides an instance of that GitHub client as a path operation depenency. - The GitHub access token is stored in the Cookie session. Note that the endpoints for logging in and logging out are just stubs; we'll develop those further so they're configured more naturally and redirect to the right place. --- src/ltdproxy/githubauth.py | 51 +++++++++++++++++++++++++ src/ltdproxy/handlers/external.py | 63 ++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/ltdproxy/githubauth.py diff --git a/src/ltdproxy/githubauth.py b/src/ltdproxy/githubauth.py new file mode 100644 index 0000000..9465b1d --- /dev/null +++ b/src/ltdproxy/githubauth.py @@ -0,0 +1,51 @@ +"""The GitHub OAuth dependency for path operations.""" + +from __future__ import annotations + +from typing import TypeVar + +import authlib.integrations.starlette_client.integration +from authlib.integrations.starlette_client import OAuth + +from ltdproxy.config import config + +GitHubOAuthType = TypeVar( + "GitHubOAuthType", + bound=authlib.integrations.starlette_client.integration.StarletteRemoteApp, +) +"""Type alias from the authlib GitHub OAuth client.""" + + +class GitHubOAuth: + """This class maintains an OAuth instance that is registered for GitHub + OAuth with the applications configurations. + + The instance of this class, ``github_oauth_dependency`` is a FastAPI + path operation dependency that provides the configured OAuth instance + to endpoint handlers. + """ + + def __init__(self) -> None: + self.oauth = OAuth() + self.oauth.register( + name="github", + client_id=config.github_oauth_client_id, + client_secret=config.github_oauth_client_secret.get_secret_value(), + access_token_url="https://github.com/login/oauth/access_token", + access_token_params=None, + authorize_url="https://github.com/login/oauth/authorize", + authorize_params=None, + api_base_url="https://api.github.com/", + client_kwargs={"scope": "user:email,read:org"}, + ) + + async def __call__(self) -> GitHubOAuthType: + # This method is async so that FastAPI does not create an extra thread + # when calling this. + return self.oauth.github + + +github_oauth_dependency = GitHubOAuth() +"""Path dependency that returns a configured +`authlib.integrations.starlette_client.OAuth` instance for GitHub OAuth. +""" diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index 0b2c97f..1807b94 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -1,14 +1,23 @@ """Handlers for the app's external root, ``/ltdproxy/``.""" +from typing import Union + import httpx +from authlib.integrations.starlette_client import OAuthError from fastapi import APIRouter, Depends from safir.dependencies.http_client import http_client_dependency from safir.dependencies.logger import logger_dependency from starlette.background import BackgroundTask -from starlette.responses import StreamingResponse +from starlette.requests import Request +from starlette.responses import ( + HTMLResponse, + RedirectResponse, + StreamingResponse, +) from structlog.stdlib import BoundLogger from ltdproxy.config import config +from ltdproxy.githubauth import GitHubOAuthType, github_oauth_dependency from ltdproxy.s3 import Bucket, bucket_dependency __all__ = ["get_s3", "external_router"] @@ -17,6 +26,58 @@ """FastAPI router for all external handlers.""" +@external_router.get("/") +async def homepage(request: Request) -> HTMLResponse: + github_token = request.session.get("github_token") + if github_token: + html = "

hello!

" 'logout' + return HTMLResponse(html) + return HTMLResponse('login') + + +@external_router.get("/auth") +async def get_oauth_callback( + request: Request, + logger: BoundLogger = Depends(logger_dependency), + github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), +) -> Union[RedirectResponse, HTMLResponse]: + try: + token = await github_oauth.authorize_access_token(request) + except OAuthError as error: + return HTMLResponse(f"

{error.error}

") + print(token) + print(type(token)) + github_token = token.get("access_token") + logger.info( + "Got github oauth token", token=token, access_token=github_token + ) + if github_token: + request.session["github_token"] = github_token + return RedirectResponse(url="/ltdproxy/") + + +@external_router.get("/login") +async def login( + request: Request, + logger: BoundLogger = Depends(logger_dependency), + github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), +) -> RedirectResponse: + # redirect_uri = request.url_for('get_oauth_callback') + redirect_uri = "http://127.0.0.1:8000/ltdproxy/auth" + logger.info("Redirecting to GitHub auth", callback_url=redirect_uri) + return await github_oauth.authorize_redirect(request, redirect_uri) + + +@external_router.get("/logout") +async def logout( + request: Request, + logger: BoundLogger = Depends(logger_dependency), +) -> RedirectResponse: + request.session.pop("github_token", None) + logger.info("Logged out") + return RedirectResponse(url="/ltdproxy/") + + @external_router.get( "/{path:path}", description="The S3 front-end proxy.", From bc612a3364244f31fae000dbf30820d1204f706d Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 25 Oct 2021 13:14:36 -0400 Subject: [PATCH 09/26] Add gidgethub dependency --- requirements/dev.txt | 210 +++++++++++++++++++------------------- requirements/main.in | 1 + requirements/main.txt | 231 +++++++++++++++++++++++------------------- 3 files changed, 235 insertions(+), 207 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index 6eab160..97c3198 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -4,9 +4,9 @@ # # pip-compile --generate-hashes --output-file=requirements/dev.txt requirements/dev.in # -anyio==3.3.2 \ - --hash=sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436 \ - --hash=sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b +anyio==3.3.4 \ + --hash=sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66 \ + --hash=sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff # via # -c requirements/main.txt # httpcore @@ -28,9 +28,9 @@ backports.entry-points-selectable==1.1.0 \ --hash=sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a \ --hash=sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc # via virtualenv -certifi==2021.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via # -c requirements/main.txt # httpx @@ -38,52 +38,52 @@ cfgv==3.3.1 \ --hash=sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426 \ --hash=sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736 # via pre-commit -charset-normalizer==2.0.6 \ - --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ - --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f +charset-normalizer==2.0.7 \ + --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \ + --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b # via # -c requirements/main.txt # httpx -click==8.0.1 \ - --hash=sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a \ - --hash=sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6 +click==8.0.3 \ + --hash=sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3 \ + --hash=sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b # via # -c requirements/main.txt # uvicorn -coverage[toml]==6.0.1 \ - --hash=sha256:07efe1fbd72e67df026ad5109bcd216acbbd4a29d5208b3dab61779bae6b7b26 \ - --hash=sha256:0898d6948b31df13391cd40568de8f35fa5901bc922c5ae05cf070587cb9c666 \ - --hash=sha256:0a7e55cc9f7efa22d5cc9966276ec7a40a8803676f6ccbfdc06a486fba9aa9ee \ - --hash=sha256:17426808e8e0824f864876312d41961223bf5e503bf8f1f846735279a60ea345 \ - --hash=sha256:1770d24f45f1f2daeae34cfa3b33fcb29702153544cd2ad40d58399dd4ff53b5 \ - --hash=sha256:1864bdf9b2ccb43e724051bc23a1c558daf101ad4488ede1945f2a8be1facdad \ - --hash=sha256:2c5f39d1556e75fc3c4fb071f9e7cfa618895a999a0de763a541d730775d0d5f \ - --hash=sha256:3490ff6dbf3f7accf0750136ed60ae1f487bccc1f097740e3b21262bc9c89854 \ - --hash=sha256:353a50f123f0185cdb7a1e1e3e2cfb9d1fd7e293cfaf68eedaf5bd8e02e3ec32 \ - --hash=sha256:3edbb3ec580c73e5a264f5d04f30245bc98eff1a26765d46c5c65134f0a0e2f7 \ - --hash=sha256:4eb9cd910ca8e243f930243a9940ea1a522e32435d15668445753d087c30ee12 \ - --hash=sha256:5b06f4f1729e2963281d9cd6e65e6976bf27b44d4c07ac5b47223ce45f822cec \ - --hash=sha256:5b1ceacb86e0a9558061dcc6baae865ed25933ea57effea644f21657cdce19bc \ - --hash=sha256:65da6e3e8325291f012921bbf71fea0a97824e1c573981871096aac6e2cf0ec5 \ - --hash=sha256:66fe33e9e0df58675e08e83fe257f89e7f625e7633ea93d0872154e09cce2724 \ - --hash=sha256:6873f3f954d3e3ab8b1881f4e5307cc19f70c9f931c41048d9f7e6fd946eabe7 \ - --hash=sha256:73880a80fad0597eca43e213e5e1711bf6c0fcdb7eb6b01b3b17841ebe5a7f8d \ - --hash=sha256:7600fac458f74c68b097379f76f3a6e3a630493fc7fc94b6508fedd9d498c194 \ - --hash=sha256:83682b73785d2e078e0b5f63410b8125b122e1a22422640c57edd4011c950f3e \ - --hash=sha256:83faa3692e8306b20293889714fdf573d10ef5efc5843bd7c7aea6971487bd6a \ - --hash=sha256:9c416ba03844608f45661a5b48dc59c6b5e89956efe388564dd138ca8caf540b \ - --hash=sha256:9d242a2434801ef5125330deddb4cddba8990c9a49b3dec99dca17dd7eefba5a \ - --hash=sha256:a2e15ab5afbee34abf716fece80ea33ea09a82e7450512f022723b1a82ec9a4e \ - --hash=sha256:abe8207dfb8a61ded9cd830d26c1073c8218fc0ae17eb899cfe8ec0fafae6e22 \ - --hash=sha256:ad7182a82843f9f85487f44567c8c688f16c906bdb8d0e44ae462aed61cb8f1b \ - --hash=sha256:b45f89a8ef65c29195f8f28dbe215f44ccb29d934f3e862d2a5c12e38698a793 \ - --hash=sha256:b81a4e667c45b13658b84f9b8f1d32ef86d5405fabcbd181b76b9e51d295f397 \ - --hash=sha256:c9c413c4397d4cdc7ca89286158d240ce524f9667b52c9a64dd7e13d16cf8815 \ - --hash=sha256:e11cca9eb5c9b3eaad899728ee2ce916138399ee8cbbccaadc1871fecb750827 \ - --hash=sha256:e66c50f0ab445fec920a9f084914ea1776a809e3016c3738519048195f851bbb \ - --hash=sha256:ea452a2d83964d08232ade470091015e7ab9b8f53acbec10f2210fbab4ce7e43 \ - --hash=sha256:f398d38e6ebc2637863db1d7be3d4f9c5174e7d24bb3b0716cdb1f204669cbcf \ - --hash=sha256:f82a17f2a77958f3eef40ad385fc82d4c6ba9a77a51a174efe03ce75daebbc16 +coverage[toml]==6.0.2 \ + --hash=sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1 \ + --hash=sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0 \ + --hash=sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9 \ + --hash=sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895 \ + --hash=sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d \ + --hash=sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe \ + --hash=sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2 \ + --hash=sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4 \ + --hash=sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce \ + --hash=sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9 \ + --hash=sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122 \ + --hash=sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7 \ + --hash=sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3 \ + --hash=sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff \ + --hash=sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149 \ + --hash=sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a \ + --hash=sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164 \ + --hash=sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1 \ + --hash=sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd \ + --hash=sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc \ + --hash=sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f \ + --hash=sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9 \ + --hash=sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9 \ + --hash=sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0 \ + --hash=sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d \ + --hash=sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa \ + --hash=sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7 \ + --hash=sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822 \ + --hash=sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc \ + --hash=sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7 \ + --hash=sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330 \ + --hash=sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb \ + --hash=sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24 # via # -r requirements/dev.in # pytest-cov @@ -91,9 +91,9 @@ distlib==0.3.3 \ --hash=sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31 \ --hash=sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05 # via virtualenv -filelock==3.3.0 \ - --hash=sha256:8c7eab13dc442dc249e95158bcc12dec724465919bdc9831fdbf0660f03d1785 \ - --hash=sha256:bbc6a0382fe8ec4744ecdf6683a2e07f65eb10ff1aff53fc02a202565446cde0 +filelock==3.3.1 \ + --hash=sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f \ + --hash=sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f # via virtualenv h11==0.12.0 \ --hash=sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6 \ @@ -127,19 +127,19 @@ httptools==0.2.0 \ # via # -c requirements/main.txt # uvicorn -httpx==0.19.0 \ - --hash=sha256:92ecd2c00c688b529eda11cedb15161eaf02dee9116712f621c70d9a40b2cdd0 \ - --hash=sha256:9bd728a6c5ec0a9e243932a9983d57d3cc4a87bb4f554e1360fce407f78f9435 +httpx==0.20.0 \ + --hash=sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b \ + --hash=sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8 # via # -c requirements/main.txt # -r requirements/dev.in -identify==2.3.0 \ - --hash=sha256:d1e82c83d063571bb88087676f81261a4eae913c492dafde184067c584bc7c05 \ - --hash=sha256:fd08c97f23ceee72784081f1ce5125c8f53a02d3f2716dde79a6ab8f1039fea5 +identify==2.3.1 \ + --hash=sha256:5a5000bd3293950d992843c0ef3d82b90a582de2161557bda7f493c8c8864f26 \ + --hash=sha256:8a92c56893e9a4ce951f09a50489986615e3eba7b4c60610e0b25f93ca4487ba # via pre-commit -idna==3.2 \ - --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ - --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via # -c requirements/main.txt # anyio @@ -201,9 +201,9 @@ py==1.10.0 \ --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a # via pytest -pyparsing==2.4.7 \ - --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ - --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b +pyparsing==3.0.1 \ + --hash=sha256:84196357aa3566d64ad123d7a3c67b0e597a115c4934b097580e5ce220b91531 \ + --hash=sha256:fd93fc45c47893c300bd98f5dd1b41c0e783eaeb727e7cea210dcc09d64ce7c3 # via packaging pytest==6.2.5 \ --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ @@ -212,50 +212,54 @@ pytest==6.2.5 \ # -r requirements/dev.in # pytest-asyncio # pytest-cov -pytest-asyncio==0.15.1 \ - --hash=sha256:2564ceb9612bbd560d19ca4b41347b54e7835c2f792c504f698e05395ed63f6f \ - --hash=sha256:3042bcdf1c5d978f6b74d96a151c4cfb9dcece65006198389ccd7e6c60eb1eea +pytest-asyncio==0.16.0 \ + --hash=sha256:5f2a21273c47b331ae6aa5b36087047b4899e40f03f18397c0e65fa5cca54e9b \ + --hash=sha256:7496c5977ce88c34379df64a66459fe395cd05543f0a2f837016e7144391fcfb # via -r requirements/dev.in pytest-cov==3.0.0 \ --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 # via -r requirements/dev.in -python-dotenv==0.19.0 \ - --hash=sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1 \ - --hash=sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172 +python-dotenv==0.19.1 \ + --hash=sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8 \ + --hash=sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a # via # -c requirements/main.txt # uvicorn -pyyaml==5.4.1 \ - --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \ - --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \ - --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \ - --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \ - --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \ - --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \ - --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \ - --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \ - --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \ - --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \ - --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \ - --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \ - --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \ - --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \ - --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \ - --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \ - --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \ - --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \ - --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \ - --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \ - --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \ - --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \ - --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \ - --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \ - --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \ - --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \ - --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \ - --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \ - --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0 +pyyaml==6.0 \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 # via # -c requirements/main.txt # pre-commit @@ -286,9 +290,9 @@ toml==0.10.2 \ # mypy # pre-commit # pytest -tomli==1.2.1 \ - --hash=sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f \ - --hash=sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442 +tomli==1.2.2 \ + --hash=sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee \ + --hash=sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade # via coverage typing-extensions==3.10.0.2 \ --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ @@ -323,9 +327,9 @@ uvloop==0.16.0 \ # via # -c requirements/main.txt # uvicorn -virtualenv==20.8.1 \ - --hash=sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300 \ - --hash=sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8 +virtualenv==20.9.0 \ + --hash=sha256:1d145deec2da86b29026be49c775cc5a9aab434f85f7efef98307fb3965165de \ + --hash=sha256:bb55ace18de14593947354e5e6cd1be75fb32c3329651da62e92bf5d0aab7213 # via pre-commit watchgod==0.7 \ --hash=sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29 \ diff --git a/requirements/main.in b/requirements/main.in index d602a74..4241513 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -18,3 +18,4 @@ aws-request-signer python-dotenv Authlib itsdangerous +gidgethub diff --git a/requirements/main.txt b/requirements/main.txt index 1092ef0..283f073 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -4,9 +4,9 @@ # # pip-compile --generate-hashes --output-file=requirements/main.txt requirements/main.in # -anyio==3.3.2 \ - --hash=sha256:0b993a2ef6c1dc456815c2b5ca2819f382f20af98087cc2090a4afed3a501436 \ - --hash=sha256:c32da314c510b34a862f5afeaf8a446ffed2c2fde21583e654bd71ecfb5b744b +anyio==3.3.4 \ + --hash=sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66 \ + --hash=sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff # via # httpcore # starlette @@ -14,72 +14,77 @@ asgiref==3.4.1 \ --hash=sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9 \ --hash=sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214 # via uvicorn -authlib==0.15.4 \ - --hash=sha256:37df3a2554bc6fe0da3cc6848c44fac2ae40634a7f8fc72543947f4330b26464 \ - --hash=sha256:d9fe5edb59801b16583faa86f88d798d99d952979b9616d5c735b9170b41ae2c +authlib==0.15.5 \ + --hash=sha256:b83cf6360c8e92b0e9df0d1f32d675790bcc4e3c03977499b1eed24dcdef4252 \ + --hash=sha256:ecf4a7a9f2508c0bb07e93a752dd3c495cfaffc20e864ef0ffc95e3f40d2abaf # via -r requirements/main.in aws-request-signer==1.1.0 \ --hash=sha256:5e04f30bf815c58f4e776a659ce6009f11ff97d66af1352e8794bddc0b3a1e0c \ --hash=sha256:da60cfeb19410243550b2a441c014f8543c639c6aec4c047cc35807ecd259e31 # via -r requirements/main.in -certifi==2021.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via httpx -cffi==1.14.6 \ - --hash=sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d \ - --hash=sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771 \ - --hash=sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872 \ - --hash=sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c \ - --hash=sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc \ - --hash=sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762 \ - --hash=sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202 \ - --hash=sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5 \ - --hash=sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548 \ - --hash=sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a \ - --hash=sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f \ - --hash=sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20 \ - --hash=sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218 \ - --hash=sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c \ - --hash=sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e \ - --hash=sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56 \ - --hash=sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224 \ - --hash=sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a \ - --hash=sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2 \ - --hash=sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a \ - --hash=sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819 \ - --hash=sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346 \ - --hash=sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b \ - --hash=sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e \ - --hash=sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534 \ - --hash=sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb \ - --hash=sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0 \ - --hash=sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156 \ - --hash=sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd \ - --hash=sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87 \ - --hash=sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc \ - --hash=sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195 \ - --hash=sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33 \ - --hash=sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f \ - --hash=sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d \ - --hash=sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd \ - --hash=sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728 \ - --hash=sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7 \ - --hash=sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca \ - --hash=sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99 \ - --hash=sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf \ - --hash=sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e \ - --hash=sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c \ - --hash=sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5 \ - --hash=sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69 +cffi==1.15.0 \ + --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \ + --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \ + --hash=sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636 \ + --hash=sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20 \ + --hash=sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728 \ + --hash=sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27 \ + --hash=sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66 \ + --hash=sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443 \ + --hash=sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0 \ + --hash=sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7 \ + --hash=sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39 \ + --hash=sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605 \ + --hash=sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a \ + --hash=sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37 \ + --hash=sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029 \ + --hash=sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139 \ + --hash=sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc \ + --hash=sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df \ + --hash=sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14 \ + --hash=sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880 \ + --hash=sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2 \ + --hash=sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a \ + --hash=sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e \ + --hash=sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474 \ + --hash=sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024 \ + --hash=sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8 \ + --hash=sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0 \ + --hash=sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e \ + --hash=sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a \ + --hash=sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e \ + --hash=sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032 \ + --hash=sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6 \ + --hash=sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e \ + --hash=sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b \ + --hash=sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e \ + --hash=sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954 \ + --hash=sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962 \ + --hash=sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c \ + --hash=sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4 \ + --hash=sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55 \ + --hash=sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962 \ + --hash=sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023 \ + --hash=sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c \ + --hash=sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6 \ + --hash=sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8 \ + --hash=sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382 \ + --hash=sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7 \ + --hash=sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc \ + --hash=sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997 \ + --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796 # via cryptography -charset-normalizer==2.0.6 \ - --hash=sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6 \ - --hash=sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f +charset-normalizer==2.0.7 \ + --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \ + --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b # via httpx -click==8.0.1 \ - --hash=sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a \ - --hash=sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6 +click==8.0.3 \ + --hash=sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3 \ + --hash=sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b # via uvicorn cryptography==35.0.0 \ --hash=sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6 \ @@ -102,13 +107,19 @@ cryptography==35.0.0 \ --hash=sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6 \ --hash=sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad \ --hash=sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76 - # via authlib + # via + # authlib + # pyjwt fastapi==0.70.0 \ --hash=sha256:66da43cfe5185ea1df99552acffd201f1832c6b364e0f4136c0a99f933466ced \ --hash=sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c # via # -r requirements/main.in # safir +gidgethub==5.0.1 \ + --hash=sha256:3efbd6998600254ec7a2869318bd3ffde38edc3a0d37be0c14bc46b45947b682 \ + --hash=sha256:67245e93eb0918b37df038148af675df43b62e832c529d7f859f6b90d9f3e70d + # via -r requirements/main.in gunicorn==20.1.0 \ --hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \ --hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8 @@ -140,15 +151,15 @@ httptools==0.2.0 \ --hash=sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb \ --hash=sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065 # via uvicorn -httpx==0.19.0 \ - --hash=sha256:92ecd2c00c688b529eda11cedb15161eaf02dee9116712f621c70d9a40b2cdd0 \ - --hash=sha256:9bd728a6c5ec0a9e243932a9983d57d3cc4a87bb4f554e1360fce407f78f9435 +httpx==0.20.0 \ + --hash=sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b \ + --hash=sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8 # via # -r requirements/main.in # safir -idna==3.2 \ - --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ - --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via # anyio # rfc3986 @@ -186,42 +197,50 @@ pydantic==1.8.2 \ # via # fastapi # safir -python-dotenv==0.19.0 \ - --hash=sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1 \ - --hash=sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172 +pyjwt[crypto]==2.3.0 \ + --hash=sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41 \ + --hash=sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f + # via gidgethub +python-dotenv==0.19.1 \ + --hash=sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8 \ + --hash=sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a # via # -r requirements/main.in # uvicorn -pyyaml==5.4.1 \ - --hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \ - --hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \ - --hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \ - --hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \ - --hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \ - --hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \ - --hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \ - --hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \ - --hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \ - --hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \ - --hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e \ - --hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \ - --hash=sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347 \ - --hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \ - --hash=sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541 \ - --hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \ - --hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \ - --hash=sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc \ - --hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \ - --hash=sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa \ - --hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \ - --hash=sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122 \ - --hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \ - --hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \ - --hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \ - --hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \ - --hash=sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247 \ - --hash=sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6 \ - --hash=sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0 +pyyaml==6.0 \ + --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ + --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ + --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ + --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ + --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ + --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ + --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ + --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ + --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ + --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ + --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ + --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 # via uvicorn rfc3986[idna2008]==1.5.0 \ --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \ @@ -245,15 +264,19 @@ starlette==0.16.0 \ # -r requirements/main.in # fastapi # safir -structlog==21.1.0 \ - --hash=sha256:62f06fc0ee32fb8580f0715eea66cb87271eb7efb0eaf9af6b639cba8981de47 \ - --hash=sha256:d9d2d890532e8db83c6977a2a676fb1889922ff0c26ad4dc0ecac26f9fafbc57 +structlog==21.2.0 \ + --hash=sha256:63a7111a32e5b615671536bb745692ea02cebfea2b39dcb7d2617eed19437cfe \ + --hash=sha256:7ac42b565e1295712313f91edbcb64e0840a9037d888c8954f11fa6c43270e99 # via safir typing-extensions==3.10.0.2 \ --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ --hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34 # via pydantic +uritemplate==4.1.1 \ + --hash=sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0 \ + --hash=sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e + # via gidgethub uvicorn[standard]==0.15.0 \ --hash=sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1 \ --hash=sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff From 6a067ad1ea5822288b677370b6a8d27735644808 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Mon, 25 Oct 2021 13:15:15 -0400 Subject: [PATCH 10/26] Add GitHub org and team memberships to session After login, we cache the relevant org and team memberships in the cookie (JSON serialized). This will allow endpoints to quickly determine if the user is in the correct endpoints for a given path authorization rule. --- src/ltdproxy/githubauth.py | 50 ++++++++++++++++++++++++++++++- src/ltdproxy/handlers/external.py | 12 +++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/ltdproxy/githubauth.py b/src/ltdproxy/githubauth.py index 9465b1d..dd0c9c0 100644 --- a/src/ltdproxy/githubauth.py +++ b/src/ltdproxy/githubauth.py @@ -2,13 +2,25 @@ from __future__ import annotations -from typing import TypeVar +import json +from typing import TYPE_CHECKING, Any, Dict, List, Tuple, TypeVar import authlib.integrations.starlette_client.integration +import gidgethub.httpx from authlib.integrations.starlette_client import OAuth from ltdproxy.config import config +if TYPE_CHECKING: + import httpx + +__all__ = [ + "GitHubOAuthType", + "GitHubOAuth", + "github_oauth_dependency", + "set_serialized_github_memberships", +] + GitHubOAuthType = TypeVar( "GitHubOAuthType", bound=authlib.integrations.starlette_client.integration.StarletteRemoteApp, @@ -49,3 +61,39 @@ async def __call__(self) -> GitHubOAuthType: """Path dependency that returns a configured `authlib.integrations.starlette_client.OAuth` instance for GitHub OAuth. """ + + +async def set_serialized_github_memberships( + *, + http_client: httpx.AsyncClient, + session: Dict[Any, Any], + github_token: str, +) -> None: + """Add JSON-serialized GitHub organization and team memberships to the + request session. + """ + # Stubs for configuration of GitHub teams and organizations relevant to + # authorization settings + relevant_orgs = ["jsickcodes"] + relevant_teams = [("jsickcodes", "proxy-team")] + + github_client = gidgethub.httpx.GitHubAPI( + http_client, "ltd-proxy", oauth_token=github_token + ) + + # Get all relevant organization memberships for the user + user_orgs: List[str] = [] + async for org in github_client.getiter("/user/memberships/orgs"): + if org["organization"]["login"] in relevant_orgs: + user_orgs.append(org["organization"]["login"]) + + # Get all relevant team memberships for the user + user_teams: List[Tuple[str, str]] = [] + async for team in github_client.getiter("/user/teams"): + team_id = (team["organization"]["login"], team["name"]) + if team_id in relevant_teams: + user_teams.append(team_id) + + # Serialize memberships to JSON to pack inside the session cookie + memberships = json.dumps({"orgs": user_orgs, "teams": user_teams}) + session["github_memberships"] = memberships diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index 1807b94..bc81531 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -17,7 +17,11 @@ from structlog.stdlib import BoundLogger from ltdproxy.config import config -from ltdproxy.githubauth import GitHubOAuthType, github_oauth_dependency +from ltdproxy.githubauth import ( + GitHubOAuthType, + github_oauth_dependency, + set_serialized_github_memberships, +) from ltdproxy.s3 import Bucket, bucket_dependency __all__ = ["get_s3", "external_router"] @@ -40,6 +44,7 @@ async def get_oauth_callback( request: Request, logger: BoundLogger = Depends(logger_dependency), github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), + http_client: httpx.AsyncClient = Depends(http_client_dependency), ) -> Union[RedirectResponse, HTMLResponse]: try: token = await github_oauth.authorize_access_token(request) @@ -53,6 +58,11 @@ async def get_oauth_callback( ) if github_token: request.session["github_token"] = github_token + set_serialized_github_memberships( + http_client=http_client, + session=request.session, + github_token=github_token, + ) return RedirectResponse(url="/ltdproxy/") From 2818ccbfa03d2a0d1a087acbd34d44f2b9c09c66 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Tue, 2 Nov 2021 16:39:02 -0400 Subject: [PATCH 11/26] Add pyyaml dependency Also add types-PyYAML dependency for mypy --- requirements/dev.in | 1 + requirements/dev.txt | 115 ++++++++++++++++++++++++------------------ requirements/main.in | 1 + requirements/main.txt | 10 ++-- 4 files changed, 74 insertions(+), 53 deletions(-) diff --git a/requirements/dev.in b/requirements/dev.in index 9e4b414..a7c1ca5 100644 --- a/requirements/dev.in +++ b/requirements/dev.in @@ -16,3 +16,4 @@ pytest pytest-asyncio pytest-cov uvicorn +types-PyYAML diff --git a/requirements/dev.txt b/requirements/dev.txt index 97c3198..c40f8a5 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -50,40 +50,53 @@ click==8.0.3 \ # via # -c requirements/main.txt # uvicorn -coverage[toml]==6.0.2 \ - --hash=sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1 \ - --hash=sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0 \ - --hash=sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9 \ - --hash=sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895 \ - --hash=sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d \ - --hash=sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe \ - --hash=sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2 \ - --hash=sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4 \ - --hash=sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce \ - --hash=sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9 \ - --hash=sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122 \ - --hash=sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7 \ - --hash=sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3 \ - --hash=sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff \ - --hash=sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149 \ - --hash=sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a \ - --hash=sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164 \ - --hash=sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1 \ - --hash=sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd \ - --hash=sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc \ - --hash=sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f \ - --hash=sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9 \ - --hash=sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9 \ - --hash=sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0 \ - --hash=sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d \ - --hash=sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa \ - --hash=sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7 \ - --hash=sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822 \ - --hash=sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc \ - --hash=sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7 \ - --hash=sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330 \ - --hash=sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb \ - --hash=sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24 +coverage[toml]==6.1.1 \ + --hash=sha256:0147f7833c41927d84f5af9219d9b32f875c0689e5e74ac8ca3cb61e73a698f9 \ + --hash=sha256:04a92a6cf9afd99f9979c61348ec79725a9f9342fb45e63c889e33c04610d97b \ + --hash=sha256:10ab138b153e4cc408b43792cb7f518f9ee02f4ff55cd1ab67ad6fd7e9905c7e \ + --hash=sha256:2e5b9c17a56b8bf0c0a9477fcd30d357deb486e4e1b389ed154f608f18556c8a \ + --hash=sha256:326d944aad0189603733d646e8d4a7d952f7145684da973c463ec2eefe1387c2 \ + --hash=sha256:359a32515e94e398a5c0fa057e5887a42e647a9502d8e41165cf5cb8d3d1ca67 \ + --hash=sha256:35cd2230e1ed76df7d0081a997f0fe705be1f7d8696264eb508076e0d0b5a685 \ + --hash=sha256:3b270c6b48d3ff5a35deb3648028ba2643ad8434b07836782b1139cf9c66313f \ + --hash=sha256:42a1fb5dee3355df90b635906bb99126faa7936d87dfc97eacc5293397618cb7 \ + --hash=sha256:479228e1b798d3c246ac89b09897ee706c51b3e5f8f8d778067f38db73ccc717 \ + --hash=sha256:4cd919057636f63ab299ccb86ea0e78b87812400c76abab245ca385f17d19fb5 \ + --hash=sha256:4d8b453764b9b26b0dd2afb83086a7c3f9379134e340288d2a52f8a91592394b \ + --hash=sha256:51a441011a30d693e71dea198b2a6f53ba029afc39f8e2aeb5b77245c1b282ef \ + --hash=sha256:557594a50bfe3fb0b1b57460f6789affe8850ad19c1acf2d14a3e12b2757d489 \ + --hash=sha256:572f917267f363101eec375c109c9c1118037c7cc98041440b5eabda3185ac7b \ + --hash=sha256:62512c0ec5d307f56d86504c58eace11c1bc2afcdf44e3ff20de8ca427ca1d0e \ + --hash=sha256:65ad3ff837c89a229d626b8004f0ee32110f9bfdb6a88b76a80df36ccc60d926 \ + --hash=sha256:666c6b32b69e56221ad1551d377f718ed00e6167c7a1b9257f780b105a101271 \ + --hash=sha256:6e994003e719458420e14ffb43c08f4c14990e20d9e077cb5cad7a3e419bbb54 \ + --hash=sha256:72bf437d54186d104388cbae73c9f2b0f8a3e11b6e8d7deb593bd14625c96026 \ + --hash=sha256:738e823a746841248b56f0f3bd6abf3b73af191d1fd65e4c723b9c456216f0ad \ + --hash=sha256:78287731e3601ea5ce9d6468c82d88a12ef8fe625d6b7bdec9b45d96c1ad6533 \ + --hash=sha256:7833c872718dc913f18e51ee97ea0dece61d9930893a58b20b3daf09bb1af6b6 \ + --hash=sha256:7e083d32965d2eb6638a77e65b622be32a094fdc0250f28ce6039b0732fbcaa8 \ + --hash=sha256:8186b5a4730c896cbe1e4b645bdc524e62d874351ae50e1db7c3e9f5dc81dc26 \ + --hash=sha256:8605add58e6a960729aa40c0fd9a20a55909dd9b586d3e8104cc7f45869e4c6b \ + --hash=sha256:977ce557d79577a3dd510844904d5d968bfef9489f512be65e2882e1c6eed7d8 \ + --hash=sha256:994ce5a7b3d20981b81d83618aa4882f955bfa573efdbef033d5632b58597ba9 \ + --hash=sha256:9ad5895938a894c368d49d8470fe9f519909e5ebc6b8f8ea5190bd0df6aa4271 \ + --hash=sha256:9eb0a1923354e0fdd1c8a6f53f5db2e6180d670e2b587914bf2e79fa8acfd003 \ + --hash=sha256:a00284dbfb53b42e35c7dd99fc0e26ef89b4a34efff68078ed29d03ccb28402a \ + --hash=sha256:a11a2c019324fc111485e79d55907e7289e53d0031275a6c8daed30690bc50c0 \ + --hash=sha256:ab6a0fe4c96f8058d41948ddf134420d3ef8c42d5508b5a341a440cce7a37a1d \ + --hash=sha256:b1d0a1bce919de0dd8da5cff4e616b2d9e6ebf3bd1410ff645318c3dd615010a \ + --hash=sha256:b8e4f15b672c9156c1154249a9c5746e86ac9ae9edc3799ee3afebc323d9d9e0 \ + --hash=sha256:bbca34dca5a2d60f81326d908d77313816fad23d11b6069031a3d6b8c97a54f9 \ + --hash=sha256:bf656cd74ff7b4ed7006cdb2a6728150aaad69c7242b42a2a532f77b63ea233f \ + --hash=sha256:c95257aa2ccf75d3d91d772060538d5fea7f625e48157f8ca44594f94d41cb33 \ + --hash=sha256:dc5023be1c2a8b0a0ab5e31389e62c28b2453eb31dd069f4b8d1a0f9814d951a \ + --hash=sha256:e14bceb1f3ae8a14374be2b2d7bc12a59226872285f91d66d301e5f41705d4d6 \ + --hash=sha256:e3c4f5211394cd0bf6874ac5d29684a495f9c374919833dcfff0bd6d37f96201 \ + --hash=sha256:e76f017b6d4140a038c5ff12be1581183d7874e41f1c0af58ecf07748d36a336 \ + --hash=sha256:e7d5606b9240ed4def9cbdf35be4308047d11e858b9c88a6c26974758d6225ce \ + --hash=sha256:f0f80e323a17af63eac6a9db0c9188c10f1fd815c3ab299727150cc0eb92c7a4 \ + --hash=sha256:fb2fa2f6506c03c48ca42e3fe5a692d7470d290c047ee6de7c0f3e5fa7639ac9 \ + --hash=sha256:ffa8fee2b1b9e60b531c4c27cf528d6b5d5da46b1730db1f4d6eee56ff282e07 # via # -r requirements/dev.in # pytest-cov @@ -91,9 +104,9 @@ distlib==0.3.3 \ --hash=sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31 \ --hash=sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05 # via virtualenv -filelock==3.3.1 \ - --hash=sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f \ - --hash=sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f +filelock==3.3.2 \ + --hash=sha256:7afc856f74fa7006a289fd10fa840e1eebd8bbff6bffb69c26c54a0512ea8cf8 \ + --hash=sha256:bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b # via virtualenv h11==0.12.0 \ --hash=sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6 \ @@ -133,9 +146,9 @@ httpx==0.20.0 \ # via # -c requirements/main.txt # -r requirements/dev.in -identify==2.3.1 \ - --hash=sha256:5a5000bd3293950d992843c0ef3d82b90a582de2161557bda7f493c8c8864f26 \ - --hash=sha256:8a92c56893e9a4ce951f09a50489986615e3eba7b4c60610e0b25f93ca4487ba +identify==2.3.3 \ + --hash=sha256:b9ffbeb7ed87e96ce017c66b80ca04fda3adbceb5c74e54fc7d99281d27d0859 \ + --hash=sha256:ffab539d9121b386ffdea84628ff3eefda15f520f392ce11b393b0a909632cdf # via pre-commit idna==3.3 \ --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ @@ -181,9 +194,9 @@ nodeenv==1.6.0 \ --hash=sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b \ --hash=sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7 # via pre-commit -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 +packaging==21.2 \ + --hash=sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966 \ + --hash=sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0 # via pytest platformdirs==2.4.0 \ --hash=sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2 \ @@ -201,9 +214,9 @@ py==1.10.0 \ --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a # via pytest -pyparsing==3.0.1 \ - --hash=sha256:84196357aa3566d64ad123d7a3c67b0e597a115c4934b097580e5ce220b91531 \ - --hash=sha256:fd93fc45c47893c300bd98f5dd1b41c0e783eaeb727e7cea210dcc09d64ce7c3 +pyparsing==2.4.7 \ + --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ + --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b # via packaging pytest==6.2.5 \ --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ @@ -294,6 +307,10 @@ tomli==1.2.2 \ --hash=sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee \ --hash=sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade # via coverage +types-pyyaml==6.0.0 \ + --hash=sha256:3d3591ddfc488fc30be3c506a0c0fe54da968fe98d8b76ab12e59d455330ffca \ + --hash=sha256:746f23d351245d176d7bc89eef79e2ee94b4e7306f7d23bfefb3dc946c0fb58d + # via -r requirements/dev.in typing-extensions==3.10.0.2 \ --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ @@ -327,9 +344,9 @@ uvloop==0.16.0 \ # via # -c requirements/main.txt # uvicorn -virtualenv==20.9.0 \ - --hash=sha256:1d145deec2da86b29026be49c775cc5a9aab434f85f7efef98307fb3965165de \ - --hash=sha256:bb55ace18de14593947354e5e6cd1be75fb32c3329651da62e92bf5d0aab7213 +virtualenv==20.10.0 \ + --hash=sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814 \ + --hash=sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218 # via pre-commit watchgod==0.7 \ --hash=sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29 \ diff --git a/requirements/main.in b/requirements/main.in index 4241513..68b278a 100644 --- a/requirements/main.in +++ b/requirements/main.in @@ -10,6 +10,7 @@ fastapi gunicorn starlette uvicorn[standard] +PyYAML # Other dependencies. safir diff --git a/requirements/main.txt b/requirements/main.txt index 283f073..a41b6f2 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -241,14 +241,16 @@ pyyaml==6.0 \ --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 - # via uvicorn + # via + # -r requirements/main.in + # uvicorn rfc3986[idna2008]==1.5.0 \ --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \ --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97 # via httpx -safir==2.1.0 \ - --hash=sha256:4e8094f58f61d0cc2ee75e3c17651735350dfcda3e389986c887fb2c5be34fd2 \ - --hash=sha256:e92b2e9226d185e34b11c18c00e38a1d9b362cbf1ba128379eab730cdfce4939 +safir==2.1.1 \ + --hash=sha256:6c33d7dfe9509fc6a14637c7078c107a941824d084bcd51a652dd7065d87efa4 \ + --hash=sha256:8de37d4e0d1de0175bb803ad0b9ae7810841241f4697ca4ffd35e3da77d10f83 # via -r requirements/main.in sniffio==1.2.0 \ --hash=sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663 \ From 17edd1c7e35fc5590223bfa6bb9ea1fac5a51c64 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Tue, 2 Nov 2021 21:20:11 -0400 Subject: [PATCH 12/26] Configure path to file with GitHub auth rules --- src/ltdproxy/config.py | 4 +++- tests/githubauth.example.yaml | 11 +++++++++++ tox.ini | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/githubauth.example.yaml diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index 4afdff6..b60b179 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -5,7 +5,7 @@ import os from enum import Enum -from pydantic import BaseSettings, Field, SecretStr +from pydantic import BaseSettings, Field, FilePath, SecretStr __all__ = ["Configuration", "config", "Profile", "LogLevel"] @@ -61,6 +61,8 @@ class Configuration(BaseSettings): session_key: SecretStr = Field(env="LTDPROXY_SESSION_KEY") + github_auth_config_path: FilePath = Field(env="LTDPROXY_AUTH_CONFIG") + config = Configuration(_env_file=os.getenv("LTD_PROXY_ENV")) """Configuration for ltd-proxy.""" diff --git a/tests/githubauth.example.yaml b/tests/githubauth.example.yaml new file mode 100644 index 0000000..e0a7cce --- /dev/null +++ b/tests/githubauth.example.yaml @@ -0,0 +1,11 @@ +default: + - org: "jsickcodes" +paths: + - pattern: "\/a\/" + authorized: + - org: "jsickcodes" + team: "Red Team" + - pattern: "\/b\/" + authorized: + - org: "jsickcodes" + team: "Blue Team" diff --git a/tox.ini b/tox.ini index 9c7b93b..68c6d4b 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ setenv = LTDPROXY_GITHUB_OAUTH_ID = foo LTDPROXY_GITHUB_OAUTH_SECRET = bar LTDPROXY_SESSION_KEY = 1234 + LTDPROXY_AUTH_CONFIG = tests/githubauth.example.yaml commands = pytest --cov=ltdproxy --cov-branch --cov-report= {posargs} From d4de567fabc0642b8bed6a7bc1b100891ba05eec Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 13:42:11 -0400 Subject: [PATCH 13/26] Add GitHubAuth The purpose of this class is to parse the github auth configuration file, and then judge if a user's cookie has the appropriate memberships to allow a user to access a given URL path. --- src/ltdproxy/githubauth.py | 205 ++++++++++++++++++++++++++++++++++++- tests/githubauth_test.py | 135 ++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 5 deletions(-) create mode 100644 tests/githubauth_test.py diff --git a/src/ltdproxy/githubauth.py b/src/ltdproxy/githubauth.py index dd0c9c0..23166f2 100644 --- a/src/ltdproxy/githubauth.py +++ b/src/ltdproxy/githubauth.py @@ -3,15 +3,30 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Any, Dict, List, Tuple, TypeVar +from enum import Enum +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Optional, + Pattern, + Set, + Tuple, + TypeVar, +) import authlib.integrations.starlette_client.integration import gidgethub.httpx +import yaml from authlib.integrations.starlette_client import OAuth +from pydantic import BaseModel from ltdproxy.config import config if TYPE_CHECKING: + from pathlib import Path + import httpx __all__ = [ @@ -72,10 +87,10 @@ async def set_serialized_github_memberships( """Add JSON-serialized GitHub organization and team memberships to the request session. """ - # Stubs for configuration of GitHub teams and organizations relevant to - # authorization settings - relevant_orgs = ["jsickcodes"] - relevant_teams = [("jsickcodes", "proxy-team")] + # These orgs and teams are mentioned in the GitHub Auth configuration, + # and therefore are ones to pay attention to in the cookie. + relevant_orgs = github_auth.relevant_orgs + relevant_teams = github_auth.relevant_teams github_client = gidgethub.httpx.GitHubAPI( http_client, "ltd-proxy", oauth_token=github_token @@ -97,3 +112,183 @@ async def set_serialized_github_memberships( # Serialize memberships to JSON to pack inside the session cookie memberships = json.dumps({"orgs": user_orgs, "teams": user_teams}) session["github_memberships"] = memberships + + +class GitHubGroup(BaseModel): + """A model for a GitHub group configuration, either an entire organization + or a team within an organization. + """ + + org: str + """A GitHub organization's slug.""" + + team: Optional[str] = None + """The name of a team within an organization.""" + + @property + def is_team(self) -> bool: + if self.team: + return True + else: + return False + + +class PathRule(BaseModel): + """A model for a URL path and authorized entities.""" + + pattern: Pattern + """Regular expression pattern that matches a path.""" + + authorized: List[GitHubGroup] + """A list fo GitHub groups (teams and/or organizations) that are + authorized to access this path. + """ + + def path_matches(self, url_path: str) -> bool: + """Test if a URL path matches the rule's patten.""" + if self.pattern.match(url_path): + return True + else: + return False + + def is_user_authorized( + self, *, user_orgs: List[str], user_teams: List[Tuple[str, str]] + ) -> bool: + """Test if a user is authorized for this path. + + The parameters come from the ``github_memberships`` attribute of + the session cookie, after parsing from JSON. + """ + for authorized_group in self.authorized: + if authorized_group.is_team: + authorized_team_id = ( + authorized_group.org, + authorized_group.team, + ) + if authorized_team_id in user_teams: + return True + else: + if authorized_group.org in user_orgs: + return True + + # no matches + return False + + +class AuthResult(str, Enum): + """The authentication/authorization result.""" + + authorized = "authorized" + unauthorized = "unauthorized" + unauthenticated = "unauthenticated" + + +class GitHubAuth(BaseModel): + """A model for the GitHubAuth configuration file, with methods for + determining if a requester is authorized to view a given path. + """ + + default: List[GitHubGroup] + """Default authorized groups if a path does not match.""" + + paths: List[PathRule] + """A list of path expressions and the groups that are authorized to + access those paths. + """ + + @classmethod + def parse_yaml(cls, path: Path) -> GitHubAuth: + """Parse the YAML representation of this configuration model.""" + data = yaml.safe_load(path.read_text()) + return cls.parse_obj(data) + + def is_user_authorized( + self, + *, + url_path: str, + user_orgs: List[str], + user_teams: List[Tuple[str, str]], + ) -> bool: + for path_rule in self.paths: + if path_rule.path_matches(url_path): + if path_rule.is_user_authorized( + user_orgs=user_orgs, user_teams=user_teams + ): + return True + else: + return False + + # Fallback to the default authorizations + for authed_group in self.default: + if authed_group.is_team: + authorized_team_id = (authed_group.org, authed_group.team) + if authorized_team_id in user_teams: + return True + else: + if authed_group.org in user_orgs: + return True + + return False + + def is_session_authorized( + self, *, path: str, session: Dict[Any, Any] + ) -> AuthResult: + try: + github_memberships_data = session["github_memberships"] + except KeyError: + return AuthResult.unauthenticated + + parsed_memberships = json.loads(github_memberships_data) + user_orgs = parsed_memberships["orgs"] + # This typechecks/validates the teams data structure + user_teams = [ + (str(t[0]), str(t[1])) for t in parsed_memberships["teams"] + ] + if self.is_user_authorized( + url_path=path, user_orgs=user_orgs, user_teams=user_teams + ): + return AuthResult.authorized + else: + return AuthResult.unauthorized + + @property + def relevant_orgs(self) -> Set[str]: + """Get all GitHub organizations mentioned in the configuration.""" + all_orgs: Set[str] = set() + + for github_group in self.default: + if not github_group.is_team: + all_orgs.add(github_group.org) + + for path_rule in self.paths: + for github_group in path_rule.authorized: + if not github_group.is_team: + all_orgs.add(github_group.org) + + return all_orgs + + @property + def relevant_teams(self) -> Set[Tuple[str, str]]: + """Get all GitHub teams mentioned in the configuration.""" + all_teams: Set[Tuple[str, str]] = set() + + for github_group in self.default: + if github_group.is_team: + assert isinstance(github_group.team, str) # mypy cue + all_teams.add((github_group.org, github_group.team)) + + for path_rule in self.paths: + for github_group in path_rule.authorized: + if github_group.is_team: + assert isinstance(github_group.team, str) # mypy cue + all_teams.add((github_group.org, github_group.team)) + + return all_teams + + +github_auth = GitHubAuth.parse_yaml(config.github_auth_config_path) +"""FastAPI dependency providing the GitHub auth rules for different paths.""" + + +async def github_auth_dependency() -> GitHubAuth: + return github_auth diff --git a/tests/githubauth_test.py b/tests/githubauth_test.py new file mode 100644 index 0000000..08e7357 --- /dev/null +++ b/tests/githubauth_test.py @@ -0,0 +1,135 @@ +"""Test the githubauth module.""" + +from __future__ import annotations + +import json +from pathlib import Path + +from ltdproxy.githubauth import AuthResult, GitHubAuth, GitHubGroup, PathRule + + +def test_path_rule() -> None: + org_group = GitHubGroup(org="jsickcodes") + rule1 = PathRule(pattern=r"\/a\/", authorized=[org_group]) + assert rule1.path_matches("/a/hello-world.html") + assert not rule1.path_matches("/b/hello-world.html") + + +def test_githubauth_example_yaml() -> None: + """Test GitHubAuth class with the example YAML file, + tests/githubauth.example.yaml. + """ + example_path = Path(__file__).parent / "githubauth.example.yaml" + assert example_path.is_file() + + github_auth = GitHubAuth.parse_yaml(example_path) + + assert github_auth.relevant_orgs == set(["jsickcodes"]) + assert github_auth.relevant_teams == set( + [ + ("jsickcodes", "Red Team"), + ("jsickcodes", "Blue Team"), + ] + ) + + # Testing the default rule + assert ( + github_auth.is_user_authorized( + url_path="/xyz", user_orgs=["jsickcodes"], user_teams=[] + ) + is True + ) + + # Testing the default rule + assert ( + github_auth.is_user_authorized( + url_path="/xyz", user_orgs=["jsickwrites"], user_teams=[] + ) + is False + ) + + # Testing the path rule for /a/ + assert ( + github_auth.is_user_authorized( + url_path="/a/index.html", user_orgs=["jsickcodes"], user_teams=[] + ) + is False + ) + assert ( + github_auth.is_user_authorized( + url_path="/a/index.html", + user_orgs=["jsickcodes"], + user_teams=[("jsickcodes", "Blue Team")], + ) + is False + ) + assert ( + github_auth.is_user_authorized( + url_path="/a/index.html", + user_orgs=["jsickcodes"], + user_teams=[ + ("jsickcodes", "Red Team"), + ("jsickcodes", "Blue Team"), + ], + ) + is True + ) + + # Test if the session auth cookie is empty + assert ( + github_auth.is_session_authorized(path="/xyz", session={}) + is AuthResult.unauthenticated + ) + + # Test if the session auth cookie doesn't have the right membership + assert ( + github_auth.is_session_authorized( + path="/xyz", + session={ + "github_memberships": json.dumps( + {"orgs": "acompany", "teams": []} + ) + }, + ) + is AuthResult.unauthorized + ) + + # Test if the session auth cookie *does* have the right membership + assert ( + github_auth.is_session_authorized( + path="/xyz", + session={ + "github_memberships": json.dumps( + {"orgs": "jsickcodes", "teams": []} + ) + }, + ) + is AuthResult.authorized + ) + + assert ( + github_auth.is_session_authorized( + path="/a/hello", + session={ + "github_memberships": json.dumps( + { + "orgs": "jsickcodes", + "teams": [["jsickcodes", "Red Team"]], + } + ) + }, + ) + is AuthResult.authorized + ) + + assert ( + github_auth.is_session_authorized( + path="/a/hello", + session={ + "github_memberships": json.dumps( + {"orgs": "jsickcodes", "teams": []} + ) + }, + ) + is AuthResult.unauthorized + ) From f8bad487e8344b33b18fad88ae482e494088999d Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 15:37:41 -0400 Subject: [PATCH 14/26] Implement GitHub auth on the proxy handler --- src/ltdproxy/handlers/external.py | 57 +++++++++++++++++++------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index bc81531..61fabad 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -4,7 +4,7 @@ import httpx from authlib.integrations.starlette_client import OAuthError -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from safir.dependencies.http_client import http_client_dependency from safir.dependencies.logger import logger_dependency from starlette.background import BackgroundTask @@ -18,7 +18,10 @@ from ltdproxy.config import config from ltdproxy.githubauth import ( + AuthResult, + GitHubAuth, GitHubOAuthType, + github_auth_dependency, github_oauth_dependency, set_serialized_github_memberships, ) @@ -30,7 +33,7 @@ """FastAPI router for all external handlers.""" -@external_router.get("/") +@external_router.get("/", name="homepage") async def homepage(request: Request) -> HTMLResponse: github_token = request.session.get("github_token") if github_token: @@ -39,7 +42,7 @@ async def homepage(request: Request) -> HTMLResponse: return HTMLResponse('login') -@external_router.get("/auth") +@external_router.get("/auth", name="get_oauth_callback") async def get_oauth_callback( request: Request, logger: BoundLogger = Depends(logger_dependency), @@ -58,7 +61,7 @@ async def get_oauth_callback( ) if github_token: request.session["github_token"] = github_token - set_serialized_github_memberships( + await set_serialized_github_memberships( http_client=http_client, session=request.session, github_token=github_token, @@ -66,7 +69,7 @@ async def get_oauth_callback( return RedirectResponse(url="/ltdproxy/") -@external_router.get("/login") +@external_router.get("/login", name="login") async def login( request: Request, logger: BoundLogger = Depends(logger_dependency), @@ -78,37 +81,49 @@ async def login( return await github_oauth.authorize_redirect(request, redirect_uri) -@external_router.get("/logout") +@external_router.get("/logout", name="logout") async def logout( request: Request, logger: BoundLogger = Depends(logger_dependency), ) -> RedirectResponse: request.session.pop("github_token", None) + request.session.pop("github_memberships", None) logger.info("Logged out") return RedirectResponse(url="/ltdproxy/") @external_router.get( - "/{path:path}", - description="The S3 front-end proxy.", + "/{path:path}", description="The S3 front-end proxy.", name="proxy" ) async def get_s3( path: str, + request: Request, logger: BoundLogger = Depends(logger_dependency), bucket: Bucket = Depends(bucket_dependency), http_client: httpx.AsyncClient = Depends(http_client_dependency), -) -> StreamingResponse: + github_auth: GitHubAuth = Depends(github_auth_dependency), +) -> Union[StreamingResponse, RedirectResponse]: """The S3 proxy endpoint.""" - bucket_path = f"{config.s3_bucket_prefix}{path}" - stream = await bucket.stream_object(http_client, bucket_path) - logger.info("stream headers", headers=stream.headers) - response_headers = { - "Content-type": stream.headers["Content-type"], - "Content-length": stream.headers["Content-length"], - "Etag": stream.headers["Etag"], - } - return StreamingResponse( - stream.aiter_raw(), - background=BackgroundTask(stream.aclose), - headers=response_headers, + github_auth_result = github_auth.is_session_authorized( + path=f"/{path}", session=request.session ) + if github_auth_result == AuthResult.unauthenticated: + return RedirectResponse(url=request.url_for("login")) + elif github_auth_result == AuthResult.unauthorized: + raise HTTPException(status_code=403, detail="Not authorized") + elif github_auth_result == AuthResult.authorized: + bucket_path = f"{config.s3_bucket_prefix}{path}" + stream = await bucket.stream_object(http_client, bucket_path) + logger.info("stream headers", headers=stream.headers) + response_headers = { + "Content-type": stream.headers["Content-type"], + "Content-length": stream.headers["Content-length"], + "Etag": stream.headers["Etag"], + } + return StreamingResponse( + stream.aiter_raw(), + background=BackgroundTask(stream.aclose), + headers=response_headers, + ) + else: + raise HTTPException(status_code=500, detail="Internal auth error") From e96a1a45c303753a97615c53a8cb167410368f74 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 16:36:43 -0400 Subject: [PATCH 15/26] Drop tests for internal handlers We'll make these internal handlers optional to make it possible to serve from the application root (and hence the internal handlers won't exist). We'll re-add these types of tests once the health check endpoints are added again. --- tests/handlers/__init__.py | 0 tests/handlers/internal_test.py | 25 ------------------------- 2 files changed, 25 deletions(-) delete mode 100644 tests/handlers/__init__.py delete mode 100644 tests/handlers/internal_test.py diff --git a/tests/handlers/__init__.py b/tests/handlers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/handlers/internal_test.py b/tests/handlers/internal_test.py deleted file mode 100644 index bc818fc..0000000 --- a/tests/handlers/internal_test.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Tests for the ltdproxy.handlers.internal module and routes.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -import pytest - -from ltdproxy.config import config - -if TYPE_CHECKING: - from httpx import AsyncClient - - -@pytest.mark.asyncio -async def test_get_index(client: AsyncClient) -> None: - """Test ``GET /``""" - response = await client.get("/") - assert response.status_code == 200 - data = response.json() - assert data["name"] == config.name - assert isinstance(data["version"], str) - assert isinstance(data["description"], str) - assert isinstance(data["repository_url"], str) - assert isinstance(data["documentation_url"], str) From 935bec0b986867d6a8f8929ec7c55bec1b8699eb Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 16:39:51 -0400 Subject: [PATCH 16/26] Add path_prefix configuration to serve from root This configuration lets us break from the "Safir" app pattern and now serve from the application root path with only the "external" routes, or serve both internal and external routes if a non-trivial prefix path is set. --- src/ltdproxy/appsetup.py | 29 +++++++++++++++++++++++++++++ src/ltdproxy/config.py | 2 ++ src/ltdproxy/main.py | 22 ++++------------------ 3 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 src/ltdproxy/appsetup.py diff --git a/src/ltdproxy/appsetup.py b/src/ltdproxy/appsetup.py new file mode 100644 index 0000000..42f1896 --- /dev/null +++ b/src/ltdproxy/appsetup.py @@ -0,0 +1,29 @@ +"""Configuration for the app.""" + +from __future__ import annotations + +from importlib.metadata import metadata +from typing import TYPE_CHECKING + +from .handlers.external import external_router +from .handlers.internal import internal_router + +if TYPE_CHECKING: + from fastapi import FastAPI + + from ltdproxy.config import Configuration + + +def add_handlers(*, config: Configuration, app: FastAPI) -> None: + if config.path_prefix == "/": + app.include_router(external_router) + else: + external_app = FastAPI( + title="ltd-proxy", + description=metadata("ltd-proxy").get("Summary", ""), + version=metadata("ltd-proxy").get("Version", "0.0.0"), + ) + external_app.include_router(external_router) + + app.include_router(internal_router) + app.mount(f"{config.path_prefix}", external_app) diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index b60b179..df6095e 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -63,6 +63,8 @@ class Configuration(BaseSettings): github_auth_config_path: FilePath = Field(env="LTDPROXY_AUTH_CONFIG") + path_prefix: str = Field("/", env="LTDPROXY_PATH_PREFIX") + config = Configuration(_env_file=os.getenv("LTD_PROXY_ENV")) """Configuration for ltd-proxy.""" diff --git a/src/ltdproxy/main.py b/src/ltdproxy/main.py index f494806..4833fef 100644 --- a/src/ltdproxy/main.py +++ b/src/ltdproxy/main.py @@ -7,21 +7,17 @@ called. """ -from importlib.metadata import metadata - from fastapi import FastAPI from safir.dependencies.http_client import http_client_dependency from safir.logging import configure_logging from safir.middleware.x_forwarded import XForwardedMiddleware from starlette.middleware.sessions import SessionMiddleware +from .appsetup import add_handlers from .config import config -from .handlers.external import external_router -from .handlers.internal import internal_router __all__ = ["app", "config"] - configure_logging( profile=config.profile, log_level=config.log_level, @@ -31,22 +27,12 @@ app = FastAPI() """The main FastAPI application for ltd-proxy.""" -# Define the external routes in a subapp so that it will serve its own OpenAPI -# interface definition and documentation URLs under the external URL. -external_app = FastAPI( - title="ltd-proxy", - description=metadata("ltd-proxy").get("Summary", ""), - version=metadata("ltd-proxy").get("Version", "0.0.0"), -) -external_app.include_router(external_router) -external_app.add_middleware( +add_handlers(app=app, config=config) + +app.add_middleware( SessionMiddleware, secret_key=config.session_key.get_secret_value() ) -# Attach the internal routes and subapp to the main application. -app.include_router(internal_router) -app.mount(f"/{config.name}", external_app) - @app.on_event("startup") async def startup_event() -> None: From 2d4faad8fd0be16a705129a471432269e0e5df15 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 16:41:25 -0400 Subject: [PATCH 17/26] Compute redirect URLs using request.url_for Needed since the app can now be served from both a prefix path or the root. --- src/ltdproxy/handlers/external.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index 61fabad..be00356 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -37,9 +37,12 @@ async def homepage(request: Request) -> HTMLResponse: github_token = request.session.get("github_token") if github_token: - html = "

hello!

" 'logout' + html = ( + "

hello!

" + f'logout' + ) return HTMLResponse(html) - return HTMLResponse('login') + return HTMLResponse(f'login') @external_router.get("/auth", name="get_oauth_callback") @@ -53,8 +56,6 @@ async def get_oauth_callback( token = await github_oauth.authorize_access_token(request) except OAuthError as error: return HTMLResponse(f"

{error.error}

") - print(token) - print(type(token)) github_token = token.get("access_token") logger.info( "Got github oauth token", token=token, access_token=github_token @@ -66,7 +67,7 @@ async def get_oauth_callback( session=request.session, github_token=github_token, ) - return RedirectResponse(url="/ltdproxy/") + return RedirectResponse(url=request.url_for("homepage")) @external_router.get("/login", name="login") @@ -76,7 +77,7 @@ async def login( github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), ) -> RedirectResponse: # redirect_uri = request.url_for('get_oauth_callback') - redirect_uri = "http://127.0.0.1:8000/ltdproxy/auth" + redirect_uri = "http://127.0.0.1:8000/auth" # FIXME add config logger.info("Redirecting to GitHub auth", callback_url=redirect_uri) return await github_oauth.authorize_redirect(request, redirect_uri) @@ -89,7 +90,7 @@ async def logout( request.session.pop("github_token", None) request.session.pop("github_memberships", None) logger.info("Logged out") - return RedirectResponse(url="/ltdproxy/") + return RedirectResponse(url=request.url_for("homepage")) @external_router.get( From bbe8b33ebd300be57af9ee9d519627f22ffe69fc Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Wed, 3 Nov 2021 16:48:46 -0400 Subject: [PATCH 18/26] Add configuration for the GitHub OAuth callback --- src/ltdproxy/config.py | 6 +++++- src/ltdproxy/handlers/external.py | 3 +-- tox.ini | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ltdproxy/config.py b/src/ltdproxy/config.py index df6095e..5ca1c2c 100644 --- a/src/ltdproxy/config.py +++ b/src/ltdproxy/config.py @@ -5,7 +5,7 @@ import os from enum import Enum -from pydantic import BaseSettings, Field, FilePath, SecretStr +from pydantic import BaseSettings, Field, FilePath, HttpUrl, SecretStr __all__ = ["Configuration", "config", "Profile", "LogLevel"] @@ -59,6 +59,10 @@ class Configuration(BaseSettings): env="LTDPROXY_GITHUB_OAUTH_SECRET" ) + github_oauth_callback_url: HttpUrl = Field( + env="LTDPROXY_GITHUB_CALLBACK_URL" + ) + session_key: SecretStr = Field(env="LTDPROXY_SESSION_KEY") github_auth_config_path: FilePath = Field(env="LTDPROXY_AUTH_CONFIG") diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index be00356..fb22b8c 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -76,8 +76,7 @@ async def login( logger: BoundLogger = Depends(logger_dependency), github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), ) -> RedirectResponse: - # redirect_uri = request.url_for('get_oauth_callback') - redirect_uri = "http://127.0.0.1:8000/auth" # FIXME add config + redirect_uri = str(config.github_oauth_callback_url) logger.info("Redirecting to GitHub auth", callback_url=redirect_uri) return await github_oauth.authorize_redirect(request, redirect_uri) diff --git a/tox.ini b/tox.ini index 68c6d4b..8b69a31 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,7 @@ setenv = LTDPROXY_AWS_SECRET_ACCESS_KEY = bar LTDPROXY_GITHUB_OAUTH_ID = foo LTDPROXY_GITHUB_OAUTH_SECRET = bar + LTDPROXY_GITHUB_CALLBACK_URL = http://127.0.0.1:8000/auth LTDPROXY_SESSION_KEY = 1234 LTDPROXY_AUTH_CONFIG = tests/githubauth.example.yaml commands = From b9cdb58daaa7dbf2bce415b121477123e1360a52 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 12:35:53 -0400 Subject: [PATCH 19/26] Redirect users to the original page after login --- src/ltdproxy/handlers/external.py | 49 +++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index fb22b8c..950539c 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -1,6 +1,7 @@ """Handlers for the app's external root, ``/ltdproxy/``.""" -from typing import Union +from typing import Optional, Union +from urllib.parse import urlencode, urlparse import httpx from authlib.integrations.starlette_client import OAuthError @@ -47,6 +48,7 @@ async def homepage(request: Request) -> HTMLResponse: @external_router.get("/auth", name="get_oauth_callback") async def get_oauth_callback( + ref: Optional[str], request: Request, logger: BoundLogger = Depends(logger_dependency), github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), @@ -67,22 +69,46 @@ async def get_oauth_callback( session=request.session, github_token=github_token, ) - return RedirectResponse(url=request.url_for("homepage")) + + # Compute redirect URL + if ref: + # The original callback URL included the "ref" query parameter with + # the referring page's URL. We'll redirect to that. + redirect_url = ref + else: + # Default redirect. + redirect_url = request.url_for("homepage") + + return RedirectResponse(url=redirect_url) @external_router.get("/login", name="login") -async def login( +async def get_login( + ref: Optional[str], request: Request, logger: BoundLogger = Depends(logger_dependency), github_oauth: GitHubOAuthType = Depends(github_oauth_dependency), ) -> RedirectResponse: + """Log a user in by redirecting to GitHub OAuth.""" redirect_uri = str(config.github_oauth_callback_url) + if ref: + # The ref query string can be set to point to the page that + # asked for the login. + # Make sure return return url is in same domain as this request + # (i.e., only redirect when on same site) + if urlparse(ref).netloc == request.url.netloc: + redirect_uri = ( + urlparse(redirect_uri) + ._replace(query=urlencode({"ref": ref}, doseq=True)) + .geturl() + ) + logger.info("Redirecting to GitHub auth", callback_url=redirect_uri) return await github_oauth.authorize_redirect(request, redirect_uri) @external_router.get("/logout", name="logout") -async def logout( +async def get_logout( request: Request, logger: BoundLogger = Depends(logger_dependency), ) -> RedirectResponse: @@ -107,11 +133,22 @@ async def get_s3( github_auth_result = github_auth.is_session_authorized( path=f"/{path}", session=request.session ) + if github_auth_result == AuthResult.unauthenticated: - return RedirectResponse(url=request.url_for("login")) + # User is not authenticated so redirect to the login page with + # this page's URL as the ref query string so they'll get redirect + # back here after login. + login_url = request.url_for("login") + ref_qs = urlencode({"ref": request.url}, doseq=True) + login_url = urlparse(login_url)._replace(query=ref_qs).geturl() + return RedirectResponse(url=login_url) + elif github_auth_result == AuthResult.unauthorized: + # User is not authorized. raise HTTPException(status_code=403, detail="Not authorized") + elif github_auth_result == AuthResult.authorized: + # User is authorized; stream from S3. bucket_path = f"{config.s3_bucket_prefix}{path}" stream = await bucket.stream_object(http_client, bucket_path) logger.info("stream headers", headers=stream.headers) @@ -125,5 +162,7 @@ async def get_s3( background=BackgroundTask(stream.aclose), headers=response_headers, ) + else: + # Catch-all error raise HTTPException(status_code=500, detail="Internal auth error") From 02b8234d5f1408d85e4cc42b9d9c65096b60fea3 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 14:31:17 -0400 Subject: [PATCH 20/26] Remove default homepage Now / is send to the proxy endpoint. Since there isn't an explicit homepage that's publicly available: - Make logout redirect to a special logout page that is publicly available. - Make the default redirect from the oauth callback just redirect to "/" if a ref page isn't set. --- src/ltdproxy/handlers/external.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index 950539c..5f7c380 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -34,18 +34,6 @@ """FastAPI router for all external handlers.""" -@external_router.get("/", name="homepage") -async def homepage(request: Request) -> HTMLResponse: - github_token = request.session.get("github_token") - if github_token: - html = ( - "

hello!

" - f'logout' - ) - return HTMLResponse(html) - return HTMLResponse(f'login') - - @external_router.get("/auth", name="get_oauth_callback") async def get_oauth_callback( ref: Optional[str], @@ -77,7 +65,7 @@ async def get_oauth_callback( redirect_url = ref else: # Default redirect. - redirect_url = request.url_for("homepage") + redirect_url = request.url_for("/") return RedirectResponse(url=redirect_url) @@ -115,7 +103,17 @@ async def get_logout( request.session.pop("github_token", None) request.session.pop("github_memberships", None) logger.info("Logged out") - return RedirectResponse(url=request.url_for("homepage")) + return RedirectResponse(url=request.url_for("logged-out")) + + +@external_router.get("/logged-out", name="logged-out") +async def get_logged_out( + request: Request, +) -> Union[HTMLResponse, RedirectResponse]: + if "github_memberships" in request.session: + # Not actually logged out yet so redirect to /logout first + return RedirectResponse(url=request.url_for("logout")) + return HTMLResponse("

You're logged out.

") @external_router.get( From 20530d34b6d74861b2875b4aee102e97b6a9fac2 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 14:34:11 -0400 Subject: [PATCH 21/26] Implement implicit index.html rewrites on S3 Make */ URLs get the */index.html object from the S3 bucket. --- src/ltdproxy/handlers/external.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index 5f7c380..de6f6a1 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -147,7 +147,11 @@ async def get_s3( elif github_auth_result == AuthResult.authorized: # User is authorized; stream from S3. - bucket_path = f"{config.s3_bucket_prefix}{path}" + if path == "" or path.endswith("/"): + # redwrite "*/" as "*/index.html" for static sites in S3 + bucket_path = f"{config.s3_bucket_prefix}{path}index.html" + else: + bucket_path = f"{config.s3_bucket_prefix}{path}" stream = await bucket.stream_object(http_client, bucket_path) logger.info("stream headers", headers=stream.headers) response_headers = { From ffe2597da5071bf26b1df5b5c46b90c1d2115fc0 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 14:37:00 -0400 Subject: [PATCH 22/26] Handle 404 status from S3 Eventually we should implement templated error pages, but this works for now. --- src/ltdproxy/handlers/external.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index de6f6a1..edfbf69 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -153,6 +153,8 @@ async def get_s3( else: bucket_path = f"{config.s3_bucket_prefix}{path}" stream = await bucket.stream_object(http_client, bucket_path) + if stream.status_code == 404: + raise HTTPException(status_code=404, detail="Does not exist.") logger.info("stream headers", headers=stream.headers) response_headers = { "Content-type": stream.headers["Content-type"], From 543cbb9c4c80cd231ef43571990fae2911e89cf9 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 14:38:36 -0400 Subject: [PATCH 23/26] Shift logging to debug level --- src/ltdproxy/handlers/external.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ltdproxy/handlers/external.py b/src/ltdproxy/handlers/external.py index edfbf69..c5a257e 100644 --- a/src/ltdproxy/handlers/external.py +++ b/src/ltdproxy/handlers/external.py @@ -47,7 +47,7 @@ async def get_oauth_callback( except OAuthError as error: return HTMLResponse(f"

{error.error}

") github_token = token.get("access_token") - logger.info( + logger.debug( "Got github oauth token", token=token, access_token=github_token ) if github_token: @@ -91,7 +91,7 @@ async def get_login( .geturl() ) - logger.info("Redirecting to GitHub auth", callback_url=redirect_uri) + logger.debug("Redirecting to GitHub auth", callback_url=redirect_uri) return await github_oauth.authorize_redirect(request, redirect_uri) @@ -102,7 +102,7 @@ async def get_logout( ) -> RedirectResponse: request.session.pop("github_token", None) request.session.pop("github_memberships", None) - logger.info("Logged out") + logger.debug("Logged out") return RedirectResponse(url=request.url_for("logged-out")) @@ -155,7 +155,7 @@ async def get_s3( stream = await bucket.stream_object(http_client, bucket_path) if stream.status_code == 404: raise HTTPException(status_code=404, detail="Does not exist.") - logger.info("stream headers", headers=stream.headers) + logger.debug("stream headers", headers=stream.headers) response_headers = { "Content-type": stream.headers["Content-type"], "Content-length": stream.headers["Content-length"], From a373867698d27b006ba64b4c38fc2c2ee3531c10 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 14:41:26 -0400 Subject: [PATCH 24/26] Disable OpenAPI docs These aren't needed for this application. --- src/ltdproxy/appsetup.py | 1 + src/ltdproxy/main.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ltdproxy/appsetup.py b/src/ltdproxy/appsetup.py index 42f1896..196a49c 100644 --- a/src/ltdproxy/appsetup.py +++ b/src/ltdproxy/appsetup.py @@ -22,6 +22,7 @@ def add_handlers(*, config: Configuration, app: FastAPI) -> None: title="ltd-proxy", description=metadata("ltd-proxy").get("Summary", ""), version=metadata("ltd-proxy").get("Version", "0.0.0"), + openapi_url=None, ) external_app.include_router(external_router) diff --git a/src/ltdproxy/main.py b/src/ltdproxy/main.py index 4833fef..a3d12b9 100644 --- a/src/ltdproxy/main.py +++ b/src/ltdproxy/main.py @@ -24,7 +24,7 @@ name=config.logger_name, ) -app = FastAPI() +app = FastAPI(openapi_url=None) """The main FastAPI application for ltd-proxy.""" add_handlers(app=app, config=config) From 6695cdfae2efa6d4ce6ae6d54c3843347c22d1bf Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 15:19:08 -0400 Subject: [PATCH 25/26] Add a health check endpoint for Kubernetes This endpoint is always at the /__healthz path, regardless of whether the app is serving from a path prefix or not. --- src/ltdproxy/appsetup.py | 3 +++ src/ltdproxy/handlers/healthcheck.py | 11 +++++++++++ tests/handlers/__init__.py | 1 + tests/handlers/healthcheck_test.py | 17 +++++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 src/ltdproxy/handlers/healthcheck.py create mode 100644 tests/handlers/__init__.py create mode 100644 tests/handlers/healthcheck_test.py diff --git a/src/ltdproxy/appsetup.py b/src/ltdproxy/appsetup.py index 196a49c..7502ca3 100644 --- a/src/ltdproxy/appsetup.py +++ b/src/ltdproxy/appsetup.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING from .handlers.external import external_router +from .handlers.healthcheck import health_router from .handlers.internal import internal_router if TYPE_CHECKING: @@ -16,6 +17,7 @@ def add_handlers(*, config: Configuration, app: FastAPI) -> None: if config.path_prefix == "/": + app.include_router(health_router) app.include_router(external_router) else: external_app = FastAPI( @@ -27,4 +29,5 @@ def add_handlers(*, config: Configuration, app: FastAPI) -> None: external_app.include_router(external_router) app.include_router(internal_router) + app.include_router(health_router) app.mount(f"{config.path_prefix}", external_app) diff --git a/src/ltdproxy/handlers/healthcheck.py b/src/ltdproxy/handlers/healthcheck.py new file mode 100644 index 0000000..bea2efc --- /dev/null +++ b/src/ltdproxy/handlers/healthcheck.py @@ -0,0 +1,11 @@ +"""Handler for the Kubernetes health check.""" + +from fastapi import APIRouter +from starlette.responses import PlainTextResponse + +health_router = APIRouter() + + +@health_router.get("/__healthz", name="healthz") +def healthy() -> PlainTextResponse: + return PlainTextResponse("OK", status_code=200) diff --git a/tests/handlers/__init__.py b/tests/handlers/__init__.py new file mode 100644 index 0000000..661b93e --- /dev/null +++ b/tests/handlers/__init__.py @@ -0,0 +1 @@ +"""Tests for handlers.""" diff --git a/tests/handlers/healthcheck_test.py b/tests/handlers/healthcheck_test.py new file mode 100644 index 0000000..eeb3abb --- /dev/null +++ b/tests/handlers/healthcheck_test.py @@ -0,0 +1,17 @@ +"""Test the healthcheck endpoint.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +if TYPE_CHECKING: + from httpx import AsyncClient + + +@pytest.mark.asyncio +async def test_get_healthz(client: AsyncClient) -> None: + """Test ``GET /__healthz``""" + response = await client.get("/__healthz") + assert response.status_code == 200 From 4945071f439a5c2e6f17c47194f6fe0b62381585 Mon Sep 17 00:00:00 2001 From: Jonathan Sick Date: Thu, 4 Nov 2021 16:04:43 -0400 Subject: [PATCH 26/26] Add additional configurations manifests This includes stub for the auth rules YAML file config map, which is mounted onto the pod's filesystem. --- README.rst | 26 +++++++++++++++++++++++++- manifests/base/auth-configmap.yaml | 16 ++++++++++++++++ manifests/base/configmap.yaml | 4 ++++ manifests/base/deployment.yaml | 10 +++++++++- manifests/base/kustomization.yaml | 1 + 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 manifests/base/auth-configmap.yaml diff --git a/README.rst b/README.rst index da90d48..d9ddce8 100644 --- a/README.rst +++ b/README.rst @@ -2,4 +2,28 @@ ltd-proxy ######### -LTD Proxy is a secure front-end proxy for LTD projects. +LTD Proxy is a secure front-end proxy for LTD projects that are hosted on Amazon AWS. +It uses GitHub OAuth to authenticate visitors and GitHub organization and/or team memberships to authorize access to pages at specific URL path prefixes. + +Kubernetes deployment +===================== + +Secret resource +--------------- + +Besides the ConfigMaps, your kustomized deployment needs to include a secret resource that is referenced as environment variables from the deployment's ``app`` container. +This secret could be generated from a Vault secret or an AWS Secret, or could be a plain Kubernetes Secret, such as: + +.. code-block:: yaml + + apiVersion: v1 + kind: Secret + type: Opaque + metadata: + name: ltdproxy + data: + LTDPROXY_AWS_ACCESS_KEY_ID: ... + LTDPROXY_AWS_SECRET_ACCESS_KEY: ... + LTDPROXY_GITHUB_OAUTH_ID: ... + LTDPROXY_GITHUB_OAUTH_SECRET: ... + LTDPROXY_SESSION_KEY: ... diff --git a/manifests/base/auth-configmap.yaml b/manifests/base/auth-configmap.yaml new file mode 100644 index 0000000..0b24789 --- /dev/null +++ b/manifests/base/auth-configmap.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: "ltdproxy-auth" + labels: + app.kubernetes.io/name: "ltd-proxy" +data: + authrules.yaml: | + default: + - org: "jsickcodes" + paths: [] + - pattern: "\/a\/" + authorized: + - org: "jsickcodes" + team: "Red Team" + diff --git a/manifests/base/configmap.yaml b/manifests/base/configmap.yaml index 454ed73..cf24aa2 100644 --- a/manifests/base/configmap.yaml +++ b/manifests/base/configmap.yaml @@ -11,5 +11,9 @@ data: SAFIR_LOGGER: "ltdproxy" SAFIR_LOG_LEVEL: "INFO" SAFIR_PROFILE: "production" + LTDPROXY_AUTH_CONFIG: "/opt/ltdproxy/auth/authrules.yaml" + LTDPROXY_PATH_PREFIX: "/" LTDPROXY_S3_BUCKET: "" LTDPROXY_S3_PREFIX: "" + LTDPROXY_AWS_REGION: "" + LTDPROXY_GITHUB_CALLBACK_URL: "" diff --git a/manifests/base/deployment.yaml b/manifests/base/deployment.yaml index 05e389f..ac08d46 100644 --- a/manifests/base/deployment.yaml +++ b/manifests/base/deployment.yaml @@ -19,7 +19,7 @@ spec: - name: app imagePullPolicy: "IfNotPresent" # Use images field in a Kustomization to set/update image tag - image: "lsstsqre/ltdproxy" + image: "ghcr.io/jsickcodes/ltd-proxy" ports: - containerPort: 8080 name: "app" @@ -32,6 +32,14 @@ spec: drop: - "all" readOnlyRootFilesystem: true + volumeMounts: + - name: "auth-config" + mountPath: "/opt/ltdproxy/auth/" + readOnly: true + volumes: + - name: "auth-config" + configMap: + name: "ltdproxy-auth" securityContext: runAsNonRoot: true runAsUser: 1000 diff --git a/manifests/base/kustomization.yaml b/manifests/base/kustomization.yaml index 2637f6d..1fda23c 100644 --- a/manifests/base/kustomization.yaml +++ b/manifests/base/kustomization.yaml @@ -7,5 +7,6 @@ images: resources: - configmap.yaml + - auth-configmap.yaml - deployment.yaml - service.yaml