mirror of
https://github.com/i701/sarlink-portal-api.git
synced 2025-04-19 23:46:53 +00:00
registration and verify abuse WIP
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 1m27s
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 1m27s
This commit is contained in:
parent
9f3f586181
commit
e0a80d4a00
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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
41
api/tasks.py
Normal 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
|
13
api/views.py
13
api/views.py
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user