diff --git a/Pipfile b/Pipfile index b1dd05043..ce21ad20d 100644 --- a/Pipfile +++ b/Pipfile @@ -12,7 +12,6 @@ requests = "*" Django = ">=4.2.14, <5.0" bagit = "*" django-registration = "*" -django-simple-captcha = "*" django-tinymce = "*" elasticsearch = "<7.14.0" django-elasticsearch-dsl = "==7.3" diff --git a/Pipfile.lock b/Pipfile.lock index 424fbecdb..5196f10b6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "8c4e53c0f15830ef951cd629c0c6da48bcc1e898a363aac041b473893333266b" + "sha256": "e8d305a130abab3890cc7e84fb481eba53ab8868dbefb9f27371b7b3050eaae2" }, "pipfile-spec": 6, "requires": { @@ -81,10 +81,11 @@ }, "automat": { "hashes": [ - "sha256:c3164f8742b9dc440f3682482d32aaff7bb53f71740dd018533f9de286b64180", - "sha256:e56beb84edad19dcc11d30e8d9b895f75deeb5ef5e96b84a467066b3b84bb04e" + "sha256:b34227cf63f6325b8ad2399ede780675083e439b20c323d376373d8ee6306d88", + "sha256:bf029a7bc3da1e2c24da2343e7598affaa9f10bf0ab63ff808566ce90551e02a" ], - "version": "==22.10.0" + "markers": "python_version >= '3.8'", + "version": "==24.8.1" }, "axe-selenium-python": { "hashes": [ @@ -155,19 +156,19 @@ }, "boto3": { "hashes": [ - "sha256:7ca22adef4c77ee128e1e1dc7d48bc9512a87cc6fe3d771b3f913d5ecd41c057", - "sha256:864f06528c583dc7b02adf12db395ecfadbf9cb0da90e907e848ffb27128ce19" + "sha256:2cef3aa476181395c260f4b6e6c5565e5a3022a874fb6b579d8e6b169f94e0b3", + "sha256:5724ddeda8e18c7614c20a09c20159ed87ff7439755cf5e250a1a3feaf9afb7e" ], "index": "pypi", - "version": "==1.34.154" + "version": "==1.35.5" }, "botocore": { "hashes": [ - "sha256:4eef4b1bb809b382ba9dc9c88f5fcc4a133f221a1acb693ee6bee4de9f325979", - "sha256:64d9b4c85a504d77cb56dabb2ad717cd8e1717424a88edb458b01d1e5797262a" + "sha256:3a0086c7124cb3b0d9f98563d00ffd14a942c3f9e731d8d1ccf0d3a1ac7ed884", + "sha256:8116b72c7ae845c195146e437e2afd9d17538a37b3f3548dcf67c12c86ba0742" ], "index": "pypi", - "version": "==1.34.154" + "version": "==1.35.5" }, "brotli": { "hashes": [ @@ -527,10 +528,10 @@ }, "cron-descriptor": { "hashes": [ - "sha256:7b1a00d7d25d6ae6896c0da4457e790b98cba778398a3d48e341e5e0d33f0488", - "sha256:a67ba21804983b1427ed7f3e1ec27ee77bf24c652b0430239c268c5ddfbf9dc0" + "sha256:736b3ae9d1a99bc3dbfc5b55b5e6e7c12031e7ba5de716625772f8b02dcd6013", + "sha256:f51ce4ffc1d1f2816939add8524f206c376a42c87a5fca3091ce26725b3b1bca" ], - "version": "==1.4.3" + "version": "==1.4.5" }, "cryptography": { "hashes": [ @@ -614,10 +615,11 @@ }, "django-celery-beat": { "hashes": [ - "sha256:f75b2d129731f1214be8383e18fae6bfeacdb55dffb2116ce849222c0106f9ad" + "sha256:8482034925e09b698c05ad61c36ed2a8dbc436724a3fe119215193a4ca6dc967", + "sha256:851c680d8fbf608ca5fecd5836622beea89fa017bc2b3f94a5b8c648c32d84b1" ], "index": "pypi", - "version": "==2.6.0" + "version": "==2.7.0" }, "django-debug-toolbar": { "hashes": [ @@ -658,12 +660,6 @@ "index": "pypi", "version": "==1.0.0" }, - "django-ranged-response": { - "hashes": [ - "sha256:f71fff352a37316b9bead717fc76e4ddd6c9b99c4680cdf4783b9755af1cf985" - ], - "version": "==0.2.0" - }, "django-ratelimit": { "hashes": [ "sha256:555943b283045b917ad59f196829530d63be2a39adb72788d985b90c81ba808b", @@ -696,14 +692,6 @@ "index": "pypi", "version": "==6.1" }, - "django-simple-captcha": { - "hashes": [ - "sha256:3ae9a7e650cb0cdbcfd4a75aa91fdf25dcc523ef541a7b1f004bd4357798fc03", - "sha256:d188516d326fadd2d5ad076eb89649d55c02cabafe3fdcc2154ac18e9f6d4b97" - ], - "index": "pypi", - "version": "==0.6.0" - }, "django-storages": { "hashes": [ "sha256:69aca94d26e6714d14ad63f33d13619e697508ee33ede184e462ed766dc2a73f", @@ -717,7 +705,7 @@ "sha256:3232e7ecde66ba4464abb6f9e6b8cc739b914efb9b29dc2cf2eee451f7cc2acb", "sha256:aa6f4965838484317b7f08d22c0d91a53d64e7bbbd34264468ae83d4023898a7" ], - "markers": "python_version >= '3.8' and python_version < '4.0'", + "markers": "python_version >= '3.8' and python_version < '4'", "version": "==7.0" }, "django-tinymce": { @@ -1040,11 +1028,11 @@ }, "gunicorn": { "hashes": [ - "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", - "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" + "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", + "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec" ], "index": "pypi", - "version": "==22.0.0" + "version": "==23.0.0" }, "h11": { "hashes": [ @@ -1194,11 +1182,11 @@ }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac", + "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603" ], "markers": "python_version >= '3.5'", - "version": "==3.7" + "version": "==3.8" }, "incremental": { "hashes": [ @@ -1250,19 +1238,19 @@ }, "locust": { "hashes": [ - "sha256:20756509939004e95c622ac3042886edab38b736f00534cc03ce2774064e7f71", - "sha256:d26b7333cdef80645f3978d8ff9aabab4d53e41ed82cc8490212aa68e8498fdd" + "sha256:1f056173aefa6ba42501c3bf04bb700df9eddd165e38bb721f7b00643b68b825", + "sha256:566d89b5c4a7b69e3ab6844c1a373909918ee9d04f5d2bab6a8104e43a5721d8" ], "index": "pypi", - "version": "==2.31.1" + "version": "==2.31.4" }, "markdown": { "hashes": [ - "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f", - "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224" + "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", + "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803" ], "index": "pypi", - "version": "==3.6" + "version": "==3.7" }, "markdown-it-py": { "hashes": [ @@ -1348,11 +1336,11 @@ }, "more-itertools": { "hashes": [ - "sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463", - "sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320" + "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27", + "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923" ], "index": "pypi", - "version": "==10.3.0" + "version": "==10.4.0" }, "msgpack": { "hashes": [ @@ -1848,11 +1836,11 @@ }, "pytesseract": { "hashes": [ - "sha256:8f22cc98f765bf13517ead0c70effedb46c153540d25783e04014f28b55a5fc6", - "sha256:f1c3a8b0f07fd01a1085d451f5b8315be6eec1d5577a6796d46dc7a62bd4120f" + "sha256:4bf5f880c99406f52a3cfc2633e42d9dc67615e69d8a509d74867d3baddb5db9", + "sha256:7a99c6c2ac598360693d83a416e36e0b33a67638bb9d77fdcac094a3589d4b34" ], "index": "pypi", - "version": "==0.3.10" + "version": "==0.3.13" }, "pytest": { "hashes": [ @@ -1894,118 +1882,118 @@ }, "pyzmq": { "hashes": [ - "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b", - "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6", - "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6", - "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1", - "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d", - "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0", - "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334", - "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e", - "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42", - "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83", - "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea", - "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88", - "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab", - "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3", - "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402", - "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb", - "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c", - "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c", - "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072", - "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa", - "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d", - "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe", - "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1", - "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71", - "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c", - "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2", - "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1", - "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8", - "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79", - "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416", - "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de", - "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120", - "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73", - "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f", - "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a", - "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0", - "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544", - "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb", - "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f", - "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88", - "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917", - "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f", - "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449", - "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69", - "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86", - "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0", - "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106", - "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598", - "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4", - "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410", - "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29", - "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a", - "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820", - "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894", - "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3", - "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b", - "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595", - "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec", - "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895", - "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b", - "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03", - "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24", - "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf", - "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd", - "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829", - "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4", - "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2", - "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a", - "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b", - "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5", - "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38", - "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384", - "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d", - "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d", - "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545", - "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c", - "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3", - "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562", - "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273", - "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab", - "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf", - "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1", - "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83", - "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277", - "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b", - "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be", - "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c", - "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322", - "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd", - "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682", - "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7", - "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f", - "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741", - "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250", - "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb", - "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3", - "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76", - "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee", - "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b", - "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099", - "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb", - "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf", - "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2", - "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c", - "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242", - "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a", - "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b", - "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1", - "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae" + "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", + "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a", + "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9", + "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", + "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37", + "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc", + "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed", + "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097", + "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d", + "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52", + "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6", + "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6", + "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2", + "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282", + "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3", + "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732", + "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5", + "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18", + "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", + "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f", + "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3", + "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b", + "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277", + "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a", + "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797", + "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca", + "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c", + "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f", + "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5", + "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a", + "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44", + "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20", + "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4", + "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8", + "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780", + "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386", + "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5", + "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2", + "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", + "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971", + "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b", + "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50", + "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c", + "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f", + "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231", + "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c", + "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08", + "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5", + "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6", + "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073", + "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e", + "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4", + "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317", + "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3", + "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072", + "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad", + "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a", + "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb", + "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd", + "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f", + "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef", + "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5", + "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187", + "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711", + "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988", + "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640", + "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c", + "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764", + "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1", + "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1", + "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289", + "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb", + "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a", + "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218", + "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c", + "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf", + "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7", + "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8", + "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726", + "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9", + "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93", + "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88", + "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115", + "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6", + "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672", + "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2", + "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea", + "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc", + "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b", + "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa", + "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003", + "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797", + "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940", + "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db", + "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc", + "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27", + "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3", + "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e", + "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98", + "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b", + "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629", + "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9", + "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6", + "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec", + "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951", + "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae", + "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4", + "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6", + "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919" ], "markers": "python_version >= '3.7'", - "version": "==26.1.0" + "version": "==26.2.0" }, "redis": { "hashes": [ @@ -2025,10 +2013,10 @@ }, "rich": { "hashes": [ - "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", - "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432" + "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc", + "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4" ], - "version": "==13.7.1" + "version": "==13.8.0" }, "s3transfer": { "hashes": [ @@ -2048,11 +2036,11 @@ }, "sentry-sdk": { "hashes": [ - "sha256:7a8d5163d2ba5c5f4464628c6b68f85e86972f7c636acc78aed45c61b98b7a5e", - "sha256:8763840497b817d44c49b3fe3f5f7388d083f2337ffedf008b2cdb63b5c86dc6" + "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6", + "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260" ], "index": "pypi", - "version": "==2.12.0" + "version": "==2.13.0" }, "service-identity": { "hashes": [ @@ -2063,11 +2051,11 @@ }, "setuptools": { "hashes": [ - "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1", - "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec" + "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e", + "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193" ], "markers": "python_version >= '3.8'", - "version": "==72.1.0" + "version": "==73.0.1" }, "setuptools-scm": { "hashes": [ @@ -2109,11 +2097,11 @@ }, "soupsieve": { "hashes": [ - "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", - "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" + "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", + "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9" ], "markers": "python_version >= '3.8'", - "version": "==2.5" + "version": "==2.6" }, "sqlparse": { "hashes": [ @@ -2148,11 +2136,11 @@ }, "trio": { "hashes": [ - "sha256:6d2fe7ee656146d598ec75128ff4a2386576801b42b691f4a91cc2c18508544a", - "sha256:998bbdc5797621e1976c86820b1bc341cc66b51d2618a31cc8720ddd7df8affe" + "sha256:0346c3852c15e5c7d40ea15972c4805689ef2cb8b5206f794c9c19450119f3a4", + "sha256:c5237e8133eb0a1d72f09a971a55c28ebe69e351c783fc64bc37db8db8bbe1d0" ], "markers": "python_version >= '3.8'", - "version": "==0.26.1" + "version": "==0.26.2" }, "trio-websocket": { "hashes": [ @@ -2165,7 +2153,6 @@ "twisted": { "extras": [ "http2", - "tls", "tls" ], "hashes": [ @@ -2173,7 +2160,6 @@ "sha256:734832ef98108136e222b5230075b1079dad8a3fc5637319615619a7725b0c81" ], "index": "pypi", - "markers": "python_full_version >= '3.8.0'", "version": "==24.7.0" }, "txaio": { @@ -2259,11 +2245,11 @@ }, "werkzeug": { "hashes": [ - "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18", - "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8" + "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", + "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306" ], "markers": "python_version >= '3.8'", - "version": "==3.0.3" + "version": "==3.0.4" }, "whitenoise": { "hashes": [ @@ -2291,43 +2277,37 @@ }, "zope-interface": { "hashes": [ - "sha256:03bd5c0db82237bbc47833a8b25f1cc090646e212f86b601903d79d7e6b37031", - "sha256:03f1452d5d1f279184d5bdb663a3dc39902d9320eceb63276240791e849054b6", - "sha256:10ebac566dd0cec66f942dc759d46a994a2b3ba7179420f0e2130f88f8a5f400", - "sha256:192b7a792e3145ed880ff6b1a206fdb783697cfdb4915083bfca7065ec845e60", - "sha256:19c829d52e921b9fe0b2c0c6a8f9a2508c49678ee1be598f87d143335b6a35dc", - "sha256:3f3495462bc0438b76536a0e10d765b168ae636092082531b88340dc40dcd118", - "sha256:3f52050c6a10d4a039ec6f2c58e5b3ade5cc570d16cf9d102711e6b8413c90e6", - "sha256:400d06c9ec8dbcc96f56e79376297e7be07a315605c9a2208720da263d44d76f", - "sha256:4ec212037becf6d2f705b7ed4538d56980b1e7bba237df0d8995cbbed29961dc", - "sha256:51d5713e8e38f2d3ec26e0dfdca398ed0c20abda2eb49ffc15a15a23eb8e5f6d", - "sha256:52f5253cca1b35eaeefa51abd366b87f48f8714097c99b131ba61f3fdbbb58e7", - "sha256:5566fd9271c89ad03d81b0831c37d46ae5e2ed211122c998637130159a120cf1", - "sha256:55bbcc74dc0c7ab489c315c28b61d7a1d03cf938cc99cc58092eb065f120c3a5", - "sha256:696c2a381fc7876b3056711717dba5eddd07c2c9e5ccd50da54029a1293b6e43", - "sha256:6ba4b3638d014918b918aa90a9c8370bd74a03abf8fcf9deb353b3a461a59a84", - "sha256:7039e624bcb820f77cc2ff3d1adcce531932990eee16121077eb51d9c76b6c14", - "sha256:88d108d004e0df25224de77ce349a7e73494ea2cb194031f7c9687e68a88ec9b", - "sha256:8c1dff87b30fd150c61367d0e2cdc49bb55f8b9fd2a303560bbc24b951573ae1", - "sha256:9a8195b99e650e6f329ce4e5eb22d448bdfef0406404080812bc96e2a05674cb", - "sha256:af0b33f04677b57843d529b9257a475d2865403300b48c67654c40abac2f9f24", - "sha256:b419f2144e1762ab845f20316f1df36b15431f2622ebae8a6d5f7e8e712b413c", - "sha256:b59deb0ddc7b431e41d720c00f99d68b52cb9bd1d5605a085dc18f502fe9c47f", - "sha256:bc0615351221926a36a0fbcb2520fb52e0b23e8c22a43754d9cb8f21358c33c0", - "sha256:c203d82069ba31e1f3bc7ba530b2461ec86366cd4bfc9b95ec6ce58b1b559c34", - "sha256:ce6cbb852fb8f2f9bb7b9cdca44e2e37bce783b5f4c167ff82cb5f5128163c8f", - "sha256:d33cb526efdc235a2531433fc1287fcb80d807d5b401f9b801b78bf22df560dd", - "sha256:da0cef4d7e3f19c3bd1d71658d6900321af0492fee36ec01b550a10924cffb9c", - "sha256:da21e7eec49252df34d426c2ee9cf0361c923026d37c24728b0fa4cc0599fd03", - "sha256:ea8d51e5eb29e57d34744369cd08267637aa5a0fefc9b5d33775ab7ff2ebf2e3", - "sha256:ec4e87e6fdc511a535254daa122c20e11959ce043b4e3425494b237692a34f1c", - "sha256:f0f5fda7cbf890371a59ab1d06512da4f2c89a6ea194e595808123c863c38eff", - "sha256:f32ca483e6ade23c7caaee9d5ee5d550cf4146e9b68d2fb6c68bac183aa41c37", - "sha256:f749ca804648d00eda62fe1098f229b082dfca930d8bad8386e572a6eafa7525", - "sha256:f89a420cf5a6f2aa7849dd59e1ff0e477f562d97cf8d6a1ee03461e1eec39887" + "sha256:0821efcbdeaf48e12c66b0d19a1f9edec2ed22697ab8885d322c8f82fe5bc892", + "sha256:08d86319fd7542984d4c0ef7865759dab58616154cb237a5a1ce758687255de0", + "sha256:093ab9a2c5105d826755c43a76770b69353dbe95ec27a0b5e88ab4f63d7744b8", + "sha256:13aacff95c59000ecd562d9717a87eca8211f6bc74bea6b8ca68e742d1f8f13d", + "sha256:185ef3a7a01fac1151622579a08995aab66590711c1a4f9b605f88129229dba1", + "sha256:22c93492e5d2f09100a4a23cf709b20f0305cdbbad14f9af2f6e9311742bed8e", + "sha256:28f29dd42819d99682e46a8d3cc2ee60461a77554d4320e0e8a37363f04208e0", + "sha256:3246cccb9e4ce34c9b32ad55a53098043af5e7185623bf5de8e6ec5d8e71415e", + "sha256:356a9c1c8cfece776f54806157057be759d812168395762f47f046b40901e974", + "sha256:3ab142cebe69e0f72bf892da040af97f61fd03c09a23ae2fc7de3ab576c5d4cd", + "sha256:4b671f943d6487d6f1a6bbdce3faffae35e4f74f98ac9b865d2b7370cb6b0bd3", + "sha256:4f5e39373952e1d689476b6e43d779553b165ce332d0fde9c36d9b095f28d052", + "sha256:738de1c72390a2caf543247013f617ed15d272e4c19731a998e81dd5a2379f1c", + "sha256:797510df26b82cf619a894dac4ff4036d11f6340bec0287c89cecb0b1b1c429e", + "sha256:7c47a5068df03f0c9215d3525b166c9d8d4f6d03cbe4e60339818f8c393e3e3e", + "sha256:8eab70e404c2416176b4630914cda275ca95678529e54e66ea45d1a0be422994", + "sha256:91bb6b5e1a158b751e12458d5618c1af42eb3dc8472b87a613d543d9fb7660e0", + "sha256:9da2fb807a20cd4fe381e23e2f906f0a0f4acece6d9abac65d5fc0a1f8383ed8", + "sha256:b76f6048c1a334e26e5d46fdb4f327d9e7e6b348ad607ee9fdce9c7325b5a635", + "sha256:b9d865209cc9795d0f9f4f63b87a86e7a9e032d3cbbb10b1c13bf27343a4fc54", + "sha256:bceaf7ee95735b0d6ac3f5bba0209d056e686999732dc32bd463a53d4488ccdb", + "sha256:bf2746931a6f83370fdc4005dbea4e39e3a3d0333da42897040698c1ff282e9c", + "sha256:d9ab785a7af39c6968385a9d39b712d2263661fa3780bd38efec0cefdbb84036", + "sha256:dd28ba1e2deb0c339881ee7755db649433347bdf3c4f3d885f029fcf10aacdf7", + "sha256:deac72b653817a68b96079c1428ae84860c76f653af03668a02f97b74f8a465b", + "sha256:e299f672cfad3392b097af885a552a51e60d3b44e8572f1401e87f863f8986b4", + "sha256:f1146bb27a411d0d40cc0e88182a6b0e979d68ab526c8e5ae9e27c06506ed017", + "sha256:f77d58cfc3af86d062b8cfa7194db74ca78a615d66bbd23b251bad1b1ecf9818" ], "markers": "python_version >= '3.8'", - "version": "==7.0.1" + "version": "==7.0.2" }, "zope.event": { "hashes": [ @@ -2580,60 +2560,62 @@ }, "pyyaml": { "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "sqlparse": { "hashes": [ diff --git a/concordia/settings_local_test.py b/concordia/settings_local_test.py index b683d3c25..3224052dc 100644 --- a/concordia/settings_local_test.py +++ b/concordia/settings_local_test.py @@ -1,3 +1,5 @@ +import os + from .settings_template import * # NOQA ignore=F405 from .settings_template import DATABASES @@ -21,3 +23,17 @@ "CONFIG": {"hosts": [("localhost", 63791)]}, } } + +# Turnstile settings +TURNSTILE_JS_API_URL = os.environ.get( + "TURNSTILE_JS_API_URL", "https://challenges.cloudflare.com/turnstile/v0/api.js" +) +TURNSTILE_VERIFY_URL = os.environ.get( + "TURNSTILE_VERIFY_URL", "https://challenges.cloudflare.com/turnstile/v0/siteverify" +) +TURNSTILE_SITEKEY = os.environ.get( + "TURNSTILE_SITEKEY", "1x00000000000000000000BB" +) # Always pass, invisible +TURNSTILE_SECRET = os.environ.get( + "TURNSTILE_SECRET", "1x0000000000000000000000000000000AA" +) # Always pass diff --git a/concordia/settings_template.py b/concordia/settings_template.py index 7ee597cb4..784bf0c9c 100644 --- a/concordia/settings_template.py +++ b/concordia/settings_template.py @@ -114,7 +114,6 @@ "concordia.apps.ConcordiaAppConfig", "exporter", "importer", - "captcha", "prometheus_metrics.apps.PrometheusMetricsConfig", "robots", "django_celery_beat", @@ -327,13 +326,6 @@ TURNSTILE_DEFAULT_CONFIG = os.environ.get("TURNSTILE_DEFAULT_CONFIG", {}) TURNSTILE_PROXIES = os.environ.get("TURNSTILE_PROXIES", {}) -CAPTCHA_CHALLENGE_FUNCT = "captcha.helpers.random_char_challenge" -#: Anonymous sessions require captcha validation every day by default: -ANONYMOUS_CAPTCHA_VALIDATION_INTERVAL = 86400 - -CAPTCHA_IMAGE_SIZE = [150, 100] -CAPTCHA_FONT_SIZE = 40 - STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", diff --git a/concordia/settings_test.py b/concordia/settings_test.py index b49eed049..56876b8e9 100644 --- a/concordia/settings_test.py +++ b/concordia/settings_test.py @@ -1,3 +1,5 @@ +import os + from .settings_template import * # NOQA ignore=F405 from .settings_template import DATABASES @@ -21,3 +23,17 @@ "CONFIG": {"hosts": [("localhost", 6379)]}, } } + +# Turnstile settings +TURNSTILE_JS_API_URL = os.environ.get( + "TURNSTILE_JS_API_URL", "https://challenges.cloudflare.com/turnstile/v0/api.js" +) +TURNSTILE_VERIFY_URL = os.environ.get( + "TURNSTILE_VERIFY_URL", "https://challenges.cloudflare.com/turnstile/v0/siteverify" +) +TURNSTILE_SITEKEY = os.environ.get( + "TURNSTILE_SITEKEY", "1x00000000000000000000BB" +) # Always pass, invisible +TURNSTILE_SECRET = os.environ.get( + "TURNSTILE_SECRET", "1x0000000000000000000000000000000AA" +) # Always pass diff --git a/concordia/static/js/src/contribute.js b/concordia/static/js/src/contribute.js index 45f39b6e5..ff8f6d0f1 100644 --- a/concordia/static/js/src/contribute.js +++ b/concordia/static/js/src/contribute.js @@ -60,40 +60,6 @@ $(document).on('keydown', function (event) { }); function setupPage() { - var $captchaModal = $('#captcha-modal'); - var $triggeringCaptchaForm = false; - var $captchaForm = $captchaModal - .find('form') - .on('submit', function (event) { - event.preventDefault(); - - var formData = $captchaForm.serializeArray(); - - $.ajax({ - url: $captchaForm.attr('action'), - method: 'POST', - dataType: 'json', - data: $.param(formData), - }) - .done(function () { - $captchaModal.modal('hide'); - if ($triggeringCaptchaForm) { - $triggeringCaptchaForm.submit(); - } - $triggeringCaptchaForm = false; - }) - .fail(function (jqXHR) { - if (jqXHR.status == 401) { - $captchaModal - .find('[name=key]') - .val(jqXHR.responseJSON.key); - $captchaModal - .find('#captcha-image') - .attr('src', jqXHR.responseJSON.image); - } - }); - }); - $('form.ajax-submission').each(function (index, formElement) { /* Generic AJAX submission logic which takes a form and POSTs its data to the @@ -147,27 +113,16 @@ function setupPage() { } }) .fail(function (jqXHR, textStatus, errorThrown) { - if (jqXHR.status == 401) { - $captchaModal - .find('[name=key]') - .val(jqXHR.responseJSON.key); - $captchaModal - .find('#captcha-image') - .attr('src', jqXHR.responseJSON.image); - $triggeringCaptchaForm = $form; - $captchaModal.modal(); - } else { - $form.trigger('form-submit-failure', { - textStatus: textStatus, - errorThrown: errorThrown, - requestData: formData, - $form: $form, - jqXHR: jqXHR, - }); - unlockControls($form); - if (eventData.lockElement) { - unlockControls($(eventData.lockElement)); - } + $form.trigger('form-submit-failure', { + textStatus: textStatus, + errorThrown: errorThrown, + requestData: formData, + $form: $form, + jqXHR: jqXHR, + }); + unlockControls($form); + if (eventData.lockElement) { + unlockControls($(eventData.lockElement)); } }); @@ -515,6 +470,11 @@ function setupPage() { url: url, method: 'POST', dataType: 'json', + data: { + 'cf-turnstile-response': $transcriptionEditor + .find('input[name="cf-turnstile-response"]') + .val(), + }, }) .done(function (responseData) { displayMessage( diff --git a/concordia/templates/registration/login.html b/concordia/templates/registration/login.html index 26af4419d..19e493958 100644 --- a/concordia/templates/registration/login.html +++ b/concordia/templates/registration/login.html @@ -5,6 +5,7 @@ {% block head_content %} {{ block.super }} + {% endblock head_content %} {% block title %}Login{% endblock title %} @@ -23,6 +24,7 @@

Welcome back!

{% endif %} {% bootstrap_form form %} +
{{ turnstile_form.turnstile }}

By using this system, you agree to comply with the Library's diff --git a/concordia/templates/transcriptions/asset_detail.html b/concordia/templates/transcriptions/asset_detail.html index 81a786611..86cb202f9 100644 --- a/concordia/templates/transcriptions/asset_detail.html +++ b/concordia/templates/transcriptions/asset_detail.html @@ -116,9 +116,6 @@

- diff --git a/concordia/templates/transcriptions/asset_detail/captcha_modal.html b/concordia/templates/transcriptions/asset_detail/captcha_modal.html deleted file mode 100644 index 2ca5c457d..000000000 --- a/concordia/templates/transcriptions/asset_detail/captcha_modal.html +++ /dev/null @@ -1,38 +0,0 @@ - diff --git a/concordia/templates/transcriptions/asset_detail/editor.html b/concordia/templates/transcriptions/asset_detail/editor.html index 56ce9c405..b9e0baa40 100644 --- a/concordia/templates/transcriptions/asset_detail/editor.html +++ b/concordia/templates/transcriptions/asset_detail/editor.html @@ -86,6 +86,8 @@

+
{{ turnstile_form.turnstile }}
+ @@ -118,6 +120,5 @@

{% endspaceless %} - {{ turnstile_form.turnstile }} diff --git a/concordia/tests/test_views.py b/concordia/tests/test_views.py index ea912bef1..8636807a9 100644 --- a/concordia/tests/test_views.py +++ b/concordia/tests/test_views.py @@ -1,9 +1,10 @@ from datetime import date, timedelta from unittest.mock import patch -from captcha.models import CaptchaStore +from django import forms from django.conf import settings from django.contrib.auth.models import User +from django.core.cache import cache from django.http import HttpResponse, JsonResponse from django.test import ( Client, @@ -31,7 +32,6 @@ from concordia.views import ( AccountProfileView, CompletedCampaignListView, - ConcordiaLoginView, ratelimit_view, registration_rate, ) @@ -113,6 +113,12 @@ class ConcordiaViewTests(CreateTestUsers, JSONAssertMixin, TestCase): This class contains the unit tests for the view in the concordia app. """ + def setUp(self): + cache.clear() + + def tearDown(self): + cache.clear() + def test_ratelimit_view(self): c = Client() response = c.get("/error/429/") @@ -477,27 +483,6 @@ def test_campaign_report(self): RATELIMIT_ENABLE=False, SESSION_ENGINE="django.contrib.sessions.backends.cache" ) class TransactionalViewTests(CreateTestUsers, JSONAssertMixin, TransactionTestCase): - def completeCaptcha(self, key=None): - """Submit a CAPTCHA response using the provided challenge key""" - - if key is None: - challenge_data = self.assertValidJSON( - self.client.get(reverse("ajax-captcha")), expected_status=401 - ) - self.assertIn("key", challenge_data) - self.assertIn("image", challenge_data) - key = challenge_data["key"] - - self.assertValidJSON( - self.client.post( - reverse("ajax-captcha"), - data={ - "key": key, - "response": CaptchaStore.objects.get(hashkey=key).response, - }, - ) - ) - def test_asset_reservation(self): """ Test the basic Asset reservation process @@ -749,74 +734,67 @@ def test_asset_reservation_tombstone_expiration(self): self.assertEqual(reservation.reservation_token, data["reservation_token"]) self.assertEqual(reservation.tombstoned, False) - def test_anonymous_transcription_save_captcha(self): - asset = create_asset() - - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} - ) - data = self.assertValidJSON(resp, expected_status=401) - self.assertIn("key", data) - self.assertIn("image", data) - - self.completeCaptcha(data["key"]) - - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} - ) - data = self.assertValidJSON(resp, expected_status=201) - def test_transcription_save(self): asset = create_asset() - # We're not testing the CAPTCHA here so we'll complete it: - self.completeCaptcha() + with patch("concordia.turnstile.fields.TurnstileField.validate") as mock: + mock.side_effect = forms.ValidationError( + "Testing error", code="invalid_turnstile" + ) + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} + ) + data = self.assertValidJSON(resp, expected_status=401) + self.assertIn("error", data) - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} - ) - data = self.assertValidJSON(resp, expected_status=201) - self.assertIn("submissionUrl", data) + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} + ) + data = self.assertValidJSON(resp, expected_status=201) + self.assertIn("submissionUrl", data) - # Test attempts to create a second transcription without marking that it - # supersedes the previous one: - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} - ) - data = self.assertValidJSON(resp, expected_status=409) - self.assertIn("error", data) + # Test attempts to create a second transcription without marking that it + # supersedes the previous one: + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} + ) + data = self.assertValidJSON(resp, expected_status=409) + self.assertIn("error", data) - # This should work with the chain specified: - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), - data={"text": "test", "supersedes": asset.transcription_set.get().pk}, - ) - data = self.assertValidJSON(resp, expected_status=201) - self.assertIn("submissionUrl", data) + # This should work with the chain specified: + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), + data={"text": "test", "supersedes": asset.transcription_set.get().pk}, + ) + data = self.assertValidJSON(resp, expected_status=201) + self.assertIn("submissionUrl", data) - # We should see an error if you attempt to supersede a transcription - # which has already been superseded: - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), - data={ - "text": "test", - "supersedes": asset.transcription_set.order_by("pk").first().pk, - }, - ) - data = self.assertValidJSON(resp, expected_status=409) - self.assertIn("error", data) + # We should see an error if you attempt to supersede a transcription + # which has already been superseded: + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), + data={ + "text": "test", + "supersedes": asset.transcription_set.order_by("pk").first().pk, + }, + ) + data = self.assertValidJSON(resp, expected_status=409) + self.assertIn("error", data) - # A logged in user can take over from an anonymous user: - self.login_user() - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), - data={ - "text": "test", - "supersedes": asset.transcription_set.order_by("pk").last().pk, - }, - ) - data = self.assertValidJSON(resp, expected_status=201) - self.assertIn("submissionUrl", data) + # A logged in user can take over from an anonymous user: + self.login_user() + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), + data={ + "text": "test", + "supersedes": asset.transcription_set.order_by("pk").last().pk, + }, + ) + data = self.assertValidJSON(resp, expected_status=201) + self.assertIn("submissionUrl", data) def test_anonymous_transcription_submission(self): asset = create_asset() @@ -826,36 +804,48 @@ def test_anonymous_transcription_submission(self): transcription.full_clean() transcription.save() - resp = self.client.post( - reverse("submit-transcription", args=(transcription.pk,)) - ) + with patch("concordia.turnstile.fields.TurnstileField.validate") as mock: + mock.side_effect = forms.ValidationError( + "Testing error", code="invalid_turnstile" + ) + resp = self.client.post( + reverse("submit-transcription", args=(transcription.pk,)) + ) data = self.assertValidJSON(resp, expected_status=401) - self.assertIn("key", data) - self.assertIn("image", data) + self.assertIn("error", data) self.assertFalse(Transcription.objects.filter(submitted__isnull=False).exists()) - self.completeCaptcha(data["key"]) - self.client.post(reverse("submit-transcription", args=(transcription.pk,))) - self.assertTrue(Transcription.objects.filter(submitted__isnull=False).exists()) + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + self.client.post( + reverse("submit-transcription", args=(transcription.pk,)), + ) + self.assertTrue( + Transcription.objects.filter(submitted__isnull=False).exists() + ) def test_transcription_submission(self): asset = create_asset() - # We're not testing the CAPTCHA here so we'll complete it: - self.completeCaptcha() - - resp = self.client.post( - reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} - ) + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + resp = self.client.post( + reverse("save-transcription", args=(asset.pk,)), data={"text": "test"} + ) data = self.assertValidJSON(resp, expected_status=201) transcription = Transcription.objects.get() self.assertIsNone(transcription.submitted) - resp = self.client.post( - reverse("submit-transcription", args=(transcription.pk,)) - ) + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + resp = self.client.post( + reverse("submit-transcription", args=(transcription.pk,)) + ) data = self.assertValidJSON(resp, expected_status=200) self.assertIn("id", data) self.assertEqual(data["id"], transcription.pk) @@ -866,9 +856,6 @@ def test_transcription_submission(self): def test_stale_transcription_submission(self): asset = create_asset() - # We're not testing the CAPTCHA here so we'll complete it: - self.completeCaptcha() - anon = get_anonymous_user() t1 = Transcription(asset=asset, user=anon, text="test") @@ -879,9 +866,12 @@ def test_stale_transcription_submission(self): t2.full_clean() t2.save() - resp = self.client.post(reverse("submit-transcription", args=(t1.pk,))) - data = self.assertValidJSON(resp, expected_status=400) - self.assertIn("error", data) + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + resp = self.client.post(reverse("submit-transcription", args=(t1.pk,))) + data = self.assertValidJSON(resp, expected_status=400) + self.assertIn("error", data) def test_transcription_review(self): asset = create_asset() @@ -1538,18 +1528,34 @@ def test_ratelimit_view(self): self.assertNotEqual(response["Retry-After"], 0) -class CaptchaTests(TestCase): +class LoginTests(TestCase, CreateTestUsers): def setUp(self): - self.request_factory = RequestFactory() + self.user = self.create_user("test-user") def test_ConcordiaLoginView(self): - request = self.request_factory.post("/") - request.session = {} - view = setup_view(ConcordiaLoginView(), request) - response = view.post(request) - self.assertNotContains(response, "captcha") - - request.limited = True - view = setup_view(ConcordiaLoginView(), request) - response = view.post(request) - self.assertContains(response, "captcha") + with patch("concordia.turnstile.fields.TurnstileField.validate") as mock: + mock.side_effect = forms.ValidationError( + "Testing error", code="invalid_turnstile" + ) + response = self.client.post( + reverse("registration_login"), + data={"username": self.user.username, "password": self.user._password}, + ) + self.assertIn("user", response.context) + self.assertFalse(response.context["user"].is_authenticated) + + with patch( + "concordia.turnstile.fields.TurnstileField.validate", return_value=True + ): + response = self.client.post( + reverse("registration_login"), + data={"username": self.user.username, "password": self.user._password}, + follow=True, + ) + self.assertRedirects( + response, + expected_url=reverse("homepage"), + target_status_code=200, + ) + self.assertIn("user", response.context) + self.assertTrue(response.context["user"].is_authenticated) diff --git a/concordia/turnstile/fields.py b/concordia/turnstile/fields.py index 57e57e179..24eb5201c 100644 --- a/concordia/turnstile/fields.py +++ b/concordia/turnstile/fields.py @@ -50,6 +50,7 @@ def widget_attrs(self, widget): def validate(self, value): super().validate(value) + opener = build_opener(ProxyHandler(settings.TURNSTILE_PROXIES)) post_data = urlencode( { @@ -57,7 +58,9 @@ def validate(self, value): "response": value, } ).encode() + request = Request(settings.TURNSTILE_VERIFY_URL, post_data) + try: response = opener.open(request, timeout=settings.TURNSTILE_TIMEOUT) except HTTPError as exc: diff --git a/concordia/urls.py b/concordia/urls.py index 10f746a52..941c8a8b7 100644 --- a/concordia/urls.py +++ b/concordia/urls.py @@ -263,8 +263,6 @@ ".well-known/change-password", # https://wicg.github.io/change-password-url/ RedirectView.as_view(pattern_name="password_change"), ), - path("captcha/ajax/", views.ajax_captcha, name="ajax-captcha"), - path("captcha/", include("captcha.urls")), path("admin/", admin.site.urls), # Internal support assists: path("error/500/", server_error), diff --git a/concordia/views.py b/concordia/views.py index 282f516fa..531ec291e 100644 --- a/concordia/views.py +++ b/concordia/views.py @@ -11,9 +11,6 @@ from urllib.parse import urlencode import markdown -from captcha.fields import CaptchaField -from captcha.helpers import captcha_image_url -from captcha.models import CaptchaStore from django.conf import settings from django.contrib import messages from django.contrib.auth import logout @@ -340,33 +337,29 @@ class ConcordiaRegistrationView(RegistrationView): @method_decorator(never_cache, name="dispatch") -@method_decorator( - ratelimit( - group="login", key="post:username", rate="3/15m", method="POST", block=False - ), - name="post", -) class ConcordiaLoginView(LoginView): form_class = UserLoginForm def post(self, request, *args, **kwargs): form = self.get_form() - - # This is set by the ratelimit decorator - # True if the request exceeds the rate limit - blocked = request.limited - recent_captcha = ( - time() - request.session.get("captcha_validation_time", 0) - ) < 86400 - - if blocked and not recent_captcha: - form.fields["captcha"] = CaptchaField() - if form.is_valid(): - return self.form_valid(form) + turnstile_form = TurnstileForm(request.POST) + if turnstile_form.is_valid(): + return self.form_valid(form) + else: + form.add_error(None, "Unable to validate user") + return self.form_invalid(form) + else: return self.form_invalid(form) + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + ctx["turnstile_form"] = TurnstileForm(auto_id=False) + + return ctx + def ratelimit_view(request, exception=None): status_code = 429 @@ -1518,44 +1511,17 @@ def get_context_data(self, **kwargs): return ctx -@never_cache -def ajax_captcha(request): - if request.method == "POST": - response = request.POST.get("response") - key = request.POST.get("key") - - if response and key: - CaptchaStore.remove_expired() - - # Note that CaptchaStore displays the response in uppercase in the - # image and in the string representation of the object but the - # actual value stored in the database is lowercase! - deleted, _ = CaptchaStore.objects.filter( - response=response.lower(), hashkey=key - ).delete() - - if deleted > 0: - request.session["captcha_validation_time"] = time() - return JsonResponse({"valid": True}) - - key = CaptchaStore.generate_key() - return JsonResponse( - {"key": key, "image": request.build_absolute_uri(captcha_image_url(key))}, - status=401, - content_type="application/json", - ) - - def validate_anonymous_user(view): @wraps(view) @never_cache def inner(request, *args, **kwargs): - if not request.user.is_authenticated: - last_validated = request.session.get("anonymous_validation_time", 0) - age = time() - last_validated - if age > settings.ANONYMOUS_VALIDATION_INTERVAL: - return ajax_captcha(request) - + if not request.user.is_authenticated and request.method == "POST": + form = TurnstileForm(request.POST) + if not form.is_valid(): + return JsonResponse( + {"error": "Unable to validate user"}, + status=401, + ) return view(request, *args, **kwargs) return inner @@ -1583,7 +1549,7 @@ def get_transcription_superseded(asset, supersedes_pk): @require_POST -@validate_anonymous_user +@login_required @atomic @ratelimit(key="header:cf-connecting-ip", rate="1/m", block=settings.RATELIMIT_BLOCK) def generate_ocr_transcription(request, *, asset_pk): @@ -1706,6 +1672,7 @@ def rollforward_transcription(request, *, asset_pk): @require_POST +@validate_anonymous_user @atomic def save_transcription(request, *, asset_pk): asset = get_object_or_404(Asset, pk=asset_pk) @@ -1713,15 +1680,6 @@ def save_transcription(request, *, asset_pk): if request.user.is_anonymous: user = get_anonymous_user() - form = TurnstileForm(request.POST) - if not form.is_valid(): - return JsonResponse( - { - "error": "Turnstile form invalid...." - "I don't know what to tell you yet...." - }, - status=400, - ) else: user = request.user diff --git a/docs/for-developers.md b/docs/for-developers.md index 2b6f438ee..1edbd2d27 100644 --- a/docs/for-developers.md +++ b/docs/for-developers.md @@ -77,6 +77,16 @@ virtualenv environment: 1. Make sure that [redis](https://redis.io/docs/getting-started/) is installed and running. +1. Configure Turnstile in your `.env` file. Unless specifically testing Turnstile, + you'll probably want the following settings: + + ```bash + echo TURNSTILE_SITEKEY=1x00000000000000000000BB >> .env + echo TURNSTILE_SECRET=1x0000000000000000000000000000000AA >> .env + ``` + + Those two settings ensure all Turnstile tests pass. See [Turnstile Testing](https://developers.cloudflare.com/turnstile/troubleshooting/testing/) for other options. + ### Local Development Environment You will likely want to run the Django development server on your localhost