From 582d9143a5b91f04cad6c089778cee0e51ab7e32 Mon Sep 17 00:00:00 2001 From: Evan Petersen Date: Mon, 13 Jul 2026 15:17:45 -0400 Subject: [PATCH] Add Cloudflare Turnstile authentication view --- README.md | 3 ++- ctlsettings/views.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ setup.py | 3 ++- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 ctlsettings/views.py diff --git a/README.md b/README.md index b9e05cf..4d99a0a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ First, install it with $ pip install ctlsettings -or add `ctlsettings==0.3.3` to your `requirements.txt`. +or add `ctlsettings==0.4.8` to your `requirements.txt`. ### Dependencies @@ -26,6 +26,7 @@ The following libraries are used in some way, so they'll need to be installed: * boto3 * sentry-sdk * gunicorn +* djangorestframework ### Use it diff --git a/ctlsettings/views.py b/ctlsettings/views.py new file mode 100644 index 0000000..1d65ddf --- /dev/null +++ b/ctlsettings/views.py @@ -0,0 +1,51 @@ +import requests +from django.conf import settings +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status + + +def verify_turnstile_token(token, remote_ip=None): + url = "https://challenges.cloudflare.com/turnstile/v0/siteverify" + data = { + "secret": settings.TURNSTILE_SECRET_KEY, + "response": token, + } + if remote_ip: + data["remoteip"] = remote_ip + response = requests.post(url, data=data) + result = response.json() + return result.get("success", False) + + +class LoginAPIView(APIView): + def post(self, request, *args, **kwargs): + + print("Request data:", request.data) + username = request.data.get('username') + password = request.data.get('password') + if not username or not password: + return Response( + {'error': 'Username and password must be provided'}, + status=status.HTTP_400_BAD_REQUEST + ) + # Skip Turnstile in local development + if settings.TURNSTILE_ENABLE and "localhost" not in request.META.get( + "HTTP_ORIGIN"): + token = request.data.get("turnstile_token") + if not token: + return Response( + {"error": "Missing Turnstile token"}, + status=status.HTTP_400_BAD_REQUEST + ) + is_valid = verify_turnstile_token( + token, + request.META.get("REMOTE_ADDR") + ) + if not is_valid: + return Response( + {"error": "Invalid Turnstile token"}, + status=status.HTTP_403_FORBIDDEN + ) + # Continue with authentication logic here + return Response({"success": True}) diff --git a/setup.py b/setup.py index e9e53c9..4f58275 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='ctlsettings', - version='0.4.7', + version='0.4.8', author='Columbia University Center for Teaching and Learning', author_email='ctl-dev@columbia.edu', url='https://github.com/ccnmtl/ctlsettings', @@ -20,6 +20,7 @@ 'requests', 'gunicorn', 'django-impersonate', + 'djangorestframework' ], scripts=[], license='GPL-3.0-or-later',