[Feature] Add Captchas to avoid spam registrations (#358)

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#358
This commit is contained in:
2026-03-13 11:36:48 +13:00
parent 8cdf91c8fb
commit 21f3390a43
4 changed files with 45 additions and 1 deletions

View File

@ -408,3 +408,9 @@ if MS_ENTRA_ENABLED:
# Site ID 10 -> beta.envipath.org # Site ID 10 -> beta.envipath.org
MATOMO_SITE_ID = os.environ.get("MATOMO_SITE_ID", "10") MATOMO_SITE_ID = os.environ.get("MATOMO_SITE_ID", "10")
# CAP
CAP_ENABLED = os.environ.get("CAP_ENABLED", "False") == "True"
CAP_API_BASE = os.environ.get("CAP_API_BASE", None)
CAP_SITE_KEY = os.environ.get("CAP_SITE_KEY", None)
CAP_SECRET_KEY = os.environ.get("CAP_SECRET_KEY", None)

View File

@ -3,6 +3,7 @@ import logging
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List, Iterable from typing import Any, Dict, List, Iterable
import requests
import nh3 import nh3
from django.conf import settings as s from django.conf import settings as s
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -146,6 +147,8 @@ def handler500(request):
def login(request): def login(request):
context = get_base_context(request) context = get_base_context(request)
context["CAP_API_BASE"] = s.CAP_API_BASE
context["CAP_SITE_KEY"] = s.CAP_SITE_KEY
if request.method == "GET": if request.method == "GET":
context["title"] = "enviPath" context["title"] = "enviPath"
@ -238,6 +241,33 @@ def register(request):
if next := request.POST.get("next"): if next := request.POST.get("next"):
context["next"] = next context["next"] = next
# Catpcha
if s.CAP_ENABLED:
cap_token = request.POST.get("cap-token")
if not cap_token:
context["message"] = "Missing CAP Token."
return render(request, "static/login.html", context)
verify_url = f"{s.CAP_API_BASE}/{s.CAP_SITE_KEY}/siteverify"
payload = {
"secret": s.CAP_SECRET_KEY,
"response": cap_token,
}
try:
resp = requests.post(verify_url, json=payload, timeout=10)
resp.raise_for_status()
verify_data = resp.json()
except requests.RequestException:
context["message"] = "Captcha verification failed."
return render(request, "static/login.html", context)
if not verify_data.get("success"):
context["message"] = "Captcha check failed. Please try again."
return render(request, "static/login.html", context)
# End Captcha
username = request.POST.get("username", "").strip() username = request.POST.get("username", "").strip()
email = request.POST.get("email", "").strip() email = request.POST.get("email", "").strip()
password = request.POST.get("password", "").strip() password = request.POST.get("password", "").strip()

View File

@ -218,6 +218,10 @@
<input type="hidden" name="next" value="{{ next }}" /> <input type="hidden" name="next" value="{{ next }}" />
<cap-widget
data-cap-api-endpoint="{{ CAP_API_BASE }}/{{ CAP_SITE_KEY }}/"
></cap-widget>
<!-- ToS and Academic Use Notice --> <!-- ToS and Academic Use Notice -->
<div class="text-xs text-base-content/70 mt-2"> <div class="text-xs text-base-content/70 mt-2">
<p> <p>
@ -233,7 +237,6 @@
enviPath is free for academic and non-commercial use only. enviPath is free for academic and non-commercial use only.
</p> </p>
</div> </div>
<button type="submit" name="confirmsignup" class="btn btn-success w-full"> <button type="submit" name="confirmsignup" class="btn btn-success w-full">
Sign Up Sign Up
</button> </button>

View File

@ -19,6 +19,11 @@
type="text/css" type="text/css"
/> />
<script src="https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.41/cap.min.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@cap.js/widget@0.1.41/src/cap.min.css"
/>
{% block extra_styles %}{% endblock %} {% block extra_styles %}{% endblock %}
</head> </head>
<body class="bg-base-100"> <body class="bg-base-100">