diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 9f1a8028..6777afcb 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -182,6 +182,9 @@ jobs:
sentry:
dsn: ${{ secrets.SENTRY_DSN }}
jsLoaderUrl: ${{ secrets.SENTRY_JS_LOADER_URL }}
+ recaptcha:
+ siteKey: ${{ secrets.RECAPTCHA_SITE_KEY }}
+ secretKey: ${{ secrets.RECAPTCHA_SECRET_KEY }}
mailer:
primary:
host: ${{ secrets.DJANGO_MAILER_PRIMARY_HOST }}
diff --git a/charts/templates/web-secret.yaml b/charts/templates/web-secret.yaml
index bc4d6ed0..160b0c0b 100644
--- a/charts/templates/web-secret.yaml
+++ b/charts/templates/web-secret.yaml
@@ -17,6 +17,9 @@
"DJANGO_MAILER_PRIMARY_HOST_PORT" (.Values.secrets.mailer.primary.port | b64enc)
"DJANGO_MAILER_PRIMARY_HOST_USER" (.Values.secrets.mailer.primary.user | b64enc)
"DJANGO_MAILER_PRIMARY_HOST_PASSWORD" (.Values.secrets.mailer.primary.password | b64enc)
+
+ "DJANGO_RECAPTCHA_SITE_KEY" (.Values.secrets.recaptcha.siteKey | b64enc)
+ "DJANGO_RECAPTCHA_SECRET_KEY" (.Values.secrets.recaptcha.secretKey | b64enc)
)
}}
{{- include "fiesta.secret" (merge (dict "Args" $data) . ) -}}
diff --git a/charts/values.yaml b/charts/values.yaml
index 14868138..80b01eea 100644
--- a/charts/values.yaml
+++ b/charts/values.yaml
@@ -39,6 +39,9 @@ secrets:
sentry:
dsn: dsn
jsLoaderUrl: url
+ recaptcha:
+ siteKey: key
+ secretKey: key
mailer:
primary:
host: host
diff --git a/fiesta/.env.base b/fiesta/.env.base
index d73b9203..16308cf5 100644
--- a/fiesta/.env.base
+++ b/fiesta/.env.base
@@ -1,3 +1,6 @@
DJANGO_BUILD_DIR=/usr/src/build
DJANGO_STATIC_ROOT=/usr/src/static
DJANGO_MEDIA_ROOT=/usr/src/media
+# test keys are NOT suitable for v2 recaptcha
+DJANGO_RECAPTCHA_PUBLIC_KEY=" "
+DJANGO_RECAPTCHA_PRIVATE_KEY=" "
diff --git a/fiesta/apps/accounts/forms/sign_up.py b/fiesta/apps/accounts/forms/sign_up.py
new file mode 100644
index 00000000..a95ecaab
--- /dev/null
+++ b/fiesta/apps/accounts/forms/sign_up.py
@@ -0,0 +1,14 @@
+from __future__ import annotations
+
+from allauth.account.forms import SignupForm as AllauthSignupForm
+from django_recaptcha.fields import ReCaptchaField
+from django_recaptcha.widgets import ReCaptchaV3
+
+
+class SignupForm(AllauthSignupForm):
+ recaptcha = ReCaptchaField(
+ widget=ReCaptchaV3(
+ action="signup",
+ attrs={"theme": "clean"},
+ )
+ )
diff --git a/fiesta/apps/accounts/templates/account/signup_form.html b/fiesta/apps/accounts/templates/account/signup_form.html
index 070dafd4..6966d159 100644
--- a/fiesta/apps/accounts/templates/account/signup_form.html
+++ b/fiesta/apps/accounts/templates/account/signup_form.html
@@ -23,6 +23,11 @@
{{ card_title|default:"Create Account" }}
{% include "fiestaforms/parts/field.html" with bf=form.email errors=form.errors.email %}
{% include "fiestaforms/parts/field.html" with bf=form.password1 errors=form.errors.password1 %}
{% include "fiestaforms/parts/field.html" with bf=form.password2 errors=form.errors.password2 %}
+
+
Sign up
Already registered?
diff --git a/fiesta/fiesta/settings/__init__.py b/fiesta/fiesta/settings/__init__.py
index 53abf72e..e19e1924 100644
--- a/fiesta/fiesta/settings/__init__.py
+++ b/fiesta/fiesta/settings/__init__.py
@@ -39,6 +39,8 @@ class Development(Base):
USE_WEBPACK_INTEGRITY = False
+ SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"]
+
def INSTALLED_APPS(self):
return super().INSTALLED_APPS + ["debug_toolbar"]
diff --git a/fiesta/fiesta/settings/auth.py b/fiesta/fiesta/settings/auth.py
index d58c98c1..50737b9d 100644
--- a/fiesta/fiesta/settings/auth.py
+++ b/fiesta/fiesta/settings/auth.py
@@ -1,5 +1,7 @@
from __future__ import annotations
+from configurations.values import SecretValue
+
class AuthConfigMixin:
AUTH_PASSWORD_VALIDATORS = [
@@ -75,6 +77,10 @@ class AuthConfigMixin:
ACCOUNT_USERNAME_REQUIRED = False # email ftw
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
+ ACCOUNT_FORMS = {
+ "signup": "apps.accounts.forms.sign_up.SignupForm",
+ }
+
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
SOCIALACCOUNT_EMAIL_VERIFICATION = "optional"
# social account settings
@@ -92,3 +98,8 @@ class AuthConfigMixin:
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True # logout after password change
ACCOUNT_USERNAME_MIN_LENGTH = 4 # a personal preference
+
+ RECAPTCHA_PUBLIC_KEY = SecretValue()
+ RECAPTCHA_PRIVATE_KEY = SecretValue()
+
+ RECAPTCHA_USE_SSL = True # Defaults to False
diff --git a/fiesta/fiesta/settings/logging.py b/fiesta/fiesta/settings/logging.py
index bb18fdf3..9bdb9ce5 100644
--- a/fiesta/fiesta/settings/logging.py
+++ b/fiesta/fiesta/settings/logging.py
@@ -58,6 +58,10 @@ def LOGGING(self):
"handlers": ["console"],
"level": self.LOG_LEVEL,
},
+ "django_recaptcha": {
+ "handlers": ["console"],
+ "level": self.LOG_LEVEL,
+ },
},
}
diff --git a/fiesta/fiesta/settings/project.py b/fiesta/fiesta/settings/project.py
index 4a241c58..9639f133 100644
--- a/fiesta/fiesta/settings/project.py
+++ b/fiesta/fiesta/settings/project.py
@@ -101,6 +101,8 @@ def DEFAULT_FROM_EMAIL(self):
"allauth_cas",
# superuser can log in as any user
"loginas",
+ # recaptcha
+ "django_recaptcha",
# editorjs integration
"django_editorjs_fields",
# location fields
diff --git a/poetry.lock b/poetry.lock
index 655393b2..7c8594e7 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -869,6 +869,20 @@ files = [
[package.dependencies]
Django = ">=2.1"
+[[package]]
+name = "django-recaptcha"
+version = "4.0.0"
+description = "Django recaptcha form field/widget app."
+optional = false
+python-versions = "*"
+files = [
+ {file = "django-recaptcha-4.0.0.tar.gz", hash = "sha256:5316438f97700c431d65351470d1255047e3f2cd9af0f2f13592b637dad9213e"},
+ {file = "django_recaptcha-4.0.0-py3-none-any.whl", hash = "sha256:0d912d5c7c009df4e47accd25029133d47a74342dbd2a8edc2877b6bffa971a3"},
+]
+
+[package.dependencies]
+django = "*"
+
[[package]]
name = "django-select2"
version = "7.11.1"
@@ -2132,4 +2146,4 @@ anyio = ">=3.0.0"
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "716d16cbcfcba887f4be4fdd30f814911acb8e539f3277f62803921c96ac6871"
+content-hash = "43d00087dadb290162ad108ddf32623fd1204085dc55e24e7f45680a996ddf58"
diff --git a/pyproject.toml b/pyproject.toml
index be7731b4..e4b9a0d4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -55,6 +55,7 @@ django-admin-env-notice = "^1.0"
cryptography = "^43.0.0"
django-admin-relation-links = "^0.2.5"
django-mailer = "^2.3.1"
+django-recaptcha = "^4.0.0"
[tool.poetry.dev-dependencies]
@@ -96,17 +97,16 @@ exclude = '''
[tool.ruff]
# see https://beta.ruff.rs/docs/rules/
-extend-select = ["UP", "DJ", "PIE", "INT", "PTH", "SIM", "RET", "G", "DTZ", "B", "I002"]
line-length = 120
target-version = "py311"
-ignore = ["E731"]
exclude = [
"migrations",
]
-[tool.ruff.isort]
-required-imports = ["from __future__ import annotations"]
+lint.ignore = ["E731"]
+extend-select = ["UP", "DJ", "PIE", "INT", "PTH", "SIM", "RET", "G", "DTZ", "B", "I002"]
+lint.isort.required-imports = ["from __future__ import annotations"]
[tool.vulture]