registration and verify abuse WIP
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 1m27s

This commit is contained in:
i701 2025-04-15 14:01:47 +05:00
parent 9f3f586181
commit e0a80d4a00
6 changed files with 100 additions and 6 deletions

View File

@ -2,10 +2,12 @@
This is the models module for api. This is the models module for api.
""" """
from datetime import timedelta
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db import models from django.db import models
from .managers import CustomUserManager from .managers import CustomUserManager
from django.utils import timezone from django.utils import timezone
import pyotp
class User(AbstractUser): class User(AbstractUser):
@ -34,6 +36,22 @@ class User(AbstractUser):
objects = CustomUserManager() objects = CustomUserManager()
class TemporaryUser(User):
otp_secret = models.CharField(max_length=50, default=pyotp.random_base32)
otp_verified = models.BooleanField(default=False)
def generate_otp(self):
totp = pyotp.TOTP(self.otp_secret, interval=300)
return totp.now()
def verify_otp(self, otp):
totp = pyotp.TOTP(self.otp_secret, interval=300)
return totp.verify(otp)
def is_expired(self):
return self.created_at < timezone.now() - timedelta(minutes=5)
class Atoll(models.Model): class Atoll(models.Model):
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
created_at = models.DateTimeField(default=timezone.now) created_at = models.DateTimeField(default=timezone.now)

View File

@ -139,3 +139,8 @@ class AtollSerializer(serializers.ModelSerializer):
model = Atoll model = Atoll
fields = "__all__" fields = "__all__"
depth = 2 depth = 2
class OTPVerificationSerializer(serializers.Serializer):
mobile = serializers.CharField()
otp = serializers.CharField()

View File

