From e6c452d000c303374d2c6b36cd8c8e1aa8a6ba2e Mon Sep 17 00:00:00 2001 From: aronwk-aaron Date: Fri, 17 Nov 2023 00:55:18 -0600 Subject: [PATCH 1/3] feat: add recaptcha support --- app/__init__.py | 55 ++++++-- app/forms.py | 5 + app/settings_example.py | 10 ++ app/templates/admin/dashboard.html.j2 | 188 -------------------------- app/templates/main/about.html.j2 | 1 - 5 files changed, 58 insertions(+), 201 deletions(-) delete mode 100644 app/templates/admin/dashboard.html.j2 diff --git a/app/__init__.py b/app/__init__.py index e14672a..5ba9792 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -204,19 +204,23 @@ def register_settings(app): # Load environment specific settings app.config['TESTING'] = False app.config['DEBUG'] = False + app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { + "pool_pre_ping": True, + "pool_size": 10, + "max_overflow": 2, + "pool_recycle": 300, + "pool_pre_ping": True, + "pool_use_lifo": True + } - # always pull these two from the env app.config['SECRET_KEY'] = os.getenv( 'APP_SECRET_KEY', app.config['APP_SECRET_KEY'] - ) app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( 'APP_DATABASE_URI', app.config['APP_DATABASE_URI'] ) - - # try to get overides, otherwise just use what we have already app.config['USER_ENABLE_REGISTER'] = os.getenv( 'USER_ENABLE_REGISTER', app.config['USER_ENABLE_REGISTER'] @@ -241,14 +245,6 @@ def register_settings(app): 'USER_REQUIRE_INVITATION', app.config['USER_REQUIRE_INVITATION'] ) - app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { - "pool_pre_ping": True, - "pool_size": 10, - "max_overflow": 2, - "pool_recycle": 300, - "pool_pre_ping": True, - "pool_use_lifo": True - } app.config['MAIL_SERVER'] = os.getenv( 'MAIL_SERVER', app.config['MAIL_SERVER'] @@ -312,6 +308,41 @@ def register_settings(app): app.config['CACHE_LOCATION'] ) + # Recaptcha settings + if "RECAPTCHA_ENABLE" not in app.config: + app.config['RECAPTCHA_ENABLE'] = False + app.config['RECAPTCHA_ENABLE'] = os.getenv( + 'RECAPTCHA_ENABLE', + app.config['RECAPTCHA_ENABLE'] + ) + if "RECAPTCHA_PUBLIC_KEY" not in app.config: + app.config['RECAPTCHA_PUBLIC_KEY'] = '' + app.config['RECAPTCHA_PUBLIC_KEY'] = os.getenv( + 'RECAPTCHA_PUBLIC_KEY', + app.config['RECAPTCHA_PUBLIC_KEY'] + ) + if "RECAPTCHA_PRIVATE_KEY" not in app.config: + app.config['RECAPTCHA_PRIVATE_KEY'] = '' + app.config['RECAPTCHA_PRIVATE_KEY'] = os.getenv( + 'RECAPTCHA_PRIVATE_KEY', + app.config['RECAPTCHA_PRIVATE_KEY'] + ) + # Optional + app.config['RECAPTCHA_API_SERVER'] = os.getenv( + 'RECAPTCHA_API_SERVER', + app.config['RECAPTCHA_API_SERVER'] + ) + app.config['RECAPTCHA_PARAMETERS'] = os.getenv( + 'RECAPTCHA_PARAMETERS', + app.config['RECAPTCHA_PARAMETERS'] + ) + if "RECAPTCHA_DATA_ATTRS" not in app.config: + app.config['RECAPTCHA_DATA_ATTRS'] = {'theme': 'white', 'size': 'invisible'} + app.config['RECAPTCHA_DATA_ATTRS'] = os.getenv( + 'RECAPTCHA_DATA_ATTRS', + app.config['RECAPTCHA_DATA_ATTRS'] + ) + def gm_level(gm_level): diff --git a/app/forms.py b/app/forms.py index 51a4a1f..5898611 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,4 +1,5 @@ from flask_wtf import FlaskForm +from flask_wtf.recaptcha import RecaptchaField from flask import current_app from flask_user.forms import ( @@ -84,6 +85,10 @@ class CustomRegisterForm(FlaskForm): ]) invite_token = HiddenField('Token') + + # Use recaptcha if config enables recaptcha + if current_app.config["ENABLE_RECAPTCHA"]: + recaptcha = RecaptchaField() submit = SubmitField('Register') diff --git a/app/settings_example.py b/app/settings_example.py index df7a992..a449e51 100644 --- a/app/settings_example.py +++ b/app/settings_example.py @@ -61,3 +61,13 @@ USER_AFTER_LOGOUT_ENDPOINT = "main.index" # Option will be removed once this feature is full implemeted ENABLE_CHAR_XML_UPLOAD = False + +# Recaptcha settings +# See: https://flask-wtf.readthedocs.io/en/1.2.x/form/#recaptcha +RECAPTCHA_ENABLE = False +RECAPTCHA_PUBLIC_KEY = '' +RECAPTCHA_PRIVATE_KEY = '' +# Optional +# RECAPTCHA_API_SERVER = '' +# RECAPTCHA_PARAMETERS = '' +RECAPTCHA_DATA_ATTRS = {'theme': 'white', 'size': 'invisible'} diff --git a/app/templates/admin/dashboard.html.j2 b/app/templates/admin/dashboard.html.j2 deleted file mode 100644 index 3a90294..0000000 --- a/app/templates/admin/dashboard.html.j2 +++ /dev/null @@ -1,188 +0,0 @@ -{% extends "bootstrap/base.html" %} -{% block title %}Key Creation{% endblock %} - -{% block navbar %} - -{% endblock navbar %}} - -{% block content %} -{# LOGO #} -
- - {# Display logo #} -
- Logo -
- -
- -{# Key creation #} -
-
-

Key Creation

-
- -
-
-
- - {# If the error value is set, display the error in red text #} - {% if error %} -
- {{ error }} -
- {% endif %} - - {# If the message value is set, display the message in green text #} - {% if message %} -
- {{ message }} -
- {% endif %} - - {# Form which takes in Admin Username, Admin Password, and the amount of keys to create. #} -
- {# Key count input #} -
- - - Number of keys to create. -
- - {# Submit button #} -
- -
-
- - {# If the keys value is set, create a list for each key in keys #} - {% if keys %} -
-
    - {% for key in keys %} -
  • {{ key }}
  • - {% endfor %} -
-
- {% endif %} -
-
-
-
- - -{# Activity graphs #} -
- -
-

Activity

-
- -
-
- -
- - - - - -
- -
-
-
-{% endblock %} - -{% block scripts %} - - - - -{% endblock scripts %}} \ No newline at end of file diff --git a/app/templates/main/about.html.j2 b/app/templates/main/about.html.j2 index 0e297f1..3cec998 100644 --- a/app/templates/main/about.html.j2 +++ b/app/templates/main/about.html.j2 @@ -82,7 +82,6 @@ {% endif %} -
Source From 7633053490eff99945bddda556aa5adccbb7a1e3 Mon Sep 17 00:00:00 2001 From: aronwk-aaron Date: Fri, 17 Nov 2023 16:38:01 -0600 Subject: [PATCH 2/3] it works --- app/__init__.py | 29 ++++---- app/forms.py | 68 ++++++------------- app/templates/flask_user/login.html | 7 +- .../flask_user/login_or_register.html | 10 +++ app/templates/flask_user/register.html | 5 ++ 5 files changed, 56 insertions(+), 63 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 5ba9792..756b8ed 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -328,20 +328,21 @@ def register_settings(app): app.config['RECAPTCHA_PRIVATE_KEY'] ) # Optional - app.config['RECAPTCHA_API_SERVER'] = os.getenv( - 'RECAPTCHA_API_SERVER', - app.config['RECAPTCHA_API_SERVER'] - ) - app.config['RECAPTCHA_PARAMETERS'] = os.getenv( - 'RECAPTCHA_PARAMETERS', - app.config['RECAPTCHA_PARAMETERS'] - ) - if "RECAPTCHA_DATA_ATTRS" not in app.config: - app.config['RECAPTCHA_DATA_ATTRS'] = {'theme': 'white', 'size': 'invisible'} - app.config['RECAPTCHA_DATA_ATTRS'] = os.getenv( - 'RECAPTCHA_DATA_ATTRS', - app.config['RECAPTCHA_DATA_ATTRS'] - ) + if "RECAPTCHA_API_SERVER" in app.config: + app.config['RECAPTCHA_API_SERVER'] = os.getenv( + 'RECAPTCHA_API_SERVER', + app.config['RECAPTCHA_API_SERVER'] + ) + if "RECAPTCHA_PARAMETERS" in app.config: + app.config['RECAPTCHA_PARAMETERS'] = os.getenv( + 'RECAPTCHA_PARAMETERS', + app.config['RECAPTCHA_PARAMETERS'] + ) + if "RECAPTCHA_DATA_ATTRS" in app.config: + app.config['RECAPTCHA_DATA_ATTRS'] = os.getenv( + 'RECAPTCHA_DATA_ATTRS', + app.config['RECAPTCHA_DATA_ATTRS'] + ) diff --git a/app/forms.py b/app/forms.py index 5898611..30d9be3 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,18 +1,15 @@ -from flask_wtf import FlaskForm -from flask_wtf.recaptcha import RecaptchaField +from flask_wtf import FlaskForm, Recaptcha, RecaptchaField from flask import current_app from flask_user.forms import ( unique_email_validator, - password_validator, - unique_username_validator + LoginForm, + RegisterForm ) from flask_user import UserManager from wtforms.widgets import TextArea, NumberInput from wtforms import ( StringField, - HiddenField, - PasswordField, BooleanField, SubmitField, validators, @@ -37,35 +34,21 @@ def validate_play_key(form, field): field.data = PlayKey.key_is_valid(key_string=field.data) return +def validate_recaptcha(form, field): + current_app.logger.info("start validating with recaptcha") + if current_app.config["RECAPTCHA_ENABLE"]: + current_app.logger.info("validating with recaptcha") + return Recaptcha() + current_app.logger.info("skipping validating with recaptcha") + return True + class CustomUserManager(UserManager): def customize(self, app): self.RegisterFormClass = CustomRegisterForm + self.LoginFormClass = CustomLoginForm - -class CustomRegisterForm(FlaskForm): - """Registration form""" - next = HiddenField() - reg_next = HiddenField() - - # Login Info - email = StringField( - 'E-Mail', - validators=[ - Optional(), - validators.Email('Invalid email address'), - unique_email_validator, - ] - ) - - username = StringField( - 'Username', - validators=[ - DataRequired(), - unique_username_validator, - ] - ) - +class CustomRegisterForm(RegisterForm): play_key_id = StringField( 'Play Key', validators=[ @@ -73,25 +56,14 @@ class CustomRegisterForm(FlaskForm): validate_play_key, ] ) + recaptcha = RecaptchaField( + validators=[validate_recaptcha] + ) - password = PasswordField('Password', validators=[ - DataRequired(), - password_validator, - validators.length(max=40, message="The maximum length of the password is 40 characters due to game client limitations") - ]) - retype_password = PasswordField('Retype Password', validators=[ - validators.EqualTo('password', message='Passwords did not match'), - validators.length(max=40, message="The maximum length of the password is 40 characters due to game client limitations") - ]) - - invite_token = HiddenField('Token') - - # Use recaptcha if config enables recaptcha - if current_app.config["ENABLE_RECAPTCHA"]: - recaptcha = RecaptchaField() - - submit = SubmitField('Register') - +class CustomLoginForm(LoginForm): + recaptcha = RecaptchaField( + validators=[validate_recaptcha] + ) class CreatePlayKeyForm(FlaskForm): diff --git a/app/templates/flask_user/login.html b/app/templates/flask_user/login.html index c8d7e3d..2353edd 100644 --- a/app/templates/flask_user/login.html +++ b/app/templates/flask_user/login.html @@ -60,7 +60,12 @@ {# Remember me #} {% if user_manager.USER_ENABLE_REMEMBER_ME %} - {{ render_checkbox_field(login_form.remember_me, tabindex=130) }} + {{ render_checkbox_field(login_form.remember_me, tabindex=130) }} + {% endif %} + + {# recaptcha #} + {% if config.RECAPTCHA_ENABLE %} + {{ render_field(form.recaptcha, tabindex=250) }} {% endif %} {# Submit button #} diff --git a/app/templates/flask_user/login_or_register.html b/app/templates/flask_user/login_or_register.html index e51a6f1..59fd755 100644 --- a/app/templates/flask_user/login_or_register.html +++ b/app/templates/flask_user/login_or_register.html @@ -29,6 +29,11 @@ {{ render_checkbox_field(login_form.remember_me, tabindex=130) }} {% endif %} + {# recaptcha #} + {% if config.RECAPTCHA_ENABLE %} + {{ form.recaptcha }} + {% endif %} + {# Submit button #} {{ render_submit_field(login_form.submit, tabindex=180) }} @@ -63,6 +68,11 @@ {{ render_field(register_form.retype_password, tabindex=240) }} {% endif %} + {# recaptcha #} + {% if config.RECAPTCHA_ENABLE %} + {{ register_form.recaptcha }} + {% endif %} + {{ render_submit_field(register_form.submit, tabindex=280) }} diff --git a/app/templates/flask_user/register.html b/app/templates/flask_user/register.html index f6a6e28..99bc9d3 100644 --- a/app/templates/flask_user/register.html +++ b/app/templates/flask_user/register.html @@ -47,6 +47,11 @@ {{ render_field(form.retype_password, tabindex=240) }} {% endif %} + {# recaptcha #} + {% if config.RECAPTCHA_ENABLE %} + {{ render_field(form.recaptcha, tabindex=250) }} + {% endif %} + {{ render_submit_field(form.submit, tabindex=280) }} From b7e48bb656c1ac0c46442f1a5c596d684bc6f4dc Mon Sep 17 00:00:00 2001 From: aronwk-aaron Date: Fri, 17 Nov 2023 17:40:01 -0600 Subject: [PATCH 3/3] extend the recaptha validator to make it optional --- app/forms.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/forms.py b/app/forms.py index 30d9be3..1926724 100644 --- a/app/forms.py +++ b/app/forms.py @@ -34,13 +34,11 @@ def validate_play_key(form, field): field.data = PlayKey.key_is_valid(key_string=field.data) return -def validate_recaptcha(form, field): - current_app.logger.info("start validating with recaptcha") - if current_app.config["RECAPTCHA_ENABLE"]: - current_app.logger.info("validating with recaptcha") - return Recaptcha() - current_app.logger.info("skipping validating with recaptcha") - return True +class CustomRecaptcha(Recaptcha): + def __call__(self, form, field): + if not current_app.config.get("RECAPTCHA_ENABLE", False): + return True + return super(CustomRecaptcha, self).__call__(form, field) class CustomUserManager(UserManager): @@ -57,12 +55,12 @@ class CustomRegisterForm(RegisterForm): ] ) recaptcha = RecaptchaField( - validators=[validate_recaptcha] + validators=[CustomRecaptcha()] ) class CustomLoginForm(LoginForm): recaptcha = RecaptchaField( - validators=[validate_recaptcha] + validators=[CustomRecaptcha()] ) class CreatePlayKeyForm(FlaskForm):