From fe60264546fbeb5548b9b1857cdb59b604d01621 Mon Sep 17 00:00:00 2001 From: Felix Chadwick-Smith <chadwick-smithff@cardiff.ac.uk> Date: Wed, 10 Jan 2024 16:08:06 +0000 Subject: [PATCH] removed Flask_WTF --- app.py | 9 +- requirements.txt | 1 - .../flask_wtf-1.2.0.dist-info/INSTALLER | 1 - .../flask_wtf-1.2.0.dist-info/METADATA | 72 ---- .../flask_wtf-1.2.0.dist-info/RECORD | 26 -- .../flask_wtf-1.2.0.dist-info/REQUESTED | 0 .../flask_wtf-1.2.0.dist-info/WHEEL | 4 - .../licenses/LICENSE.rst | 28 -- venv/Lib/site-packages/flask_wtf/__init__.py | 8 - .../__pycache__/__init__.cpython-311.pyc | Bin 532 -> 0 bytes .../__pycache__/_compat.cpython-311.pyc | Bin 698 -> 0 bytes .../__pycache__/csrf.cpython-311.pyc | Bin 15040 -> 0 bytes .../__pycache__/file.cpython-311.pyc | Bin 8532 -> 0 bytes .../__pycache__/form.cpython-311.pyc | Bin 7261 -> 0 bytes .../__pycache__/i18n.cpython-311.pyc | Bin 2060 -> 0 bytes venv/Lib/site-packages/flask_wtf/_compat.py | 11 - venv/Lib/site-packages/flask_wtf/csrf.py | 329 ------------------ venv/Lib/site-packages/flask_wtf/file.py | 151 -------- venv/Lib/site-packages/flask_wtf/form.py | 127 ------- venv/Lib/site-packages/flask_wtf/i18n.py | 47 --- .../flask_wtf/recaptcha/__init__.py | 3 - .../__pycache__/__init__.cpython-311.pyc | Bin 380 -> 0 bytes .../__pycache__/fields.cpython-311.pyc | Bin 1134 -> 0 bytes .../__pycache__/validators.cpython-311.pyc | Bin 3739 -> 0 bytes .../__pycache__/widgets.cpython-311.pyc | Bin 2875 -> 0 bytes .../flask_wtf/recaptcha/fields.py | 17 - .../flask_wtf/recaptcha/validators.py | 75 ---- .../flask_wtf/recaptcha/widgets.py | 43 --- 28 files changed, 3 insertions(+), 949 deletions(-) delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/REQUESTED delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL delete mode 100644 venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst delete mode 100644 venv/Lib/site-packages/flask_wtf/__init__.py delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/_compat.py delete mode 100644 venv/Lib/site-packages/flask_wtf/csrf.py delete mode 100644 venv/Lib/site-packages/flask_wtf/file.py delete mode 100644 venv/Lib/site-packages/flask_wtf/form.py delete mode 100644 venv/Lib/site-packages/flask_wtf/i18n.py delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/fields.py delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/validators.py delete mode 100644 venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py diff --git a/app.py b/app.py index d0a5a1a..f31300f 100644 --- a/app.py +++ b/app.py @@ -2,14 +2,12 @@ import os import secrets from flask import Flask, render_template, request, redirect, url_for, send_from_directory, abort from flask_sqlalchemy import SQLAlchemy -from flask_wtf import FlaskForm from wtforms import StringField, SubmitField app = Flask(__name__, static_folder='static') app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' app.config['SECRET_KEY'] = secrets.token_hex(16) -app.config['WTF_CSRF_ENABLED'] = True db = SQLAlchemy(app) @@ -18,10 +16,9 @@ class Project(db.Model): title = db.Column(db.String(100), nullable=False) description = db.Column(db.Text, nullable=False) -class AddProjectForm(FlaskForm): - title = StringField('Title') - description = StringField('Description') - submit = SubmitField('Submit') +class AddProjectForm(): + # Remove FlaskForm-related code + pass @app.route('/') def home(): diff --git a/requirements.txt b/requirements.txt index b7475d8..65df85b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ click==8.1.7 colorama==0.4.6 Flask==3.0.0 Flask-SQLAlchemy==3.1.1 -Flask-WTF==1.2.0 gevent==23.9.1 greenlet==3.0.3 gunicorn==21.2.0 diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA deleted file mode 100644 index cb81ed4..0000000 --- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/METADATA +++ /dev/null @@ -1,72 +0,0 @@ -Metadata-Version: 2.1 -Name: Flask-WTF -Version: 1.2.0 -Summary: Form rendering, validation, and CSRF protection for Flask with WTForms. -Project-URL: Documentation, https://flask-wtf.readthedocs.io/ -Project-URL: Changes, https://flask-wtf.readthedocs.io/changes/ -Project-URL: Source Code, https://github.com/wtforms/flask-wtf/ -Project-URL: Issue Tracker, https://github.com/wtforms/flask-wtf/issues/ -Project-URL: Chat, https://discord.gg/pallets -Maintainer: WTForms -License: Copyright 2010 WTForms - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -License-File: LICENSE.rst -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Requires-Python: >=3.8 -Requires-Dist: flask -Requires-Dist: itsdangerous -Requires-Dist: wtforms -Provides-Extra: email -Requires-Dist: email-validator; extra == 'email' -Description-Content-Type: text/x-rst - -Flask-WTF -========= - -Simple integration of Flask and WTForms, including CSRF, file upload, -and reCAPTCHA. - -Links ------ - -- Documentation: https://flask-wtf.readthedocs.io/ -- Changes: https://flask-wtf.readthedocs.io/changes/ -- PyPI Releases: https://pypi.org/project/Flask-WTF/ -- Source Code: https://github.com/wtforms/flask-wtf/ -- Issue Tracker: https://github.com/wtforms/flask-wtf/issues/ -- Chat: https://discord.gg/pallets diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD deleted file mode 100644 index c0e003b..0000000 --- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/RECORD +++ /dev/null @@ -1,26 +0,0 @@ -flask_wtf-1.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -flask_wtf-1.2.0.dist-info/METADATA,sha256=DzMrNjYSRoyVUkYdVzRXCc0AOriWm1nM4HzNyd8VSMo,3373 -flask_wtf-1.2.0.dist-info/RECORD,, -flask_wtf-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -flask_wtf-1.2.0.dist-info/WHEEL,sha256=9QBuHhg6FNW7lppboF2vKVbCGTVzsFykgRQjjlajrhA,87 -flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst,sha256=1fGQNkUVeMs27u8EyZ6_fXyi5w3PBDY2UZvEIOFafGI,1475 -flask_wtf/__init__.py,sha256=ObJw25Z-Uq2HgLhMKhfKk-BVr2hBXxBdEx_fyV4geBA,214 -flask_wtf/__pycache__/__init__.cpython-311.pyc,, -flask_wtf/__pycache__/_compat.cpython-311.pyc,, -flask_wtf/__pycache__/csrf.cpython-311.pyc,, -flask_wtf/__pycache__/file.cpython-311.pyc,, -flask_wtf/__pycache__/form.cpython-311.pyc,, -flask_wtf/__pycache__/i18n.cpython-311.pyc,, -flask_wtf/_compat.py,sha256=N3sqC9yzFWY-3MZ7QazX1sidvkO3d5yy4NR6lkp0s94,248 -flask_wtf/csrf.py,sha256=O-fjnWygxxi_FsIU2koua97ZpIhiOJVDHA57dXLpvTA,10171 -flask_wtf/file.py,sha256=CKr7kAgGqmKyC47gPuYoR7EHVSgl06xKtkyMmEPsXhs,4759 -flask_wtf/form.py,sha256=TmR7xCrxin2LHp6thn7fq1OeU8aLB7xsZzvv52nH7Ss,4049 -flask_wtf/i18n.py,sha256=TyO8gqt9DocHMSaNhj0KKgxoUrPYs-G1nVW-jns0SOw,1166 -flask_wtf/recaptcha/__init__.py,sha256=m4eNGoU3Q0Wnt_wP8VvOlA0mwWuoMtAcK9pYT7sPFp8,106 -flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc,, -flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc,, -flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc,, -flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc,, -flask_wtf/recaptcha/fields.py,sha256=M1-RFuUKOsJAzsLm3xaaxuhX2bB9oRqS-HVSN-NpkmI,433 -flask_wtf/recaptcha/validators.py,sha256=3sd1mUQT3Y3D_WJeKwecxUGstnhh_QD-A_dEBJfkf6s,2434 -flask_wtf/recaptcha/widgets.py,sha256=J_XyxAZt3uB15diIMnkXXGII2dmsWCsVsKV3KQYn4Ns,1512 diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/REQUESTED b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL deleted file mode 100644 index ba1a8af..0000000 --- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.18.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst b/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst deleted file mode 100644 index 63c3617..0000000 --- a/venv/Lib/site-packages/flask_wtf-1.2.0.dist-info/licenses/LICENSE.rst +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 WTForms - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/flask_wtf/__init__.py b/venv/Lib/site-packages/flask_wtf/__init__.py deleted file mode 100644 index 3f884c0..0000000 --- a/venv/Lib/site-packages/flask_wtf/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .csrf import CSRFProtect -from .form import FlaskForm -from .form import Form -from .recaptcha import Recaptcha -from .recaptcha import RecaptchaField -from .recaptcha import RecaptchaWidget - -__version__ = "1.2.0" diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index cc492fb7f408d036f111ed881bee398192c91b20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 532 zcmY+AKTE?v7{>3K|1GU9iXeiELI-PG(Luze5)eeCMT8JIVsfdW`BN@w#ZG<;H&^jX z_!%-u=p;C~3EevRUT6z>@8RCx^Bla#-FvlKMo?RyZ}bcL4>!vyEnwCKcts9!u#W<4 zV9dGXmjb~Mau$F^L(Ez9r9d|1oF!nzP;!=mRYL_<49!t{sIL8D7+lBGN~hP=Z&{R3 zI{~Zdz7-Gjhy`H8Mc2C2wuXuQXhB$88Qr741IgMdx%ZqtO(s&S(QY)a>*c9v$IN8{ zTs0M47|KLICULK^<xnQ`P~n@(Pb!3rDT}=*B;*6lAVTw-&Z&79Lu%UX=5gz&ePUij z_9&oX5}TKS)u*vJrs3GU@*d3COX$JSvIlwW@)wb(#62X$3%!Jp#_)Nro&}LJ^65ES zhxz<7;t6bj0>(H)J84lF+D?ng&}LdxhIZ4UGPIW#m7)E#sPEdwONB31gX{kRgW8h^ diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/_compat.cpython-311.pyc deleted file mode 100644 index d3b114711695270b6f6b23c27085d8d5a9e4ac65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 698 zcmZWnJ#P~+7=G<bj%$^+0t7)!FlOMRtzbwH3sDggWC2tuI>|C;Cmg*m>`S8{h8y?+ zY~2`(5c~#y!y$EuyfU#}l@6#A_FXTi>TCIVKm7Rpu>5sxZ56cK`1ytZ)%v$_YH6Ms z3!#kzV8BoSL%{G982$vz;^@!<_^x*)LyUpLyQR+>93NuM(7O_(Z)--Q_F0o-X8*W! zJ`d8BUP8zDV`u}fCL4{;oW;5}4s@;|Vlc!dAFG=?0hN2ZuXg%85!|DxA4j`XM1C|L zbS&i%648(oqE-nBV>Stl-6G`uga*~gH&~bor-LV5_qF6gy583P2b<el58Zz3O+p@} z(tQ@vF_-Q>kM`Z?{u@{NDZi6YZ?6oGOb=w5j_wgJ4ilQ*O=iMOT!Jy=hK6jp<_#z7 zi<O$8Y#L1IOe)(SN3q}^F6&&e2<69R<_=blo{o=WF;jLmNvEltX*H`<S1vw4SzWcH zA0`1G`9aEsTCa3{O*p29vJvwuPMt?p>c`@$cFohvOhaKIgyyi3m8-B3S}EWn<hQqv zP59*hnFHv0<{ZJb&u{Zv-Cxi<f?f{2IkZi*J#Vz~_GaFFQCJw^W6*G7pg0d@hyMUg C*0H<* diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/csrf.cpython-311.pyc deleted file mode 100644 index 3fd39c21ef0bbb6810a3f32664d89d8534bea2f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15040 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 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/file.cpython-311.pyc deleted file mode 100644 index d747ca899480b04119d65dc0245885758a6377a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8532 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 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/form.cpython-311.pyc deleted file mode 100644 index 434265db1bc141bd6e9e00db52dc7ece9afde4a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7261 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 diff --git a/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/__pycache__/i18n.cpython-311.pyc deleted file mode 100644 index d4ee7b4c91f2cf66961434de6bc21c4f859e9c8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2060 zcmZ`(&2Jk;6rWkI?ag|lwAkPzZHcL>5>uKlAcTOBqE@OZsuW1!U|Fm--c90WckRxM zU7BtyA9`?6fnHDoq9~^{kV^E(iNC=?matZe6bW(Y%~cAAo_I6PI<`XJ&b)o^&DWdX zy!RgV_NoY||M#D)Z)Al2;z~fE3))+O-zqYZiEZTI48~lRY{>~_LP8FKEN5gP%XZj_ zWFkTi+fhf!C?H2NJ!bR{%BT{$fK25MGJ7^8gtnl!C=-*A725!1D=?vqW~y^29ectp zY3xU7saPx!24XB%BE)i;ZWN1rgjm-~7Ud)AqQ!K(kTYxxv|h)e)L68rUNqQJTJp6k z#Bix?uzbO#zQ#JVT>D@U!q7v|_x=U8iWu;KsAS?f0D!ffN9bx>E@L9bSu~iBx>lPZ z6K}xITYPsxw~&V_7nl<0up2*J(0U}0I{&QiK<kE#9evTrA~xkm;ln3)-c0WaR(lA5 zkE|G<TC7A|nqo^<Dpvr=b1Ve_GwVy1>cnnDO#z)N6mO&!NWn=h*alrbJ8#TecItYb zEv0C|u~JUK%rE3~E$~}QX8o|3<tMJQg#vM?-np$G-M7M1M$Cd^<lVGNI68z!r>}w^ zmP^H3M!_$GF+w=7grn)pcEK<Se+a^HBpR?ZCj6EZ14}*s=-hPnGini<%}tEGGyc}Z zWcGX^S8^b<X!fGRBPUz7+;a9(em+a{%sN{%a?8RjuBd~5qxtbEccgg3KdSTC>-3+* z;S`=>)B{#Q|ENfdHxUje8h!C9YetaL_uX>+l{afq-h}b*_~3(gYp14vJHH*DsmEt3 zmv*D^TJ+dX^3{6s%%91zKayiR+Q8k*_bxxU`n~<5z4pc9i_H+~AAgFZe)Sn=RSmWt ze*5caT{*QIOV;G%9-NPIoCU-I;IuagY!z83gH4oy*o8C=nG$f>3_%pg0?S-Gz+pB= znD@Pp#Zne*1zLAF-jY12B6-+Nc+z^uESR`Y9^L@^R(lK%#ItEh0Ou>E{a8zfe3@GI zf*+xb<lRLdGs+!+m?JO2-{+6dgK{Ntr6Z$9_N^yj3Vj_|1?^}jwzZ+UHdMupfx)$b zYPnk8#J{S)sE;SNho|eq(=}z9Bw%hXsDB_IlminKC<kqS58YVy<cjJ+k}nIy2){k{ zq&eitFG9eRyij*y0t(XX6MjelMNi2Mun3cW81NMeK$L8Q_>rPrB8Kf_m+-fwJU;-L z02?J(x$<%sIDz>zUo#D?3!UAVR(-EAlv<zpp}bbUKXrd<bF!w5@Ae(}_U2bN*QVA# z-~42|Z?xVwT2n>^V6*A4ujsmKIF_#as;)!2muxO;x_+%>*sT$A42~lFNq$CRop?bJ zWP-iM-I);XRKO+vH6<zheiOBs5($qr(P7rwj;H=?x|hV@FN%R57P%@)hA19>td+c? z@`%#lYx{*}geS8o8GcmP4cpdr5sNMVc;z4*uLH-QD<8D(`5`$21N_bCBVf%C#<+n7 zYTd1YPSm>F|IImBLtU+bPS?8IuKGqL+KeP{e-pLYNDKomhuKqZdv-8z9344c_3qxj Lce|qTPu%7|PW|9T diff --git a/venv/Lib/site-packages/flask_wtf/_compat.py b/venv/Lib/site-packages/flask_wtf/_compat.py deleted file mode 100644 index 50973e0..0000000 --- a/venv/Lib/site-packages/flask_wtf/_compat.py +++ /dev/null @@ -1,11 +0,0 @@ -import warnings - - -class FlaskWTFDeprecationWarning(DeprecationWarning): - pass - - -warnings.simplefilter("always", FlaskWTFDeprecationWarning) -warnings.filterwarnings( - "ignore", category=FlaskWTFDeprecationWarning, module="wtforms|flask_wtf" -) diff --git a/venv/Lib/site-packages/flask_wtf/csrf.py b/venv/Lib/site-packages/flask_wtf/csrf.py deleted file mode 100644 index 06afa0c..0000000 --- a/venv/Lib/site-packages/flask_wtf/csrf.py +++ /dev/null @@ -1,329 +0,0 @@ -import hashlib -import hmac -import logging -import os -from urllib.parse import urlparse - -from flask import Blueprint -from flask import current_app -from flask import g -from flask import request -from flask import session -from itsdangerous import BadData -from itsdangerous import SignatureExpired -from itsdangerous import URLSafeTimedSerializer -from werkzeug.exceptions import BadRequest -from wtforms import ValidationError -from wtforms.csrf.core import CSRF - -__all__ = ("generate_csrf", "validate_csrf", "CSRFProtect") -logger = logging.getLogger(__name__) - - -def generate_csrf(secret_key=None, token_key=None): - """Generate a CSRF token. The token is cached for a request, so multiple - calls to this function will generate the same token. - - During testing, it might be useful to access the signed token in - ``g.csrf_token`` and the raw token in ``session['csrf_token']``. - - :param secret_key: Used to securely sign the token. Default is - ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``. - :param token_key: Key where token is stored in session for comparison. - Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``. - """ - - secret_key = _get_config( - secret_key, - "WTF_CSRF_SECRET_KEY", - current_app.secret_key, - message="A secret key is required to use CSRF.", - ) - field_name = _get_config( - token_key, - "WTF_CSRF_FIELD_NAME", - "csrf_token", - message="A field name is required to use CSRF.", - ) - - if field_name not in g: - s = URLSafeTimedSerializer(secret_key, salt="wtf-csrf-token") - - if field_name not in session: - session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest() - - try: - token = s.dumps(session[field_name]) - except TypeError: - session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest() - token = s.dumps(session[field_name]) - - setattr(g, field_name, token) - - return g.get(field_name) - - -def validate_csrf(data, secret_key=None, time_limit=None, token_key=None): - """Check if the given data is a valid CSRF token. This compares the given - signed token to the one stored in the session. - - :param data: The signed CSRF token to be checked. - :param secret_key: Used to securely sign the token. Default is - ``WTF_CSRF_SECRET_KEY`` or ``SECRET_KEY``. - :param time_limit: Number of seconds that the token is valid. Default is - ``WTF_CSRF_TIME_LIMIT`` or 3600 seconds (60 minutes). - :param token_key: Key where token is stored in session for comparison. - Default is ``WTF_CSRF_FIELD_NAME`` or ``'csrf_token'``. - - :raises ValidationError: Contains the reason that validation failed. - - .. versionchanged:: 0.14 - Raises ``ValidationError`` with a specific error message rather than - returning ``True`` or ``False``. - """ - - secret_key = _get_config( - secret_key, - "WTF_CSRF_SECRET_KEY", - current_app.secret_key, - message="A secret key is required to use CSRF.", - ) - field_name = _get_config( - token_key, - "WTF_CSRF_FIELD_NAME", - "csrf_token", - message="A field name is required to use CSRF.", - ) - time_limit = _get_config(time_limit, "WTF_CSRF_TIME_LIMIT", 3600, required=False) - - if not data: - raise ValidationError("The CSRF token is missing.") - - if field_name not in session: - raise ValidationError("The CSRF session token is missing.") - - s = URLSafeTimedSerializer(secret_key, salt="wtf-csrf-token") - - try: - token = s.loads(data, max_age=time_limit) - except SignatureExpired as e: - raise ValidationError("The CSRF token has expired.") from e - except BadData as e: - raise ValidationError("The CSRF token is invalid.") from e - - if not hmac.compare_digest(session[field_name], token): - raise ValidationError("The CSRF tokens do not match.") - - -def _get_config( - value, config_name, default=None, required=True, message="CSRF is not configured." -): - """Find config value based on provided value, Flask config, and default - value. - - :param value: already provided config value - :param config_name: Flask ``config`` key - :param default: default value if not provided or configured - :param required: whether the value must not be ``None`` - :param message: error message if required config is not found - :raises KeyError: if required config is not found - """ - - if value is None: - value = current_app.config.get(config_name, default) - - if required and value is None: - raise RuntimeError(message) - - return value - - -class _FlaskFormCSRF(CSRF): - def setup_form(self, form): - self.meta = form.meta - return super().setup_form(form) - - def generate_csrf_token(self, csrf_token_field): - return generate_csrf( - secret_key=self.meta.csrf_secret, token_key=self.meta.csrf_field_name - ) - - def validate_csrf_token(self, form, field): - if g.get("csrf_valid", False): - # already validated by CSRFProtect - return - - try: - validate_csrf( - field.data, - self.meta.csrf_secret, - self.meta.csrf_time_limit, - self.meta.csrf_field_name, - ) - except ValidationError as e: - logger.info(e.args[0]) - raise - - -class CSRFProtect: - """Enable CSRF protection globally for a Flask app. - - :: - - app = Flask(__name__) - csrf = CSRFProtect(app) - - Checks the ``csrf_token`` field sent with forms, or the ``X-CSRFToken`` - header sent with JavaScript requests. Render the token in templates using - ``{{ csrf_token() }}``. - - See the :ref:`csrf` documentation. - """ - - def __init__(self, app=None): - self._exempt_views = set() - self._exempt_blueprints = set() - - if app: - self.init_app(app) - - def init_app(self, app): - app.extensions["csrf"] = self - - app.config.setdefault("WTF_CSRF_ENABLED", True) - app.config.setdefault("WTF_CSRF_CHECK_DEFAULT", True) - app.config["WTF_CSRF_METHODS"] = set( - app.config.get("WTF_CSRF_METHODS", ["POST", "PUT", "PATCH", "DELETE"]) - ) - app.config.setdefault("WTF_CSRF_FIELD_NAME", "csrf_token") - app.config.setdefault("WTF_CSRF_HEADERS", ["X-CSRFToken", "X-CSRF-Token"]) - app.config.setdefault("WTF_CSRF_TIME_LIMIT", 3600) - app.config.setdefault("WTF_CSRF_SSL_STRICT", True) - - app.jinja_env.globals["csrf_token"] = generate_csrf - app.context_processor(lambda: {"csrf_token": generate_csrf}) - - @app.before_request - def csrf_protect(): - if not app.config["WTF_CSRF_ENABLED"]: - return - - if not app.config["WTF_CSRF_CHECK_DEFAULT"]: - return - - if request.method not in app.config["WTF_CSRF_METHODS"]: - return - - if not request.endpoint: - return - - if app.blueprints.get(request.blueprint) in self._exempt_blueprints: - return - - view = app.view_functions.get(request.endpoint) - dest = f"{view.__module__}.{view.__name__}" - - if dest in self._exempt_views: - return - - self.protect() - - def _get_csrf_token(self): - # find the token in the form data - field_name = current_app.config["WTF_CSRF_FIELD_NAME"] - base_token = request.form.get(field_name) - - if base_token: - return base_token - - # if the form has a prefix, the name will be {prefix}-csrf_token - for key in request.form: - if key.endswith(field_name): - csrf_token = request.form[key] - - if csrf_token: - return csrf_token - - # find the token in the headers - for header_name in current_app.config["WTF_CSRF_HEADERS"]: - csrf_token = request.headers.get(header_name) - - if csrf_token: - return csrf_token - - return None - - def protect(self): - if request.method not in current_app.config["WTF_CSRF_METHODS"]: - return - - try: - validate_csrf(self._get_csrf_token()) - except ValidationError as e: - logger.info(e.args[0]) - self._error_response(e.args[0]) - - if request.is_secure and current_app.config["WTF_CSRF_SSL_STRICT"]: - if not request.referrer: - self._error_response("The referrer header is missing.") - - good_referrer = f"https://{request.host}/" - - if not same_origin(request.referrer, good_referrer): - self._error_response("The referrer does not match the host.") - - g.csrf_valid = True # mark this request as CSRF valid - - def exempt(self, view): - """Mark a view or blueprint to be excluded from CSRF protection. - - :: - - @app.route('/some-view', methods=['POST']) - @csrf.exempt - def some_view(): - ... - - :: - - bp = Blueprint(...) - csrf.exempt(bp) - - """ - - if isinstance(view, Blueprint): - self._exempt_blueprints.add(view) - return view - - if isinstance(view, str): - view_location = view - else: - view_location = ".".join((view.__module__, view.__name__)) - - self._exempt_views.add(view_location) - return view - - def _error_response(self, reason): - raise CSRFError(reason) - - -class CSRFError(BadRequest): - """Raise if the client sends invalid CSRF data with the request. - - Generates a 400 Bad Request response with the failure reason by default. - Customize the response by registering a handler with - :meth:`flask.Flask.errorhandler`. - """ - - description = "CSRF validation failed." - - -def same_origin(current_uri, compare_uri): - current = urlparse(current_uri) - compare = urlparse(compare_uri) - - return ( - current.scheme == compare.scheme - and current.hostname == compare.hostname - and current.port == compare.port - ) diff --git a/venv/Lib/site-packages/flask_wtf/file.py b/venv/Lib/site-packages/flask_wtf/file.py deleted file mode 100644 index 5646600..0000000 --- a/venv/Lib/site-packages/flask_wtf/file.py +++ /dev/null @@ -1,151 +0,0 @@ -from collections import abc - -from werkzeug.datastructures import FileStorage -from wtforms import FileField as _FileField -from wtforms import MultipleFileField as _MultipleFileField -from wtforms.validators import DataRequired -from wtforms.validators import StopValidation -from wtforms.validators import ValidationError - - -class FileField(_FileField): - """Werkzeug-aware subclass of :class:`wtforms.fields.FileField`.""" - - def process_formdata(self, valuelist): - valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x) - data = next(valuelist, None) - - if data is not None: - self.data = data - else: - self.raw_data = () - - -class MultipleFileField(_MultipleFileField): - """Werkzeug-aware subclass of :class:`wtforms.fields.MultipleFileField`. - - .. versionadded:: 1.2.0 - """ - - def process_formdata(self, valuelist): - valuelist = (x for x in valuelist if isinstance(x, FileStorage) and x) - data = list(valuelist) or None - - if data is not None: - self.data = data - else: - self.raw_data = () - - -class FileRequired(DataRequired): - """Validates that the uploaded files(s) is a Werkzeug - :class:`~werkzeug.datastructures.FileStorage` object. - - :param message: error message - - You can also use the synonym ``file_required``. - """ - - def __call__(self, form, field): - if not isinstance(field.data, list): - field.data = [field.data] - if not ( - all(isinstance(x, FileStorage) and x for x in field.data) and field.data - ): - raise StopValidation( - self.message or field.gettext("This field is required.") - ) - - -file_required = FileRequired - - -class FileAllowed: - """Validates that the uploaded file(s) is allowed by a given list of - extensions or a Flask-Uploads :class:`~flaskext.uploads.UploadSet`. - - :param upload_set: A list of extensions or an - :class:`~flaskext.uploads.UploadSet` - :param message: error message - - You can also use the synonym ``file_allowed``. - """ - - def __init__(self, upload_set, message=None): - self.upload_set = upload_set - self.message = message - - def __call__(self, form, field): - if not isinstance(field.data, list): - field.data = [field.data] - if not ( - all(isinstance(x, FileStorage) and x for x in field.data) and field.data - ): - return - - filenames = [f.filename.lower() for f in field.data] - - for filename in filenames: - if isinstance(self.upload_set, abc.Iterable): - if any(filename.endswith("." + x) for x in self.upload_set): - continue - - raise StopValidation( - self.message - or field.gettext( - "File does not have an approved extension: {extensions}" - ).format(extensions=", ".join(self.upload_set)) - ) - - if not self.upload_set.file_allowed(field.data, filename): - raise StopValidation( - self.message - or field.gettext("File does not have an approved extension.") - ) - - -file_allowed = FileAllowed - - -class FileSize: - """Validates that the uploaded file(s) is within a minimum and maximum - file size (set in bytes). - - :param min_size: minimum allowed file size (in bytes). Defaults to 0 bytes. - :param max_size: maximum allowed file size (in bytes). - :param message: error message - - You can also use the synonym ``file_size``. - """ - - def __init__(self, max_size, min_size=0, message=None): - self.min_size = min_size - self.max_size = max_size - self.message = message - - def __call__(self, form, field): - if not isinstance(field.data, list): - field.data = [field.data] - if not ( - all(isinstance(x, FileStorage) and x for x in field.data) and field.data - ): - return - - for f in field.data: - file_size = len(f.read()) - print(f, file_size, self.max_size, self.min_size) - f.seek(0) # reset cursor position to beginning of file - - if (file_size < self.min_size) or (file_size > self.max_size): - # the file is too small or too big => validation failure - raise ValidationError( - self.message - or field.gettext( - "File must be between {min_size} and {max_size} bytes.".format( - min_size=self.min_size, max_size=self.max_size - ) - ) - ) - - -file_size = FileSize diff --git a/venv/Lib/site-packages/flask_wtf/form.py b/venv/Lib/site-packages/flask_wtf/form.py deleted file mode 100644 index c7f52e0..0000000 --- a/venv/Lib/site-packages/flask_wtf/form.py +++ /dev/null @@ -1,127 +0,0 @@ -from flask import current_app -from flask import request -from flask import session -from markupsafe import Markup -from werkzeug.datastructures import CombinedMultiDict -from werkzeug.datastructures import ImmutableMultiDict -from werkzeug.utils import cached_property -from wtforms import Form -from wtforms.meta import DefaultMeta -from wtforms.widgets import HiddenInput - -from .csrf import _FlaskFormCSRF - -try: - from .i18n import translations -except ImportError: - translations = None # babel not installed - - -SUBMIT_METHODS = {"POST", "PUT", "PATCH", "DELETE"} -_Auto = object() - - -class FlaskForm(Form): - """Flask-specific subclass of WTForms :class:`~wtforms.form.Form`. - - If ``formdata`` is not specified, this will use :attr:`flask.request.form` - and :attr:`flask.request.files`. Explicitly pass ``formdata=None`` to - prevent this. - """ - - class Meta(DefaultMeta): - csrf_class = _FlaskFormCSRF - csrf_context = session # not used, provided for custom csrf_class - - @cached_property - def csrf(self): - return current_app.config.get("WTF_CSRF_ENABLED", True) - - @cached_property - def csrf_secret(self): - return current_app.config.get("WTF_CSRF_SECRET_KEY", current_app.secret_key) - - @cached_property - def csrf_field_name(self): - return current_app.config.get("WTF_CSRF_FIELD_NAME", "csrf_token") - - @cached_property - def csrf_time_limit(self): - return current_app.config.get("WTF_CSRF_TIME_LIMIT", 3600) - - def wrap_formdata(self, form, formdata): - if formdata is _Auto: - if _is_submitted(): - if request.files: - return CombinedMultiDict((request.files, request.form)) - elif request.form: - return request.form - elif request.is_json: - return ImmutableMultiDict(request.get_json()) - - return None - - return formdata - - def get_translations(self, form): - if not current_app.config.get("WTF_I18N_ENABLED", True): - return super().get_translations(form) - - return translations - - def __init__(self, formdata=_Auto, **kwargs): - super().__init__(formdata=formdata, **kwargs) - - def is_submitted(self): - """Consider the form submitted if there is an active request and - the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. - """ - - return _is_submitted() - - def validate_on_submit(self, extra_validators=None): - """Call :meth:`validate` only if the form is submitted. - This is a shortcut for ``form.is_submitted() and form.validate()``. - """ - return self.is_submitted() and self.validate(extra_validators=extra_validators) - - def hidden_tag(self, *fields): - """Render the form's hidden fields in one call. - - A field is considered hidden if it uses the - :class:`~wtforms.widgets.HiddenInput` widget. - - If ``fields`` are given, only render the given fields that - are hidden. If a string is passed, render the field with that - name if it exists. - - .. versionchanged:: 0.13 - - No longer wraps inputs in hidden div. - This is valid HTML 5. - - .. versionchanged:: 0.13 - - Skip passed fields that aren't hidden. - Skip passed names that don't exist. - """ - - def hidden_fields(fields): - for f in fields: - if isinstance(f, str): - f = getattr(self, f, None) - - if f is None or not isinstance(f.widget, HiddenInput): - continue - - yield f - - return Markup("\n".join(str(f) for f in hidden_fields(fields or self))) - - -def _is_submitted(): - """Consider the form submitted if there is an active request and - the method is ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. - """ - - return bool(request) and request.method in SUBMIT_METHODS diff --git a/venv/Lib/site-packages/flask_wtf/i18n.py b/venv/Lib/site-packages/flask_wtf/i18n.py deleted file mode 100644 index 1cc0e9c..0000000 --- a/venv/Lib/site-packages/flask_wtf/i18n.py +++ /dev/null @@ -1,47 +0,0 @@ -from babel import support -from flask import current_app -from flask import request -from flask_babel import get_locale -from wtforms.i18n import messages_path - -__all__ = ("Translations", "translations") - - -def _get_translations(): - """Returns the correct gettext translations. - Copy from flask-babel with some modifications. - """ - - if not request: - return None - - # babel should be in extensions for get_locale - if "babel" not in current_app.extensions: - return None - - translations = getattr(request, "wtforms_translations", None) - - if translations is None: - translations = support.Translations.load( - messages_path(), [get_locale()], domain="wtforms" - ) - request.wtforms_translations = translations - - return translations - - -class Translations: - def gettext(self, string): - t = _get_translations() - return string if t is None else t.ugettext(string) - - def ngettext(self, singular, plural, n): - t = _get_translations() - - if t is None: - return singular if n == 1 else plural - - return t.ungettext(singular, plural, n) - - -translations = Translations() diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py b/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py deleted file mode 100644 index 3100d37..0000000 --- a/venv/Lib/site-packages/flask_wtf/recaptcha/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .fields import RecaptchaField -from .validators import Recaptcha -from .widgets import RecaptchaWidget diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index fe79a9aef7d9d7147a94eab5c4c4a48536547539..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 380 zcmZ3^%ge<81QPq^rDg%?#~=<2FhLogwSbK23@HpLj5!Rsj8Tk?AU0DDQ!aB9Gnmbs z!;;Gy#R_Dz1T$!|zGMVy&}6*D7nGWuSWuFjk?5A0nv(();e?9-Is9-=cxFm^YKfmF z+by;<kjmmBW}peTxXKc9GE)*u@{5X#SU>{o<zR)yMXW&HN`}uslHpgRvsFxJacWU< zOtO)IsiCovS&U15a%paAUP*C`XKrG8YH>_iYF=54Pi9g~ab`)XZb4#lHdr_<C$Ts? zzPuz&zX;}1{rLFIyv&mLc)fzkUmP~M`6;D2sdhy?K$}3mDs}`CAD9^#8E-HsT|h-Q T7!)s{q8kh<7qFosP@n?<B|U4n diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/fields.cpython-311.pyc deleted file mode 100644 index eba47cb335209d9abc36f31be5779b684315a9cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1134 zcmb7C&ubGw6rS15W;bcmq7quDXoXV9!M0Y~Ls1cxN@<CpmckMkx4V<L?dFFwn?@x< z4h18qc#(RN3biUd`49LvMCc*Rso=?5q#mRv-|TjSK`*|Y{r0_`H*dc8&3s6uk_c@0 z%Uk*xBlJxMk!i=y;SxAo$UzSFP!XFLBS&#mPbsRV8u&3UR@6)le64`ZxPopXC%y?0 z#k(p(d+>&4>W+Q~WsL7K%wVC-yVP^O!_G3&YpzqEJ_k2(o7z^zx9?kGpz8+7c33II zsPN%6h@S#UicIVvQ&A9A*J4rXE;xHpfK1H+l1{9N^4L)~6Q<ro53y+gF^%bvmKDg3 zD$7+;h(34^?*Y8VTd1y727F2S*m~<1@DteI|D!_h{F)N2tK+(oS6ozAckoLoD*_HU zn^9RDQiR5<70QG`h+A@fLNc0&aq8uT=2@%M6UhzBa~;bsGcMxmHH#IvNDyLs7UzU; zNqZEmakO%MF1O4n<2ieJYUaYl>Dk<kvRy6GlFxGsMXNx0ZiALKa!c-Nj=MfRU$N}< z;CCL1t&^IcpJdUAOy=dG@rlYK)(=}e9_l@yiHPnXO!7H^Uk%hwpKYb5-Y>jc+*#bd z+)TEO;TPI7{kgtx48KWlCtJo0jF0*7+r>jzjWP;Lz$cqYNGVEIkrE=3gcQq8)sucd zArGpS7tS!*wixVkS_$ryh=+Ge40hWfl(8}kS`evY)oki{pfhGCpa_>Ie3hL946a20 zk_o>H08K?tJ2lo^*-wpqO^xrT#vAjUI2syh-0H*>ZMK8{1K24joXr?w00b|yBA*DQ zX7VltA_Uzrscd*fbV4jiIpFpQ1qA}eN5-X4pDXMPWXo1~4WOf9jN53m)jJMQy45?{ h=v=FJ9Q2K}lBXMbCvITq>u-3q{6qfz`H!i9*l%xz82tbM diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/validators.cpython-311.pyc deleted file mode 100644 index 1fd4e8df320b2b9c464813d4b27e912dd20f570a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3739 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 diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc b/venv/Lib/site-packages/flask_wtf/recaptcha/__pycache__/widgets.cpython-311.pyc deleted file mode 100644 index 6a613bad6fb737df45ae0e6d900e6d7b2bdaea14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2875 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 M-hcm(zz)Oz03GkH&j0`b diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py b/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py deleted file mode 100644 index e91fd09..0000000 --- a/venv/Lib/site-packages/flask_wtf/recaptcha/fields.py +++ /dev/null @@ -1,17 +0,0 @@ -from wtforms.fields import Field - -from . import widgets -from .validators import Recaptcha - -__all__ = ["RecaptchaField"] - - -class RecaptchaField(Field): - widget = widgets.RecaptchaWidget() - - # error message if recaptcha validation fails - recaptcha_error = None - - def __init__(self, label="", validators=None, **kwargs): - validators = validators or [Recaptcha()] - super().__init__(label, validators, **kwargs) diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py b/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py deleted file mode 100644 index c5cafb3..0000000 --- a/venv/Lib/site-packages/flask_wtf/recaptcha/validators.py +++ /dev/null @@ -1,75 +0,0 @@ -import json -from urllib import request as http -from urllib.parse import urlencode - -from flask import current_app -from flask import request -from wtforms import ValidationError - -RECAPTCHA_VERIFY_SERVER_DEFAULT = "https://www.google.com/recaptcha/api/siteverify" -RECAPTCHA_ERROR_CODES = { - "missing-input-secret": "The secret parameter is missing.", - "invalid-input-secret": "The secret parameter is invalid or malformed.", - "missing-input-response": "The response parameter is missing.", - "invalid-input-response": "The response parameter is invalid or malformed.", -} - - -__all__ = ["Recaptcha"] - - -class Recaptcha: - """Validates a ReCaptcha.""" - - def __init__(self, message=None): - if message is None: - message = RECAPTCHA_ERROR_CODES["missing-input-response"] - self.message = message - - def __call__(self, form, field): - if current_app.testing: - return True - - if request.is_json: - response = request.json.get("g-recaptcha-response", "") - else: - response = request.form.get("g-recaptcha-response", "") - remote_ip = request.remote_addr - - if not response: - raise ValidationError(field.gettext(self.message)) - - if not self._validate_recaptcha(response, remote_ip): - field.recaptcha_error = "incorrect-captcha-sol" - raise ValidationError(field.gettext(self.message)) - - def _validate_recaptcha(self, response, remote_addr): - """Performs the actual validation.""" - try: - private_key = current_app.config["RECAPTCHA_PRIVATE_KEY"] - except KeyError: - raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set") from None - - verify_server = current_app.config.get("RECAPTCHA_VERIFY_SERVER") - if not verify_server: - verify_server = RECAPTCHA_VERIFY_SERVER_DEFAULT - - data = urlencode( - {"secret": private_key, "remoteip": remote_addr, "response": response} - ) - - http_response = http.urlopen(verify_server, data.encode("utf-8")) - - if http_response.code != 200: - return False - - json_resp = json.loads(http_response.read()) - - if json_resp["success"]: - return True - - for error in json_resp.get("error-codes", []): - if error in RECAPTCHA_ERROR_CODES: - raise ValidationError(RECAPTCHA_ERROR_CODES[error]) - - return False diff --git a/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py b/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py deleted file mode 100644 index bfae830..0000000 --- a/venv/Lib/site-packages/flask_wtf/recaptcha/widgets.py +++ /dev/null @@ -1,43 +0,0 @@ -from urllib.parse import urlencode - -from flask import current_app -from markupsafe import Markup - -RECAPTCHA_SCRIPT_DEFAULT = "https://www.google.com/recaptcha/api.js" -RECAPTCHA_DIV_CLASS_DEFAULT = "g-recaptcha" -RECAPTCHA_TEMPLATE = """ -<script src='%s' async defer></script> -<div class="%s" %s></div> -""" - -__all__ = ["RecaptchaWidget"] - - -class RecaptchaWidget: - def recaptcha_html(self, public_key): - html = current_app.config.get("RECAPTCHA_HTML") - if html: - return Markup(html) - params = current_app.config.get("RECAPTCHA_PARAMETERS") - script = current_app.config.get("RECAPTCHA_SCRIPT") - if not script: - script = RECAPTCHA_SCRIPT_DEFAULT - if params: - script += "?" + urlencode(params) - attrs = current_app.config.get("RECAPTCHA_DATA_ATTRS", {}) - attrs["sitekey"] = public_key - snippet = " ".join(f'data-{k}="{attrs[k]}"' for k in attrs) # noqa: B028, B907 - div_class = current_app.config.get("RECAPTCHA_DIV_CLASS") - if not div_class: - div_class = RECAPTCHA_DIV_CLASS_DEFAULT - return Markup(RECAPTCHA_TEMPLATE % (script, div_class, snippet)) - - def __call__(self, field, error=None, **kwargs): - """Returns the recaptcha input HTML.""" - - try: - public_key = current_app.config["RECAPTCHA_PUBLIC_KEY"] - except KeyError: - raise RuntimeError("RECAPTCHA_PUBLIC_KEY config not set") from None - - return self.recaptcha_html(public_key) -- GitLab