@ -6,6 +6,8 @@ from django_rest_passwordreset.signals import reset_password_token_created
from django.db.models.signals import post_save from django.db.models.signals import post_save
from api.models import User from api.models import User
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from api.tasks import verify_user_with_person_api_task
from asgiref.sync import sync_to_async
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
@ -26,6 +28,13 @@ def assign_device_permissions(sender, instance, created, **kwargs):
instance.user_permissions.add(permission) instance.user_permissions.add(permission)
@receiver(post_save, sender=User)
@sync_to_async
def verify_user_with_person_api(sender, instance, created, **kwargs):
if created:
verify_user_with_person_api_task(instance.id)
@receiver(reset_password_token_created) @receiver(reset_password_token_created)
def password_reset_token_created( def password_reset_token_created(
sender, instance, reset_password_token, *args, **kwargs sender, instance, reset_password_token, *args, **kwargs

41
api/tasks.py Normal file
View File

@ -0,0 +1,41 @@
from django.shortcuts import get_object_or_404
from api.models import User
import requests
from apibase.env import env, BASE_DIR
import os
PERSON_API_URL = env.str("PERSON_VERIFY_BASE_URL", "")
env.read_env(os.path.join(BASE_DIR, ".env"))
def verify_user_with_person_api_task(user_id: int):
"""
Verify the user with the Person API.
:param user_id: The ID of the user to verify.
"""
user = get_object_or_404(User, id=user_id)
# Call the Person API to verify the user
response = requests.get(f"{PERSON_API_URL}/api/person/{user.id_card}")
if response.status_code == 200:
data = response.json()
print(f"Data from Person API: {data}")
print("Data from of user: ", user.__dict__)
if (
data.get("nic") == user.id_card
and data.get("name_en") == f"{user.first_name} {user.last_name}"
and data.get("house_name_en") == user.address
and data.get("dob") == user.dob.isoformat()
):
user.verified = True
user.save()
return True
else:
user.verified = False
user.save()
return False
else:
# Handle the error case
print(f"Error verifying user: {response.status_code} - {response.text}")
return False

View File

@ -43,6 +43,7 @@ ACCOUNT_NUMBER_PATTERN = r"^(7\d{12}|9\d{16})$"
class ErrorMessages: class ErrorMessages:
USERNAME_EXISTS = "Username already exists." USERNAME_EXISTS = "Username already exists."
MOBILE_EXISTS = "Mobile number already exists."
INVALID_ID_CARD = "Please enter a valid ID card number." INVALID_ID_CARD = "Please enter a valid ID card number."
INVALID_MOBILE = "Please enter a valid mobile number." INVALID_MOBILE = "Please enter a valid mobile number."
INVALID_ACCOUNT = "Please enter a valid account number." INVALID_ACCOUNT = "Please enter a valid account number."
@ -99,12 +100,9 @@ class CreateUserView(generics.CreateAPIView):
firstname = request.data.get("firstname") firstname = request.data.get("firstname")
lastname = request.data.get("lastname") lastname = request.data.get("lastname")
# Validate required fields first if User.objects.filter(mobile=mobile).exists():
validation_error = self.validate_required_fields(request.data) return Response({"message": ErrorMessages.MOBILE_EXISTS}, status=400)
if validation_error:
return validation_error
# Check username uniqueness after validation
if User.objects.filter(username=username).exists(): if User.objects.filter(username=username).exists():
return Response({"message": ErrorMessages.USERNAME_EXISTS}, status=400) return Response({"message": ErrorMessages.USERNAME_EXISTS}, status=400)
@ -120,6 +118,11 @@ class CreateUserView(generics.CreateAPIView):
if acc_no is None or not re.match(ACCOUNT_NUMBER_PATTERN, acc_no): if acc_no is None or not re.match(ACCOUNT_NUMBER_PATTERN, acc_no):
return Response({"message": ErrorMessages.INVALID_ACCOUNT}, status=400) return Response({"message": ErrorMessages.INVALID_ACCOUNT}, status=400)
# Validate required fields first
validation_error = self.validate_required_fields(request.data)
if validation_error:
return validation_error
# Fetch Atoll and Island instances # Fetch Atoll and Island instances
try: try:
atoll = Atoll.objects.get(id=atoll_id) atoll = Atoll.objects.get(id=atoll_id)

View File

@ -2,24 +2,31 @@ aiohappyeyeballs==2.4.3
aiohttp==3.11.2 aiohttp==3.11.2
aiohttp-retry==2.8.3 aiohttp-retry==2.8.3
aiosignal==1.3.1 aiosignal==1.3.1
amqp==5.3.1
anyio==4.9.0
arabic-reshaper==3.0.0 arabic-reshaper==3.0.0
asgiref==3.8.1 asgiref==3.8.1
asn1crypto==1.5.1 asn1crypto==1.5.1
async-timeout==4.0.3 async-timeout==4.0.3
attrs==24.2.0 attrs==24.2.0
billiard==4.2.1
black==23.12.1 black==23.12.1
boto3==1.35.49 boto3==1.35.49
botocore==1.35.49 botocore==1.35.49
celery==5.5.1
certifi==2024.2.2 certifi==2024.2.2
cffi==1.16.0 cffi==1.16.0
chardet==5.2.0 chardet==5.2.0
charset-normalizer==3.3.2 charset-normalizer==3.3.2
click==8.1.7 click==8.1.7
click-didyoumean==0.3.1
click-plugins==1.1.1
click-repl==0.3.0
colorama==0.4.6 colorama==0.4.6
cryptography==41.0.7 cryptography==41.0.7
cssselect2==0.7.0 cssselect2==0.7.0
dj-database-url==2.1.0 dj-database-url==2.1.0
django==5.1.2 django==5.2
django-cors-headers==4.3.1 django-cors-headers==4.3.1
django-debug-toolbar==4.2.0 django-debug-toolbar==4.2.0
django-easy-audit==1.3.7 django-easy-audit==1.3.7
@ -38,15 +45,20 @@ djangorestframework-simplejwt==5.3.1
djangorestframework-stubs==3.15.1 djangorestframework-stubs==3.15.1
dnspython==2.4.2 dnspython==2.4.2
drf-spectacular==0.27.2 drf-spectacular==0.27.2
exceptiongroup==1.2.2
faker==30.8.0 faker==30.8.0
frozenlist==1.5.0 frozenlist==1.5.0
gunicorn==23.0.0 gunicorn==23.0.0
h11==0.14.0
html5lib==1.1 html5lib==1.1
httpcore==1.0.7
httpx==0.28.1
idna==3.6 idna==3.6
inflection==0.5.1 inflection==0.5.1
jmespath==1.0.1 jmespath==1.0.1
jsonschema==4.23.0 jsonschema==4.23.0
jsonschema-specifications==2024.10.1 jsonschema-specifications==2024.10.1
kombu==5.5.2
lxml==5.1.0 lxml==5.1.0
multidict==6.1.0 multidict==6.1.0
mypy-extensions==1.0.0 mypy-extensions==1.0.0
@ -55,6 +67,7 @@ packaging==23.2
pathspec==0.12.1 pathspec==0.12.1
pillow==10.2.0 pillow==10.2.0
platformdirs==4.1.0 platformdirs==4.1.0
prompt-toolkit==3.0.50
propcache==0.2.0 propcache==0.2.0
psycopg==3.2.3 psycopg==3.2.3
psycopg-binary==3.2.3 psycopg-binary==3.2.3
@ -70,6 +83,7 @@ pypng==0.20220715.0
python-bidi==0.4.2 python-bidi==0.4.2
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
python-decouple==3.8 python-decouple==3.8
python-telegram-bot==22.0
pytz==2023.3.post1 pytz==2023.3.post1
pyyaml==6.0.1 pyyaml==6.0.1
qrcode==7.4.2 qrcode==7.4.2
@ -81,11 +95,13 @@ rpds-py==0.21.0
ruff==0.7.0 ruff==0.7.0
s3transfer==0.10.3 s3transfer==0.10.3
six==1.16.0 six==1.16.0
sniffio==1.3.1
sqlparse==0.5.1 sqlparse==0.5.1
svglib==1.5.1 svglib==1.5.1
tinycss2==1.2.1 tinycss2==1.2.1
tomli==2.0.2 tomli==2.0.2
toposort==1.10 toposort==1.10
twilio==9.3.7
types-pyyaml==6.0.12.20240917 types-pyyaml==6.0.12.20240917
types-requests==2.32.0.20241016 types-requests==2.32.0.20241016
typing-extensions==4.12.2 typing-extensions==4.12.2
@ -94,6 +110,8 @@ tzlocal==5.2
uritemplate==4.1.1 uritemplate==4.1.1
uritools==4.0.2 uritools==4.0.2
urllib3==2.2.1 urllib3==2.2.1
vine==5.1.0
wcwidth==0.2.13
webencodings==0.5.1 webencodings==0.5.1
whitenoise==6.7.0 whitenoise==6.7.0
yarl==1.17.2 yarl==1.17.2