Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .installation_state/django_migrations.done
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2026-06-07T07:47:07Z
1 change: 1 addition & 0 deletions .installation_state/env_file.done
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2026-06-07T07:36:50Z
1 change: 1 addition & 0 deletions .installation_state/superuser.done
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2026-06-07T07:49:33Z
2 changes: 1 addition & 1 deletion nrm_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ def resolve_env_path(name, default="", *, trailing_sep=False):
FERNET_KEY = env("FERNET_KEY")

API_KEY = env("API_KEY", default="")

RECAPTCHA_SECRET_KEY = env("RECAPTCHA_SECRET_KEY", default="")

lulc_years = [
"2017_2018",
Expand Down
96 changes: 87 additions & 9 deletions users/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import logging
import token
import requests

from django.conf import settings
from django.contrib.auth.models import Group
Expand Down Expand Up @@ -33,6 +35,45 @@

logger = logging.getLogger(__name__)

def verify_recaptcha(token):
try:
response = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={
"secret": settings.RECAPTCHA_SECRET_KEY,
"response": token,
},
timeout=10,
)

response.raise_for_status()

result = response.json()
success = result.get("success", False)

if not success:
logger.warning(
"reCAPTCHA verification failed. Response: %s",
result,
)

return success
except requests.exceptions.Timeout:
logger.error("reCAPTCHA request timed out")
return False
except requests.exceptions.RequestException as exc:
logger.exception(
"Error while verifying reCAPTCHA: %s",
str(exc),
)
return False
except Exception as exc:
logger.exception(
"Unexpected error during reCAPTCHA verification: %s",
str(exc),
)
return False


class RegisterView(viewsets.GenericViewSet, generics.CreateAPIView):
"""API endpoint for user registration."""
Expand Down Expand Up @@ -95,6 +136,8 @@ def post(self, request, *args, **kwargs):
},
status=status.HTTP_201_CREATED,
)




class LoginView(TokenObtainPairView):
Expand All @@ -107,16 +150,51 @@ class LoginView(TokenObtainPairView):

def post(self, request, *args, **kwargs):
# Call parent class method to validate credentials and get tokens
response = super().post(request, *args, **kwargs)

token = response.data.get("access")
jwt_auth = JWTAuthentication()
validated_token = jwt_auth.get_validated_token(token)
user = jwt_auth.get_user(validated_token)

response.data["user"] = UserSerializer(user).data
try:
captcha_token = request.data.get("captcha")
if not captcha_token:
logger.warning(
"Login attempt without captcha. Username: %s",
request.data.get("username"),
)
return Response(
{"message": "Captcha is required"},
status=status.HTTP_400_BAD_REQUEST,
)
if not verify_recaptcha(captcha_token):
logger.warning(
"Invalid captcha during login. Username: %s",
request.data.get("username"),
)
return Response(
{"message": "Invalid captcha"},
status=status.HTTP_400_BAD_REQUEST,
)

response = super().post(request, *args, **kwargs)

token = response.data.get("access")
jwt_auth = JWTAuthentication()
validated_token = jwt_auth.get_validated_token(token)
user = jwt_auth.get_user(validated_token)
logger.info(
"User logged in successfully. User ID: %s Username: %s",
user.id,
user.username,
)
response.data["user"] = UserSerializer(user).data
return response
except Exception as exc:
logger.exception(
"Unexpected error during login: %s",
str(exc),
)

return response
return Response(
{"message": "An error occurred during login"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)



class LogoutView(generics.GenericAPIView):
Expand Down