mirror of
https://github.com/i701/sarlink-portal-api.git
synced 2025-06-13 13:36:20 +00:00
Add user verification endpoint and logic; implement check against Person API
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 4m58s
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 4m58s
This commit is contained in:
parent
26d7fb6dd1
commit
3957ca0ea4
@ -20,6 +20,7 @@ from .views import (
|
|||||||
filter_temporary_user,
|
filter_temporary_user,
|
||||||
UpdateUserWalletView,
|
UpdateUserWalletView,
|
||||||
VerifyOTPView,
|
VerifyOTPView,
|
||||||
|
UserVerifyAPIView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ 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/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"),
|
||||||
path("healthcheck/", healthcheck, name="healthcheck"),
|
path("healthcheck/", healthcheck, name="healthcheck"),
|
||||||
|
110
api/utils.py
110
api/utils.py
@ -1,3 +1,12 @@
|
|||||||
|
import logging
|
||||||
|
from typing import List, TypedDict
|
||||||
|
import requests
|
||||||
|
from decouple import config
|
||||||
|
from api.models import User
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def reverse_dhivehi_string(input_str):
|
def reverse_dhivehi_string(input_str):
|
||||||
"""
|
"""
|
||||||
Reverses a Dhivehi string while preserving character composition.
|
Reverses a Dhivehi string while preserving character composition.
|
||||||
@ -27,3 +36,104 @@ def reverse_dhivehi_string(input_str):
|
|||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
return "".join(corrected_chars)
|
return "".join(corrected_chars)
|
||||||
|
|
||||||
|
|
||||||
|
class MismatchResult(TypedDict):
|
||||||
|
ok: bool
|
||||||
|
mismatch_fields: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
def check_person_api_verification(
|
||||||
|
user_data: User,
|
||||||
|
id_card: str | None,
|
||||||
|
) -> MismatchResult:
|
||||||
|
"""
|
||||||
|
Compares user data with data from the Person API and returns a verification result.
|
||||||
|
|
||||||
|
:param user_data: A dictionary containing user information. Expected keys:
|
||||||
|
'id_card', 'first_name', 'last_name', 'address', 'dob',
|
||||||
|
'atoll_name', 'island_name'.
|
||||||
|
:param api_data: A dictionary containing data from the Person API. Expected keys:
|
||||||
|
'nic', 'name_en', 'house_name_en', 'dob', 'atoll_en',
|
||||||
|
'island_name_en'.
|
||||||
|
:return: A dictionary with 'ok' (boolean) and 'mismatch_fields' (list of strings).
|
||||||
|
"""
|
||||||
|
PERSON_VERIFY_BASE_URL = config("PERSON_VERIFY_BASE_URL", default="") # type: ignore
|
||||||
|
if not PERSON_VERIFY_BASE_URL:
|
||||||
|
raise ValueError(
|
||||||
|
"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}")
|
||||||
|
if response.status_code != 200:
|
||||||
|
logger.error(
|
||||||
|
f"Failed to fetch data from Person API for ID Card '{id_card}'. "
|
||||||
|
f"Status Code: {response.status_code}, Response: {response.text}"
|
||||||
|
)
|
||||||
|
return {"ok": False, "mismatch_fields": ["api_error"]}
|
||||||
|
api_data = response.json()
|
||||||
|
if not api_data:
|
||||||
|
logger.error(
|
||||||
|
f"No data found in Person API for ID Card '{id_card}'. Response: {response.text}"
|
||||||
|
)
|
||||||
|
return {"ok": False, "mismatch_fields": ["no_data"]}
|
||||||
|
|
||||||
|
# Initialize a list to hold fields that do not match
|
||||||
|
mismatch_fields = []
|
||||||
|
|
||||||
|
# Prepare user data for comparison
|
||||||
|
user_full_name = f"{user_data.first_name} {user_data.last_name}".strip()
|
||||||
|
user_dob_iso = user_data.dob.isoformat() if user_data.dob else None
|
||||||
|
|
||||||
|
# Prepare API data for comparison
|
||||||
|
api_nic = api_data.get("nic")
|
||||||
|
api_name = api_data.get("name_en")
|
||||||
|
api_house_name = api_data.get("house_name_en")
|
||||||
|
api_dob = api_data.get("dob")
|
||||||
|
api_atoll = api_data.get("atoll_en")
|
||||||
|
api_island_name = api_data.get("island_name_en")
|
||||||
|
|
||||||
|
# Perform comparisons and identify mismatches
|
||||||
|
if user_data.id_card != api_nic:
|
||||||
|
mismatch_fields.append("id_card")
|
||||||
|
logger.debug(f"ID Card mismatch: User '{user_data.id_card}' vs API '{api_nic}'")
|
||||||
|
|
||||||
|
if user_full_name != api_name:
|
||||||
|
mismatch_fields.append("name")
|
||||||
|
logger.debug(f"Name mismatch: User '{user_full_name}' vs API '{api_name}'")
|
||||||
|
|
||||||
|
if user_data.address != api_house_name:
|
||||||
|
mismatch_fields.append("address")
|
||||||
|
logger.debug(
|
||||||
|
f"Address mismatch: User '{user_data.address}' vs API '{api_house_name}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# API DOB might include time component, so split it
|
||||||
|
api_dob_date_only = api_dob.split("T")[0] if api_dob else None
|
||||||
|
if user_dob_iso != api_dob_date_only:
|
||||||
|
mismatch_fields.append("dob")
|
||||||
|
logger.debug(
|
||||||
|
f"DOB mismatch: User '{user_dob_iso}' vs API '{api_dob_date_only}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use .strip() for atoll and island names due to potential whitespace
|
||||||
|
user_atoll_name = user_data.atoll.name if user_data.atoll is not None else None
|
||||||
|
api_atoll_stripped = api_atoll.strip() if api_atoll else None
|
||||||
|
if user_atoll_name != api_atoll_stripped:
|
||||||
|
mismatch_fields.append("atoll")
|
||||||
|
logger.debug(
|
||||||
|
f"Atoll mismatch: User '{user_atoll_name}' vs API '{api_atoll_stripped}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
user_island_name = user_data.island.name if user_data.island is not None else None
|
||||||
|
api_island_name_stripped = api_island_name.strip() if api_island_name else None
|
||||||
|
if user_island_name != api_island_name_stripped:
|
||||||
|
mismatch_fields.append("island_name")
|
||||||
|
logger.debug(
|
||||||
|
f"Island Name mismatch: User '{user_island_name}' vs API '{api_island_name_stripped}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
if mismatch_fields:
|
||||||
|
return {"ok": False, "mismatch_fields": mismatch_fields}
|
||||||
|
else:
|
||||||
|
return {"ok": True, "mismatch_fields": []}
|
||||||
|
49
api/views.py
49
api/views.py
@ -32,6 +32,7 @@ from django.core.mail import send_mail
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from api.notifications import send_otp
|
from api.notifications import send_otp
|
||||||
from .tasks import add
|
from .tasks import add
|
||||||
|
from .utils import check_person_api_verification
|
||||||
|
|
||||||
# local apps import
|
# local apps import
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
@ -344,6 +345,54 @@ class ListUserView(StaffEditorPermissionMixin, generics.ListAPIView):
|
|||||||
filterset_class = UserFilter
|
filterset_class = UserFilter
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
if user.is_authenticated and user.is_staff:
|
||||||
|
return User.objects.all()
|
||||||
|
return User.objects.filter(is_staff=False)
|
||||||
|
|
||||||
|
|
||||||
|
class UserVerifyAPIView(StaffEditorPermissionMixin, generics.UpdateAPIView):
|
||||||
|
serializer_class = CustomUserSerializer
|
||||||
|
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 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)
|
||||||
|
verified_person = check_person_api_verification(
|
||||||
|
user_data=user, id_card=user.id_card
|
||||||
|
)
|
||||||
|
if not verified_person["ok"]:
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"message": "User verification failed. Please check sarlink user details.",
|
||||||
|
"mismatch_fields": verified_person["mismatch_fields"],
|
||||||
|
},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
if verified_person["mismatch_fields"]:
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"message": "User verification failed due to mismatched fields.",
|
||||||
|
"mismatch_fields": verified_person["mismatch_fields"],
|
||||||
|
},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
)
|
||||||
|
user.verified = True
|
||||||
|
user.save()
|
||||||
|
return Response({"message": "User verification status updated."})
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def filter_user(request):
|
def filter_user(request):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user