Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c5ed1e89d | ||
|
|
976a119fcc | ||
|
|
d64a2675e4 | ||
|
|
eee314af46 | ||
|
|
ff065fa4a9 | ||
|
|
72c2ea1ecc | ||
|
|
596ce510c7 | ||
|
|
436a8b7d7a | ||
|
|
82ae1e6cea | ||
|
|
56ab79bd8c | ||
|
|
64c2189209 | ||
|
|
dacf821bad | ||
|
|
3f0a5f0f03 | ||
|
|
db53874ff4 | ||
|
|
d557bb879f |
@@ -6,6 +6,8 @@ class UserFilter(django_filters.FilterSet):
|
|||||||
last_name = django_filters.CharFilter(lookup_expr="icontains")
|
last_name = django_filters.CharFilter(lookup_expr="icontains")
|
||||||
first_name = django_filters.CharFilter(lookup_expr="icontains")
|
first_name = django_filters.CharFilter(lookup_expr="icontains")
|
||||||
email = django_filters.CharFilter(lookup_expr="icontains")
|
email = django_filters.CharFilter(lookup_expr="icontains")
|
||||||
|
id_card = django_filters.CharFilter(lookup_expr="icontains")
|
||||||
|
mobile = django_filters.CharFilter(lookup_expr="icontains")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Generated by Django 5.2 on 2025-07-15 20:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("api", "0016_user_is_admin"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="temporaryuser",
|
||||||
|
name="t_id_card",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, db_index=True, max_length=255, null=True, unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="temporaryuser",
|
||||||
|
name="t_mobile",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, db_index=True, max_length=255, null=True, unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="id_card",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, db_index=True, max_length=255, null=True, unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="mobile",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, db_index=True, max_length=255, null=True, unique=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
+12
-4
@@ -13,10 +13,14 @@ import pyotp
|
|||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
address = models.CharField(max_length=255, blank=True)
|
address = models.CharField(max_length=255, blank=True)
|
||||||
email = models.EmailField(blank=True, null=True, unique=True)
|
email = models.EmailField(blank=True, null=True, unique=True)
|
||||||
mobile = models.CharField(max_length=255, blank=True, unique=True, null=True)
|
mobile = models.CharField(
|
||||||
|
max_length=255, blank=True, unique=True, null=True, db_index=True
|
||||||
|
)
|
||||||
designation = models.CharField(max_length=255, blank=True)
|
designation = models.CharField(max_length=255, blank=True)
|
||||||
acc_no = models.CharField(max_length=255, blank=True)
|
acc_no = models.CharField(max_length=255, blank=True)
|
||||||
id_card = models.CharField(max_length=255, blank=True, unique=True, null=True)
|
id_card = models.CharField(
|
||||||
|
max_length=255, blank=True, unique=True, null=True, db_index=True
|
||||||
|
)
|
||||||
verified = models.BooleanField(default=False)
|
verified = models.BooleanField(default=False)
|
||||||
is_admin = models.BooleanField(default=False)
|
is_admin = models.BooleanField(default=False)
|
||||||
dob = models.DateField(blank=True, null=True)
|
dob = models.DateField(blank=True, null=True)
|
||||||
@@ -46,10 +50,14 @@ class TemporaryUser(models.Model):
|
|||||||
t_last_name = models.CharField(max_length=255, blank=True)
|
t_last_name = models.CharField(max_length=255, blank=True)
|
||||||
t_address = models.CharField(max_length=255, blank=True)
|
t_address = models.CharField(max_length=255, blank=True)
|
||||||
t_email = models.EmailField(blank=True, null=True, unique=True)
|
t_email = models.EmailField(blank=True, null=True, unique=True)
|
||||||
t_mobile = models.CharField(max_length=255, blank=True, unique=True, null=True)
|
t_mobile = models.CharField(
|
||||||
|
max_length=255, blank=True, unique=True, null=True, db_index=True
|
||||||
|
)
|
||||||
t_designation = models.CharField(max_length=255, blank=True)
|
t_designation = models.CharField(max_length=255, blank=True)
|
||||||
t_acc_no = models.CharField(max_length=255, blank=True)
|
t_acc_no = models.CharField(max_length=255, blank=True)
|
||||||
t_id_card = models.CharField(max_length=255, blank=True, unique=True, null=True)
|
t_id_card = models.CharField(
|
||||||
|
max_length=255, blank=True, unique=True, null=True, db_index=True
|
||||||
|
)
|
||||||
t_verified = models.BooleanField(default=False)
|
t_verified = models.BooleanField(default=False)
|
||||||
t_dob = models.DateField(blank=True, null=True)
|
t_dob = models.DateField(blank=True, null=True)
|
||||||
t_terms_accepted = models.BooleanField(default=False)
|
t_terms_accepted = models.BooleanField(default=False)
|
||||||
|
|||||||
+29
-1
@@ -1,9 +1,12 @@
|
|||||||
from knox.models import AuthToken
|
from knox.models import AuthToken
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from api.models import User, Atoll, Island, TemporaryUser
|
from api.models import Atoll, Island, TemporaryUser
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class IslandSerializer(serializers.ModelSerializer):
|
class IslandSerializer(serializers.ModelSerializer):
|
||||||
@@ -21,6 +24,30 @@ class AtollSerializer(serializers.ModelSerializer):
|
|||||||
depth = 2
|
depth = 2
|
||||||
|
|
||||||
|
|
||||||
|
class UserProfileUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta: # type: ignore
|
||||||
|
model = User
|
||||||
|
fields = (
|
||||||
|
"email",
|
||||||
|
"mobile",
|
||||||
|
) # Only allow these fields
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta: # type: ignore
|
||||||
|
model = User
|
||||||
|
fields = (
|
||||||
|
"id_card",
|
||||||
|
"mobile",
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"address",
|
||||||
|
"dob",
|
||||||
|
"atoll",
|
||||||
|
"island",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustomUserSerializer(serializers.ModelSerializer):
|
class CustomUserSerializer(serializers.ModelSerializer):
|
||||||
"""serializer for the user object"""
|
"""serializer for the user object"""
|
||||||
|
|
||||||
@@ -80,6 +107,7 @@ class CustomReadOnlyUserSerializer(serializers.ModelSerializer):
|
|||||||
"username",
|
"username",
|
||||||
"mobile",
|
"mobile",
|
||||||
"address",
|
"address",
|
||||||
|
"acc_no",
|
||||||
"id_card",
|
"id_card",
|
||||||
)
|
)
|
||||||
depth = 1
|
depth = 1
|
||||||
|
|||||||
+6
-3
@@ -12,12 +12,15 @@ from api.tasks import verify_user_with_person_api_task
|
|||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def assign_device_permissions(sender, instance, created, **kwargs):
|
def assign_device_permissions(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
# Assign all permissions for devices and read permission for atoll and island
|
|
||||||
device_permissions = Permission.objects.filter(content_type__model="device")
|
device_permissions = Permission.objects.filter(content_type__model="device")
|
||||||
atoll_read_permission = Permission.objects.get(codename="view_atoll")
|
atoll_read_permission = Permission.objects.get(codename="view_atoll")
|
||||||
island_read_permission = Permission.objects.get(codename="view_island")
|
island_read_permission = Permission.objects.get(codename="view_island")
|
||||||
payment_permissions = Permission.objects.filter(content_type__model="payment")
|
payment_permissions = Permission.objects.filter(
|
||||||
topup_permissions = Permission.objects.filter(content_type__model="topup")
|
content_type__model="payment"
|
||||||
|
).exclude(codename__startswith="delete_")
|
||||||
|
topup_permissions = Permission.objects.filter(
|
||||||
|
content_type__model="topup"
|
||||||
|
).exclude(codename__startswith="delete_")
|
||||||
|
|
||||||
for permission in topup_permissions:
|
for permission in topup_permissions:
|
||||||
instance.user_permissions.add(permission)
|
instance.user_permissions.add(permission)
|
||||||
|
|||||||
+8
-3
@@ -5,7 +5,7 @@ from knox import views as knox_views
|
|||||||
from .views import (
|
from .views import (
|
||||||
LoginView,
|
LoginView,
|
||||||
CreateTemporaryUserView,
|
CreateTemporaryUserView,
|
||||||
ManageUserView,
|
UserprofileAPIView,
|
||||||
KnoxTokenListApiView,
|
KnoxTokenListApiView,
|
||||||
ListUserView,
|
ListUserView,
|
||||||
UserDetailAPIView,
|
UserDetailAPIView,
|
||||||
@@ -21,13 +21,15 @@ from .views import (
|
|||||||
UpdateUserWalletView,
|
UpdateUserWalletView,
|
||||||
VerifyOTPView,
|
VerifyOTPView,
|
||||||
UserVerifyAPIView,
|
UserVerifyAPIView,
|
||||||
|
UserUpdateAPIView,
|
||||||
|
UserRejectAPIView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("register/", CreateTemporaryUserView.as_view(), name="register"),
|
path("register/", CreateTemporaryUserView.as_view(), name="register"),
|
||||||
path("register/verify/", VerifyOTPView.as_view(), name="verify-otp"),
|
path("register/verify/", VerifyOTPView.as_view(), name="verify-otp"),
|
||||||
path("profile/", ManageUserView.as_view(), name="profile"),
|
path("profile/", UserprofileAPIView.as_view(), name="profile"),
|
||||||
path("login/", LoginView.as_view(), name="knox_login"),
|
path("login/", LoginView.as_view(), name="knox_login"),
|
||||||
path("logout/", knox_views.LogoutView.as_view(), name="knox_logout"),
|
path("logout/", knox_views.LogoutView.as_view(), name="knox_logout"),
|
||||||
path("logoutall/", knox_views.LogoutAllView.as_view(), name="knox_logoutall"),
|
path("logoutall/", knox_views.LogoutAllView.as_view(), name="knox_logoutall"),
|
||||||
@@ -38,9 +40,12 @@ urlpatterns = [
|
|||||||
"update-wallet/<int:pk>/", UpdateUserWalletView.as_view(), name="update-wallet"
|
"update-wallet/<int:pk>/", UpdateUserWalletView.as_view(), name="update-wallet"
|
||||||
),
|
),
|
||||||
path("users/<int:pk>/", UserDetailAPIView.as_view(), name="user-detail"),
|
path("users/<int:pk>/", UserDetailAPIView.as_view(), name="user-detail"),
|
||||||
path("users/<int:pk>/verify/", UserVerifyAPIView.as_view(), name="user-verify"),
|
path("users/<int:pk>/update/", UserUpdateAPIView.as_view(), name="user-update"),
|
||||||
path("users/filter/", filter_user, name="filter-users"),
|
path("users/filter/", filter_user, name="filter-users"),
|
||||||
path("users/temp/filter/", filter_temporary_user, name="filter-temporary-users"),
|
path("users/temp/filter/", filter_temporary_user, name="filter-temporary-users"),
|
||||||
|
# User verification flow
|
||||||
|
path("users/<int:pk>/verify/", UserVerifyAPIView.as_view(), name="user-verify"),
|
||||||
|
path("users/<int:pk>/reject/", UserRejectAPIView.as_view(), name="user-reject"),
|
||||||
path("healthcheck/", healthcheck, name="healthcheck"),
|
path("healthcheck/", healthcheck, name="healthcheck"),
|
||||||
path("test/", test_email, name="testemail"),
|
path("test/", test_email, name="testemail"),
|
||||||
path("atolls/", ListAtollView.as_view(), name="atolls"),
|
path("atolls/", ListAtollView.as_view(), name="atolls"),
|
||||||
|
|||||||
+27
-18
@@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import List, TypedDict
|
from typing import List, Optional, TypedDict
|
||||||
import requests
|
import requests
|
||||||
from decouple import config
|
from decouple import config
|
||||||
from api.models import User
|
from api.models import User
|
||||||
@@ -40,7 +40,9 @@ def reverse_dhivehi_string(input_str):
|
|||||||
|
|
||||||
class MismatchResult(TypedDict):
|
class MismatchResult(TypedDict):
|
||||||
ok: bool
|
ok: bool
|
||||||
mismatch_fields: List[str]
|
mismatch_fields: Optional[List[str]]
|
||||||
|
error: Optional[str]
|
||||||
|
detail: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
def check_person_api_verification(
|
def check_person_api_verification(
|
||||||
@@ -63,20 +65,22 @@ def check_person_api_verification(
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
"PERSON_VERIFY_BASE_URL is not set in the environment variables."
|
"PERSON_VERIFY_BASE_URL is not set in the environment variables."
|
||||||
)
|
)
|
||||||
print(id_card)
|
|
||||||
response = requests.get(f"{PERSON_VERIFY_BASE_URL}/api/person/{id_card}")
|
response = requests.get(f"{PERSON_VERIFY_BASE_URL}/api/person/{id_card}")
|
||||||
|
api_reponse = response.json()
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Failed to fetch data from Person API for ID Card '{id_card}'. "
|
f"Failed to fetch data from Person API for ID Card '{id_card}'. "
|
||||||
f"Status Code: {response.status_code}, Response: {response.text}"
|
f"Status Code: {response.status_code}, Response: {response.text}"
|
||||||
)
|
)
|
||||||
return {"ok": False, "mismatch_fields": ["api_error"]}
|
return {
|
||||||
api_data = response.json()
|
"ok": False,
|
||||||
if not api_data:
|
"mismatch_fields": None,
|
||||||
logger.error(
|
"error": response.json()["error"] if "error" in response.json() else None,
|
||||||
f"No data found in Person API for ID Card '{id_card}'. Response: {response.text}"
|
"detail": response.json()["detail"]
|
||||||
)
|
if "detail" in response.json()
|
||||||
return {"ok": False, "mismatch_fields": ["no_data"]}
|
else None,
|
||||||
|
}
|
||||||
|
|
||||||
# Initialize a list to hold fields that do not match
|
# Initialize a list to hold fields that do not match
|
||||||
mismatch_fields = []
|
mismatch_fields = []
|
||||||
@@ -86,12 +90,12 @@ def check_person_api_verification(
|
|||||||
user_dob_iso = user_data.dob.isoformat() if user_data.dob else None
|
user_dob_iso = user_data.dob.isoformat() if user_data.dob else None
|
||||||
|
|
||||||
# Prepare API data for comparison
|
# Prepare API data for comparison
|
||||||
api_nic = api_data.get("nic")
|
api_nic = api_reponse.get("nic")
|
||||||
api_name = api_data.get("name_en")
|
api_name = api_reponse.get("name_en")
|
||||||
api_house_name = api_data.get("house_name_en")
|
api_house_name = api_reponse.get("house_name_en")
|
||||||
api_dob = api_data.get("dob")
|
api_dob = api_reponse.get("dob")
|
||||||
api_atoll = api_data.get("atoll_en")
|
api_atoll = api_reponse.get("atoll_en")
|
||||||
api_island_name = api_data.get("island_name_en")
|
api_island_name = api_reponse.get("island_name_en")
|
||||||
|
|
||||||
# Perform comparisons and identify mismatches
|
# Perform comparisons and identify mismatches
|
||||||
if user_data.id_card != api_nic:
|
if user_data.id_card != api_nic:
|
||||||
@@ -134,6 +138,11 @@ def check_person_api_verification(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if mismatch_fields:
|
if mismatch_fields:
|
||||||
return {"ok": False, "mismatch_fields": mismatch_fields}
|
return {
|
||||||
|
"ok": False,
|
||||||
|
"mismatch_fields": mismatch_fields,
|
||||||
|
"error": None,
|
||||||
|
"detail": None,
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
return {"ok": True, "mismatch_fields": []}
|
return {"ok": True, "mismatch_fields": [], "error": None, "detail": None}
|
||||||
|
|||||||
+114
-22
@@ -7,6 +7,7 @@ from rest_framework.authtoken.serializers import AuthTokenSerializer
|
|||||||
from api.filters import UserFilter
|
from api.filters import UserFilter
|
||||||
from api.mixins import StaffEditorPermissionMixin
|
from api.mixins import StaffEditorPermissionMixin
|
||||||
from api.models import User, Atoll, Island, TemporaryUser
|
from api.models import User, Atoll, Island, TemporaryUser
|
||||||
|
from api.notifications import send_sms
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
@@ -17,6 +18,7 @@ from api.serializers import (
|
|||||||
CustomUserByWalletBalanceSerializer,
|
CustomUserByWalletBalanceSerializer,
|
||||||
OTPVerificationSerializer,
|
OTPVerificationSerializer,
|
||||||
TemporaryUserSerializer,
|
TemporaryUserSerializer,
|
||||||
|
UserUpdateSerializer,
|
||||||
)
|
)
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -40,6 +42,7 @@ from .serializers import (
|
|||||||
CustomUserSerializer,
|
CustomUserSerializer,
|
||||||
CustomReadOnlyUserSerializer,
|
CustomReadOnlyUserSerializer,
|
||||||
CustomReadOnlyUserByIDCardSerializer,
|
CustomReadOnlyUserByIDCardSerializer,
|
||||||
|
UserProfileUpdateSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
ID_CARD_PATTERN = r"^[A-Z]{1,2}[0-9]{6,7}$"
|
ID_CARD_PATTERN = r"^[A-Z]{1,2}[0-9]{6,7}$"
|
||||||
@@ -305,17 +308,56 @@ class LoginView(KnoxLoginView):
|
|||||||
return Response({"message": message}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"message": message}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class ManageUserView(generics.RetrieveUpdateAPIView):
|
class UserprofileAPIView(generics.RetrieveUpdateAPIView):
|
||||||
"""Manage the authenticated user"""
|
"""Retrieve user api view"""
|
||||||
|
|
||||||
serializer_class = CustomUserSerializer
|
queryset = User.objects.all()
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
"""Return the serializer class based on the request method"""
|
||||||
|
if self.request.method == "GET":
|
||||||
|
return CustomReadOnlyUserSerializer
|
||||||
|
elif self.request.method == "PUT" or self.request.method == "PATCH":
|
||||||
|
return UserProfileUpdateSerializer
|
||||||
|
return super().get_serializer_class()
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
"""Retrieve and return authenticated user"""
|
"""Retrieve and return authenticated user"""
|
||||||
return self.request.user
|
return self.request.user
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdateAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
||||||
|
serializer_class = UserUpdateSerializer
|
||||||
|
queryset = User.objects.all()
|
||||||
|
lookup_field = "pk"
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
user_id = kwargs.get("pk")
|
||||||
|
user = get_object_or_404(User, pk=user_id)
|
||||||
|
if user.is_superuser:
|
||||||
|
return Response(
|
||||||
|
{"message": "You cannot update a superuser."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
|
if request.user != user and (
|
||||||
|
not request.user.is_authenticated
|
||||||
|
or not getattr(request.user, "is_admin", False)
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"message": "You are not authorized to update this user."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
|
serializer = self.get_serializer(
|
||||||
|
user,
|
||||||
|
data=request.data,
|
||||||
|
partial=True,
|
||||||
|
)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
user.save()
|
||||||
|
return super().update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class KnoxTokenListApiView(
|
class KnoxTokenListApiView(
|
||||||
StaffEditorPermissionMixin,
|
StaffEditorPermissionMixin,
|
||||||
generics.ListAPIView,
|
generics.ListAPIView,
|
||||||
@@ -343,9 +385,9 @@ class ListUserView(StaffEditorPermissionMixin, generics.ListAPIView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
if user.is_authenticated and user.is_staff:
|
if user.is_authenticated and getattr(user, "is_admin"):
|
||||||
return User.objects.all()
|
return User.objects.filter(is_superuser=False)
|
||||||
return User.objects.filter(is_staff=False)
|
return User.objects.none()
|
||||||
|
|
||||||
|
|
||||||
class UserVerifyAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
class UserVerifyAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
||||||
@@ -364,30 +406,70 @@ class UserVerifyAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
|||||||
{"message": "You are not authorized to update this user."},
|
{"message": "You are not authorized to update this user."},
|
||||||
status=status.HTTP_403_FORBIDDEN,
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
)
|
)
|
||||||
serializer = self.get_serializer(user, data=request.data, partial=True)
|
if user.verified:
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
verified_person = check_person_api_verification(
|
|
||||||
user_data=user, id_card=user.id_card
|
|
||||||
)
|
|
||||||
if not verified_person["ok"]:
|
|
||||||
return Response(
|
return Response(
|
||||||
{
|
{"message": "User is already verified."},
|
||||||
"message": "User verification failed. Please check sarlink user details.",
|
|
||||||
"mismatch_fields": verified_person["mismatch_fields"],
|
|
||||||
},
|
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
if verified_person["mismatch_fields"]:
|
serializer = self.get_serializer(user, data=request.data, partial=True)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
result = check_person_api_verification(user_data=user, id_card=user.id_card)
|
||||||
|
if not result["ok"]:
|
||||||
return Response(
|
return Response(
|
||||||
{
|
result,
|
||||||
"message": "User verification failed due to mismatched fields.",
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
"mismatch_fields": verified_person["mismatch_fields"],
|
)
|
||||||
},
|
if result["mismatch_fields"]:
|
||||||
|
return Response(
|
||||||
|
result,
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
user.verified = True
|
user.verified = True
|
||||||
user.save()
|
user.save()
|
||||||
return Response({"message": "User verification status updated."})
|
return Response({"message": "User successfully verified."})
|
||||||
|
|
||||||
|
|
||||||
|
class UserRejectAPIView(StaffEditorPermissionMixin, generics.DestroyAPIView):
|
||||||
|
serializer_class = CustomUserSerializer
|
||||||
|
queryset = User.objects.all()
|
||||||
|
lookup_field = "pk"
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
rejection_details = request.data.get("rejection_details", "")
|
||||||
|
if not rejection_details:
|
||||||
|
return Response(
|
||||||
|
{"message": "Rejection details are required."},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
user_id = kwargs.get("pk")
|
||||||
|
user = get_object_or_404(User, pk=user_id)
|
||||||
|
mobile_number = user.mobile
|
||||||
|
if not mobile_number:
|
||||||
|
return Response(
|
||||||
|
{"message": "User does not have a mobile number."},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
if user.is_superuser:
|
||||||
|
return Response(
|
||||||
|
{"message": "You cannot remove a superuser."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
|
if request.user != user and (
|
||||||
|
not request.user.is_authenticated
|
||||||
|
or not getattr(request.user, "is_admin", False)
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"message": "You are not authorized to reject this user."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
|
user.delete()
|
||||||
|
t_user = get_object_or_404(TemporaryUser, t_mobile=user.mobile)
|
||||||
|
t_user.delete()
|
||||||
|
send_sms(message=rejection_details, mobile=mobile_number)
|
||||||
|
return Response(
|
||||||
|
{"message": "User successfully rejected."},
|
||||||
|
status=status.HTTP_204_NO_CONTENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
@@ -459,6 +541,16 @@ class UserDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
|
|||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
user = request.user
|
||||||
|
if (
|
||||||
|
user != instance
|
||||||
|
and not getattr(user, "is_admin", False)
|
||||||
|
and not user.is_superuser
|
||||||
|
):
|
||||||
|
return Response(
|
||||||
|
{"message": "You are not authorized to view this user's details."},
|
||||||
|
status=status.HTTP_403_FORBIDDEN,
|
||||||
|
)
|
||||||
serializer = self.get_serializer(instance)
|
serializer = self.get_serializer(instance)
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,30 @@ class ListCreatePaymentView(StaffEditorPermissionMixin, generics.ListCreateAPIVi
|
|||||||
serializer = PaymentSerializer(payment)
|
serializer = PaymentSerializer(payment)
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
all_payments = request.query_params.get("all_payments", "false").lower() in [
|
||||||
|
"true",
|
||||||
|
"1",
|
||||||
|
"yes",
|
||||||
|
]
|
||||||
|
if (
|
||||||
|
request.user.is_authenticated
|
||||||
|
and getattr(request.user, "is_admin")
|
||||||
|
and bool(all_payments)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
queryset = queryset.filter(user=request.user)
|
||||||
|
|
||||||
|
page = self.paginate_queryset(queryset)
|
||||||
|
if page is not None:
|
||||||
|
serializer = self.get_serializer(page, many=True)
|
||||||
|
return self.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class PaymentDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
|
class PaymentDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView):
|
||||||
queryset = Payment.objects.select_related("user").all()
|
queryset = Payment.objects.select_related("user").all()
|
||||||
|
|||||||
Reference in New Issue
Block a user