aboutsummaryrefslogtreecommitdiffstats
path: root/backend/routes
diff options
context:
space:
mode:
Diffstat (limited to 'backend/routes')
-rw-r--r--backend/routes/forms/new.py5
-rw-r--r--backend/routes/forms/submit.py95
2 files changed, 76 insertions, 24 deletions
diff --git a/backend/routes/forms/new.py b/backend/routes/forms/new.py
index ff39f12..6437a4a 100644
--- a/backend/routes/forms/new.py
+++ b/backend/routes/forms/new.py
@@ -26,5 +26,10 @@ class FormCreate(Route):
except ValidationError as e:
return JSONResponse(e.errors())
+ if await request.state.db.forms.find_one({"_id": form.id}):
+ return JSONResponse({
+ "error": "Form with same ID already exists."
+ }, status_code=400)
+
await request.state.db.forms.insert_one(form.dict(by_alias=True))
return JSONResponse(form.dict())
diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py
index a94a1c9..3ecbda0 100644
--- a/backend/routes/forms/submit.py
+++ b/backend/routes/forms/submit.py
@@ -4,15 +4,24 @@ Submit a form.
import binascii
import hashlib
+import uuid
-import jwt
+import httpx
+import pydnsbl
+from pydantic import ValidationError
from starlette.requests import Request
from starlette.responses import JSONResponse
-from backend.constants import SECRET_KEY
+from backend.constants import HCAPTCHA_API_SECRET, FormFeatures
+from backend.models import Form, FormResponse
from backend.route import Route
+HCAPTCHA_VERIFY_URL = "https://hcaptcha.com/siteverify"
+HCAPTCHA_HEADERS = {
+ "Content-Type": "application/x-www-form-urlencoded"
+}
+
class SubmitForm(Route):
"""
@@ -28,40 +37,78 @@ class SubmitForm(Route):
if form := await request.state.db.forms.find_one(
{"_id": request.path_params["form_id"], "features": "OPEN"}
):
- response_obj = {}
+ form = Form(**form)
+ response = data.copy()
+ response["id"] = str(uuid.uuid4())
+ response["form_id"] = form.id
- if "DISABLE_ANTISPAM" not in form["features"]:
+ if FormFeatures.DISABLE_ANTISPAM.value not in form.features:
ip_hash_ctx = hashlib.md5()
ip_hash_ctx.update(request.client.host.encode())
ip_hash = binascii.hexlify(ip_hash_ctx.digest())
+ user_agent_hash_ctx = hashlib.md5()
+ user_agent_hash_ctx.update(request.headers["User-Agent"].encode())
+ user_agent_hash = binascii.hexlify(user_agent_hash_ctx.digest())
- response_obj["antispam"] = {
- "ip": ip_hash.decode()
- }
+ dsn_checker = pydnsbl.DNSBLIpChecker()
+ dsn_blacklist = await dsn_checker.check_async(request.client.host)
- if "REQUIRES_LOGIN" in form["features"]:
- if token := data.get("token"):
- data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
- response_obj["user"] = {
- "user": f"{data['username']}#{data['discriminator']}",
- "id": data["id"]
+ async with httpx.AsyncClient() as client:
+ query_params = {
+ "secret": HCAPTCHA_API_SECRET,
+ "response": data.get("captcha")
}
+ r = await client.post(
+ HCAPTCHA_VERIFY_URL,
+ params=query_params,
+ headers=HCAPTCHA_HEADERS
+ )
+ r.raise_for_status()
+ captcha_data = r.json()
+
+ response["antispam"] = {
+ "ip_hash": ip_hash.decode(),
+ "user_agent_hash": user_agent_hash.decode(),
+ "captcha_pass": captcha_data["success"],
+ "dns_blacklisted": dsn_blacklist.blacklisted,
+ }
+
+ if FormFeatures.REQUIRES_LOGIN.value in form.features:
+ if request.user.is_authenticated:
+ response["user"] = request.user.payload
- if "COLLECT_EMAIL" in form["features"]:
- if data.get("email"):
- response_obj["user"]["email"] = data["email"]
- else:
- return JSONResponse({
- "error": "User data did not include email information"
- })
+ if FormFeatures.COLLECT_EMAIL.value in form.features and "email" not in response["user"]: # noqa
+ return JSONResponse({
+ "error": "email_required"
+ }, status_code=400)
else:
return JSONResponse({
- "error": "Missing Discord user data"
- })
+ "error": "missing_discord_data"
+ }, status_code=400)
+
+ missing_fields = []
+ for question in form.questions:
+ if question.id not in response["response"]:
+ missing_fields.append(question.id)
+
+ if missing_fields:
+ return JSONResponse({
+ "error": "missing_fields",
+ "fields": missing_fields
+ }, status_code=400)
+
+ try:
+ response_obj = FormResponse(**response)
+ except ValidationError as e:
+ return JSONResponse(e.errors())
+
+ await request.state.db.responses.insert_one(
+ response_obj.dict(by_alias=True)
+ )
return JSONResponse({
- "form": form,
- "response": response_obj
+ "form": form.dict(),
+ "response": response_obj.dict()
})
else:
return JSONResponse({