Feat(API): Added rate-limiting and json error handling
This commit is contained in:
parent
7815894d9f
commit
10b9f90524
@ -10,13 +10,19 @@
|
|||||||
|
|
||||||
### Development dependencies
|
### Development dependencies
|
||||||
|
|
||||||
#### Python Dev-deps
|
#### Python dependencies
|
||||||
|
|
||||||
|
- `quart`: a micro-webframework, async version of Flask.
|
||||||
- `black`: Code formatter
|
- `black`: Code formatter
|
||||||
- `isort`: Import formatter
|
- `isort`: Import formatter
|
||||||
- `mypy`: Type checking
|
- `mypy`: Type checking
|
||||||
- `flake8`: General Python bugs
|
- `flake8`: General Python bugs
|
||||||
- `vulture`: Find unused code in Python programs
|
- `vulture`: Find unused code in Python programs
|
||||||
|
- `pytest`: For testing (turbocharged with `async`)
|
||||||
|
- `bcrypt`: Hashing and salting password.
|
||||||
|
- `zxcvbn`: Test password strength.
|
||||||
|
- `freezegun`: Check for expired token.
|
||||||
|
- `quart-rate-limiter`: rate limiting
|
||||||
|
|
||||||
#### SQL Dev-deps
|
#### SQL Dev-deps
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
TODO_BASE_URL="localhost:5050"
|
TODO_BASE_URL="localhost:5050"
|
||||||
TODO_DEBUG=true
|
TODO_DEBUG=true
|
||||||
TODO_SECRET_KEY="secret key"
|
TODO_SECRET_KEY="secret key"
|
||||||
|
|
||||||
|
# disable for when we not using HTTPS
|
||||||
|
TODO_QUART_AUTH_COOKIE_SECURE=false
|
||||||
|
TODO_QUART_AUTH_COOKIE_SAMESITE="Strict"
|
||||||
|
93
backend/pdm.lock
generated
93
backend/pdm.lock
generated
@ -22,6 +22,12 @@ dependencies = [
|
|||||||
"stevedore>=1.20.0",
|
"stevedore>=1.20.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bcrypt"
|
||||||
|
version = "4.0.1"
|
||||||
|
requires_python = ">=3.6"
|
||||||
|
summary = "Modern password hashing for your software and your servers"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "22.10.0"
|
version = "22.10.0"
|
||||||
@ -78,6 +84,15 @@ dependencies = [
|
|||||||
"pyflakes<2.6.0,>=2.5.0",
|
"pyflakes<2.6.0,>=2.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "freezegun"
|
||||||
|
version = "1.2.2"
|
||||||
|
requires_python = ">=3.6"
|
||||||
|
summary = "Let your Python tests travel through time"
|
||||||
|
dependencies = [
|
||||||
|
"python-dateutil>=2.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gitdb"
|
name = "gitdb"
|
||||||
version = "4.0.9"
|
version = "4.0.9"
|
||||||
@ -272,6 +287,15 @@ dependencies = [
|
|||||||
"pytest>=6.1.0",
|
"pytest>=6.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.8.2"
|
||||||
|
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||||
|
summary = "Extensions to the standard Python datetime module"
|
||||||
|
dependencies = [
|
||||||
|
"six>=1.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyyaml"
|
name = "pyyaml"
|
||||||
version = "6.0"
|
version = "6.0"
|
||||||
@ -294,6 +318,30 @@ dependencies = [
|
|||||||
"werkzeug>=2.2.0",
|
"werkzeug>=2.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quart-auth"
|
||||||
|
version = "0.7.0"
|
||||||
|
requires_python = ">=3.7"
|
||||||
|
summary = "A Quart extension to provide secure cookie authentication"
|
||||||
|
dependencies = [
|
||||||
|
"quart>=0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quart-rate-limiter"
|
||||||
|
version = "0.7.0"
|
||||||
|
requires_python = ">=3.7"
|
||||||
|
summary = "A Quart extension to provide rate limiting support"
|
||||||
|
dependencies = [
|
||||||
|
"quart>=0.15",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.16.0"
|
||||||
|
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
summary = "Python 2 and 3 compatibility utilities"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smmap"
|
name = "smmap"
|
||||||
version = "5.0.0"
|
version = "5.0.0"
|
||||||
@ -356,7 +404,7 @@ dependencies = [
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock_version = "4.0"
|
lock_version = "4.0"
|
||||||
content_hash = "sha256:f0267acbf584dfd2411f20fc3f63a5d42aa0bc85b37355aa528c0df8dda99498"
|
content_hash = "sha256:827a57e737ffa5147e1957e97f693d911838395691fd9fec9c2d93387e08cfb5"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
"aiofiles 22.1.0" = [
|
"aiofiles 22.1.0" = [
|
||||||
@ -371,6 +419,29 @@ content_hash = "sha256:f0267acbf584dfd2411f20fc3f63a5d42aa0bc85b37355aa528c0df8d
|
|||||||
{url = "https://files.pythonhosted.org/packages/39/36/a37a2f6f8d0ed8c3bc616616ed5019e1df2680bd8b7df49ceae80fd457de/bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"},
|
{url = "https://files.pythonhosted.org/packages/39/36/a37a2f6f8d0ed8c3bc616616ed5019e1df2680bd8b7df49ceae80fd457de/bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"},
|
||||||
{url = "https://files.pythonhosted.org/packages/da/eb/ff828f4ec32c85e10d9c344e6b7f11bcacfb5d70f2fd16bea6fc1ae6df06/bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"},
|
{url = "https://files.pythonhosted.org/packages/da/eb/ff828f4ec32c85e10d9c344e6b7f11bcacfb5d70f2fd16bea6fc1ae6df06/bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"},
|
||||||
]
|
]
|
||||||
|
"bcrypt 4.0.1" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/13/68/f3184c1f15581ebd936125b4da04cba0995f97ecd5ee8f4262c8ebba2646/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/28/ed/3c443bfbfdb37cd7c0d055b961311f49049ab4a00f45ba3bfd10d33a9443/bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/2c/be/376341b47e1e3fc424c9df1af60b5aedbd5ab04f73ccdf4107e42d92ef09/bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/41/16/49ff5146fb815742ad58cafb5034907aa7f166b1344d0ddd7fd1c818bd17/bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/41/86/05248719aa42a4fe1ca379d45794198700e992b91d389bfaa69533fc3331/bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/46/81/d8c22cd7e5e1c6a7d48e41a1d1d46c92f17dae70a54d9814f746e6027dec/bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/5e/01/098b798dc6c6984f2d5026269e80d7cad22d6ecacd5989bdf35a9c99a03d/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/64/fe/da28a5916128d541da0993328dc5cf4b43dfbf6655f2c7a2abe26ca2dc88/bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/77/2c/53c17079898584306eafdc937e0c7cc1bf8e2fe17e9909716ef3f9d6555d/bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/78/d4/3b2657bd58ef02b23a07729b0df26f21af97169dbd0b5797afa9e97ebb49/bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/7d/50/e683d8418974a602ba40899c8a5c38b3decaf5a4d36c32fc65dce454d8a8/bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/87/69/edacb37481d360d06fc947dab5734aaf511acb7d1a1f9e2849454376c0f8/bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/8c/ae/3af7d006aacf513975fd1948a6b4d6f8b4a307f8a244e1a3d3774b297aad/bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/99/a5/ff4aaf2adbefb2c9808d49cec37f65e0572c4ce856b13b194fd87a6cbd14/bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/aa/48/fd2b197a9741fa790ba0b88a9b10b5e88e62ff5cf3e1bc96d8354d7ce613/bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/aa/ca/6a534669890725cbb8c1fb4622019be31813c8edaa7b6d5b62fc9360a17e/bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/d8/f6/43ade4d37a3319baee9aec53f636411e70c18f0e4add9cc44a18f517af5f/bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/dd/4f/3632a69ce344c1551f7c9803196b191a8181c6a1ad2362c225581ef0d383/bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/ec/0a/1582790232fef6c2aa201f345577306b8bfe465c2c665dec04c86a016879/bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/fb/4b/e255df2000c2de4df524740b5f1d0a31157a1f7715b3eaf2e8f9c5c0acbb/bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/fb/a7/ee4561fd9b78ca23c8e5591c150cc58626a5dfb169345ab18e1c2c664ee0/bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"},
|
||||||
|
]
|
||||||
"black 22.10.0" = [
|
"black 22.10.0" = [
|
||||||
{url = "https://files.pythonhosted.org/packages/2c/11/f2737cd3b458d91401801e83a014e87c63e8904dc063200f77826c352f54/black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
|
{url = "https://files.pythonhosted.org/packages/2c/11/f2737cd3b458d91401801e83a014e87c63e8904dc063200f77826c352f54/black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
|
||||||
{url = "https://files.pythonhosted.org/packages/3d/c5/b3ab9b563f35fb284d37ab2b14acaed9a27d8cdea9c31364766eb54946a7/black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
|
{url = "https://files.pythonhosted.org/packages/3d/c5/b3ab9b563f35fb284d37ab2b14acaed9a27d8cdea9c31364766eb54946a7/black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
|
||||||
@ -417,6 +488,10 @@ content_hash = "sha256:f0267acbf584dfd2411f20fc3f63a5d42aa0bc85b37355aa528c0df8d
|
|||||||
{url = "https://files.pythonhosted.org/packages/ad/00/9808c62b2d529cefc69ce4e4a1ea42c0f855effa55817b7327ec5b75e60a/flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
{url = "https://files.pythonhosted.org/packages/ad/00/9808c62b2d529cefc69ce4e4a1ea42c0f855effa55817b7327ec5b75e60a/flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||||
{url = "https://files.pythonhosted.org/packages/cf/a0/b881b63a17a59d9d07f5c0cc91a29182c8e8a9aa2bde5b3b2b16519c02f4/flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
{url = "https://files.pythonhosted.org/packages/cf/a0/b881b63a17a59d9d07f5c0cc91a29182c8e8a9aa2bde5b3b2b16519c02f4/flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||||
]
|
]
|
||||||
|
"freezegun 1.2.2" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/1d/97/002ac49ec52858538b4aa6f6831f83c2af562c17340bdf6043be695f39ac/freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/50/cd/ba1c8319c002727ccfa03049127218d1767232a77219924d03ba170e0601/freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"},
|
||||||
|
]
|
||||||
"gitdb 4.0.9" = [
|
"gitdb 4.0.9" = [
|
||||||
{url = "https://files.pythonhosted.org/packages/a3/7c/5d747655049bfbf75b5fcec57c8115896cb78d6fafa84f6d3ef4c0f13a98/gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
|
{url = "https://files.pythonhosted.org/packages/a3/7c/5d747655049bfbf75b5fcec57c8115896cb78d6fafa84f6d3ef4c0f13a98/gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
|
||||||
{url = "https://files.pythonhosted.org/packages/fc/44/64e02ef96f20b347385f0e9c03098659cb5a1285d36c3d17c56e534d80cf/gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
|
{url = "https://files.pythonhosted.org/packages/fc/44/64e02ef96f20b347385f0e9c03098659cb5a1285d36c3d17c56e534d80cf/gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
|
||||||
@ -587,6 +662,10 @@ content_hash = "sha256:f0267acbf584dfd2411f20fc3f63a5d42aa0bc85b37355aa528c0df8d
|
|||||||
{url = "https://files.pythonhosted.org/packages/65/57/fb0d22ee0d9cd8d8b277cdf4fd7efa3e245c9577d2c2d54c6c2c18b535a2/pytest_asyncio-0.20.2-py3-none-any.whl", hash = "sha256:07e0abf9e6e6b95894a39f688a4a875d63c2128f76c02d03d16ccbc35bcc0f8a"},
|
{url = "https://files.pythonhosted.org/packages/65/57/fb0d22ee0d9cd8d8b277cdf4fd7efa3e245c9577d2c2d54c6c2c18b535a2/pytest_asyncio-0.20.2-py3-none-any.whl", hash = "sha256:07e0abf9e6e6b95894a39f688a4a875d63c2128f76c02d03d16ccbc35bcc0f8a"},
|
||||||
{url = "https://files.pythonhosted.org/packages/eb/db/479f1c43df26c42c9797dfc866dc98bcf28d3765ae49bf2da681ab344b72/pytest-asyncio-0.20.2.tar.gz", hash = "sha256:32a87a9836298a881c0ec637ebcc952cfe23a56436bdc0d09d1511941dd8a812"},
|
{url = "https://files.pythonhosted.org/packages/eb/db/479f1c43df26c42c9797dfc866dc98bcf28d3765ae49bf2da681ab344b72/pytest-asyncio-0.20.2.tar.gz", hash = "sha256:32a87a9836298a881c0ec637ebcc952cfe23a56436bdc0d09d1511941dd8a812"},
|
||||||
]
|
]
|
||||||
|
"python-dateutil 2.8.2" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/36/7a/87837f39d0296e723bb9b62bbb257d0355c7f6128853c78955f57342a56d/python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/4c/c4/13b4776ea2d76c115c1d1b84579f3764ee6d57204f6be27119f13a61d0a9/python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
||||||
|
]
|
||||||
"pyyaml 6.0" = [
|
"pyyaml 6.0" = [
|
||||||
{url = "https://files.pythonhosted.org/packages/02/25/6ba9f6bb50a3d4fbe22c1a02554dc670682a07c8701d1716d19ddea2c940/PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
{url = "https://files.pythonhosted.org/packages/02/25/6ba9f6bb50a3d4fbe22c1a02554dc670682a07c8701d1716d19ddea2c940/PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
||||||
{url = "https://files.pythonhosted.org/packages/08/f4/ffa743f860f34a5e8c60abaaa686f82c9ac7a2b50e5a1c3b1eb564d59159/PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
{url = "https://files.pythonhosted.org/packages/08/f4/ffa743f860f34a5e8c60abaaa686f82c9ac7a2b50e5a1c3b1eb564d59159/PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||||
@ -633,6 +712,18 @@ content_hash = "sha256:f0267acbf584dfd2411f20fc3f63a5d42aa0bc85b37355aa528c0df8d
|
|||||||
{url = "https://files.pythonhosted.org/packages/3d/3b/4fd974fef00ad26d9b078700e932776126528eb384aaa1e20a0867af53e5/Quart-0.18.3.tar.gz", hash = "sha256:dc4de597d5d4693627c90904b233d729531f6b27c1164f760476d3967aee2a4a"},
|
{url = "https://files.pythonhosted.org/packages/3d/3b/4fd974fef00ad26d9b078700e932776126528eb384aaa1e20a0867af53e5/Quart-0.18.3.tar.gz", hash = "sha256:dc4de597d5d4693627c90904b233d729531f6b27c1164f760476d3967aee2a4a"},
|
||||||
{url = "https://files.pythonhosted.org/packages/5c/05/64e318eaf6dbeb8db79402cb71d6ecda676c9510f382b9243efe67b9ba50/Quart-0.18.3-py3-none-any.whl", hash = "sha256:c221a7deb83a014dee040108d654b6141fe37f59e249c5caa0fdcf6506caf50b"},
|
{url = "https://files.pythonhosted.org/packages/5c/05/64e318eaf6dbeb8db79402cb71d6ecda676c9510f382b9243efe67b9ba50/Quart-0.18.3-py3-none-any.whl", hash = "sha256:c221a7deb83a014dee040108d654b6141fe37f59e249c5caa0fdcf6506caf50b"},
|
||||||
]
|
]
|
||||||
|
"quart-auth 0.7.0" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/5b/50/d98d5ba030f87bc73e55973ad90e8ea61dbbdd252715e03b8395c5a374b3/quart_auth-0.7.0-py3-none-any.whl", hash = "sha256:7729fccded2d821179fb3005116672d129ce17aa3f248ad8991587132b323a35"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/d6/9b/5da686100b031b549e1bc98019888cd1e95b8bee26d7efcd864e3fd537b1/quart-auth-0.7.0.tar.gz", hash = "sha256:e288f43789f694981e3695325cfe565361fdad252c5e1a663c5ca6ea41405a05"},
|
||||||
|
]
|
||||||
|
"quart-rate-limiter 0.7.0" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/49/1d/c5567a0128e84dec57bf283eb9708b8e0c91ca80488e4652ae82b3015348/quart_rate_limiter-0.7.0-py3-none-any.whl", hash = "sha256:7d8d0a72051cf60368810364676c151128f77120274d60e8df4e9c94419b4ac4"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/ab/90/55e48a9f0ce1628db103161351d65feeb15287b81ca570db66a4e6f4c0c5/quart-rate-limiter-0.7.0.tar.gz", hash = "sha256:e4cec1a0d476cf7fe2815e722d884e2cee8365bdd08f55480ed190ae07075a5d"},
|
||||||
|
]
|
||||||
|
"six 1.16.0" = [
|
||||||
|
{url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
|
{url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
|
]
|
||||||
"smmap 5.0.0" = [
|
"smmap 5.0.0" = [
|
||||||
{url = "https://files.pythonhosted.org/packages/21/2d/39c6c57032f786f1965022563eec60623bb3e1409ade6ad834ff703724f3/smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
|
{url = "https://files.pythonhosted.org/packages/21/2d/39c6c57032f786f1965022563eec60623bb3e1409ade6ad834ff703724f3/smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
|
||||||
{url = "https://files.pythonhosted.org/packages/6d/01/7caa71608bc29952ae09b0be63a539e50d2484bc37747797a66a60679856/smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
|
{url = "https://files.pythonhosted.org/packages/6d/01/7caa71608bc29952ae09b0be63a539e50d2484bc37747797a66a60679856/smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
|
||||||
|
@ -9,6 +9,10 @@ requires-python = ">=3.10"
|
|||||||
license = {text = "Private"}
|
license = {text = "Private"}
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quart>=0.18.3",
|
"quart>=0.18.3",
|
||||||
|
"quart-auth>=0.7.0",
|
||||||
|
"bcrypt>=4.0.1",
|
||||||
|
"itsdangerous>=2.1.2",
|
||||||
|
"quart-rate-limiter>=0.7.0",
|
||||||
]
|
]
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ dev = [
|
|||||||
"pytest>=7.1.2",
|
"pytest>=7.1.2",
|
||||||
"pytest-asyncio>=0.19.0",
|
"pytest-asyncio>=0.19.0",
|
||||||
"djhtml>=1.5.2",
|
"djhtml>=1.5.2",
|
||||||
|
"freezegun>=1.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from quart import Blueprint, ResponseReturnValue
|
from quart import Blueprint, ResponseReturnValue
|
||||||
|
from quart_rate_limiter import rate_exempt
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint("control", __name__)
|
blueprint = Blueprint("control", __name__)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.get("/control/ping")
|
@blueprint.get("/control/ping")
|
||||||
|
@rate_exempt
|
||||||
async def ping() -> ResponseReturnValue:
|
async def ping() -> ResponseReturnValue:
|
||||||
return {"ping": "pong"}
|
return {"ping": "pong"}
|
||||||
|
@ -1,16 +1,41 @@
|
|||||||
from quart import Quart, ResponseReturnValue
|
from quart import Quart, ResponseReturnValue
|
||||||
|
|
||||||
|
# Each blueprint is a logical collection of features in our web app
|
||||||
from backend.blueprints.control import blueprint as control_blueprint
|
from backend.blueprints.control import blueprint as control_blueprint
|
||||||
|
|
||||||
|
# For making sure error responses are in JSON format
|
||||||
from backend.lib.api_error import APIError
|
from backend.lib.api_error import APIError
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
from quart_rate_limiter import RateLimiter
|
||||||
|
from quart_rate_limiter import RateLimitExceeded
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
app = Quart(__name__)
|
# Authentication
|
||||||
|
from quart_auth import AuthManager
|
||||||
|
|
||||||
|
|
||||||
|
app: Quart = Quart(__name__)
|
||||||
|
auth_manager: AuthManager = AuthManager(app)
|
||||||
|
rate_limiter: RateLimiter = RateLimiter(app)
|
||||||
|
|
||||||
|
# Configure the web app
|
||||||
|
# Either in DEV/DEBUG mode or TEST mode
|
||||||
app.config.from_prefixed_env(prefix="TODO")
|
app.config.from_prefixed_env(prefix="TODO")
|
||||||
|
|
||||||
|
|
||||||
app.register_blueprint(control_blueprint)
|
app.register_blueprint(control_blueprint)
|
||||||
|
|
||||||
|
|
||||||
|
# rate limiting
|
||||||
|
@app.errorhandler(RateLimitExceeded) # type: ignore
|
||||||
|
async def handle_rate_limit_exceeded_error(
|
||||||
|
error: RateLimitExceeded,
|
||||||
|
) -> ResponseReturnValue:
|
||||||
|
return {}, error.get_headers(), 429
|
||||||
|
|
||||||
|
|
||||||
|
# handles errors
|
||||||
@app.errorhandler(APIError) # type: ignore
|
@app.errorhandler(APIError) # type: ignore
|
||||||
async def handle_api_error(error: APIError) -> ResponseReturnValue:
|
async def handle_api_error(error: APIError) -> ResponseReturnValue:
|
||||||
return {"code": error.code}, error.status_code
|
return {"code": error.code}, error.status_code
|
||||||
|
@ -2,3 +2,7 @@ TODO_BASE_URL="localhost:5050"
|
|||||||
TODO_DEBUG=true
|
TODO_DEBUG=true
|
||||||
TODO_SECRET_KEY="secret key"
|
TODO_SECRET_KEY="secret key"
|
||||||
TODO_TESTING=true
|
TODO_TESTING=true
|
||||||
|
|
||||||
|
# disable for when we not using HTTPS
|
||||||
|
TODO_QUART_AUTH_COOKIE_SECURE=false
|
||||||
|
TODO_QUART_AUTH_COOKIE_SAMESITE="Strict"
|
||||||
|
29
backend/tests/test_rate_limits.py
Normal file
29
backend/tests/test_rate_limits.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from quart_rate_limiter import (
|
||||||
|
QUART_RATE_LIMITER_EXEMPT_ATTRIBUTE,
|
||||||
|
QUART_RATE_LIMITER_LIMITS_ATTRIBUTE,
|
||||||
|
)
|
||||||
|
|
||||||
|
from backend.run import app
|
||||||
|
|
||||||
|
IGNORED_ENDPOINTS = {"static"}
|
||||||
|
|
||||||
|
|
||||||
|
# Check if all api routes are rate limited
|
||||||
|
async def test_routes_have_rate_limits() -> None:
|
||||||
|
for rule in app.url_map.iter_rules():
|
||||||
|
endpoint = rule.endpoint
|
||||||
|
|
||||||
|
exempt = getattr(
|
||||||
|
app.view_functions[endpoint],
|
||||||
|
QUART_RATE_LIMITER_EXEMPT_ATTRIBUTE,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exempt and endpoint not in IGNORED_ENDPOINTS:
|
||||||
|
rate_limits = getattr(
|
||||||
|
app.view_functions[endpoint],
|
||||||
|
QUART_RATE_LIMITER_LIMITS_ATTRIBUTE,
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert rate_limits != []
|
Loading…
x
Reference in New Issue
Block a user