bottleneckcrm/backend/common/views/user_views.py

484 lines
18 KiB
Python

from django.conf import settings
from django.shortcuts import get_object_or_404
from django.utils import timezone
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers, status
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from cases.models import Case
from cases.serializer import CaseSerializer
from common import swagger_params
from common.models import Profile, Teams
from common.serializer import (
BillingAddressSerializer,
CommentSerializer,
CreateProfileSerializer,
CreateUserSerializer,
ProfileSerializer,
TeamsSerializer,
UserCreateSwaggerSerializer,
UserUpdateStatusSwaggerSerializer,
)
from common.tasks import send_email_user_delete
from common.utils import COUNTRIES, ROLES
from contacts.models import Contact
from contacts.serializer import ContactSerializer
from opportunity.models import Opportunity
from opportunity.serializer import OpportunitySerializer
class GetTeamsAndUsersView(APIView):
permission_classes = (IsAuthenticated,)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
responses={
200: inline_serializer(
name="TeamsAndUsersResponse",
fields={
"teams": TeamsSerializer(many=True),
"profiles": ProfileSerializer(many=True),
},
)
},
)
def get(self, request, *args, **kwargs):
data = {}
teams = Teams.objects.filter(org=request.profile.org).order_by("-id")
teams_data = TeamsSerializer(teams, many=True).data
profiles = Profile.objects.filter(
is_active=True, org=request.profile.org
).order_by("user__email")
profiles_data = ProfileSerializer(profiles, many=True).data
data["teams"] = teams_data
data["profiles"] = profiles_data
return Response(data)
class UsersListView(APIView, LimitOffsetPagination):
permission_classes = (IsAuthenticated,)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
request=UserCreateSwaggerSerializer,
responses={
201: inline_serializer(
name="UserCreateResponse",
fields={
"error": serializers.BooleanField(),
"message": serializers.CharField(),
},
)
},
)
def post(self, request, format=None):
if self.request.profile.role != "ADMIN" and not self.request.user.is_superuser:
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
else:
params = request.data
if params:
user_serializer = CreateUserSerializer(
data=params, org=request.profile.org
)
address_serializer = BillingAddressSerializer(data=params)
profile_serializer = CreateProfileSerializer(data=params)
data = {}
if not user_serializer.is_valid():
data["user_errors"] = dict(user_serializer.errors)
if not profile_serializer.is_valid():
data["profile_errors"] = profile_serializer.errors
if not address_serializer.is_valid():
data["address_errors"] = (address_serializer.errors,)
if data:
return Response(
{"error": True, "errors": data},
status=status.HTTP_400_BAD_REQUEST,
)
if address_serializer.is_valid():
address_obj = address_serializer.save()
user = user_serializer.save(
is_active=True,
)
user.email = user.email
user.save()
profile = Profile.objects.create(
user=user,
date_of_joining=timezone.now(),
role=params.get("role"),
address=address_obj,
org=request.profile.org,
)
return Response(
{"error": False, "message": "User Created Successfully"},
status=status.HTTP_201_CREATED,
)
@extend_schema(
tags=["users"],
parameters=swagger_params.user_list_params,
responses={
200: inline_serializer(
name="UsersListResponse",
fields={
"active_users": serializers.DictField(),
"inactive_users": serializers.DictField(),
"admin_email": serializers.CharField(),
"roles": serializers.ListField(),
"status": serializers.ListField(),
},
)
},
)
def get(self, request, format=None):
# Check if profile exists and user has permission
if not self.request.profile:
return Response(
{"error": True, "errors": "Organization context required"},
status=status.HTTP_400_BAD_REQUEST,
)
if self.request.profile.role != "ADMIN" and not self.request.user.is_superuser:
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
queryset = Profile.objects.filter(org=request.profile.org).order_by("-id")
params = request.query_params
if params:
if params.get("email"):
queryset = queryset.filter(user__email__icontains=params.get("email"))
if params.get("role"):
queryset = queryset.filter(role=params.get("role"))
if params.get("status"):
queryset = queryset.filter(is_active=params.get("status"))
context = {}
queryset_active_users = queryset.filter(is_active=True)
results_active_users = self.paginate_queryset(
queryset_active_users.distinct(), self.request, view=self
)
active_users = ProfileSerializer(results_active_users, many=True).data
if results_active_users:
offset = queryset_active_users.filter(
id__gte=results_active_users[-1].id
).count()
if offset == queryset_active_users.count():
offset = None
else:
offset = 0
context["active_users"] = {
"active_users_count": self.count,
"active_users": active_users,
"offset": offset,
}
queryset_inactive_users = queryset.filter(is_active=False)
results_inactive_users = self.paginate_queryset(
queryset_inactive_users.distinct(), self.request, view=self
)
inactive_users = ProfileSerializer(results_inactive_users, many=True).data
if results_inactive_users:
offset = queryset_inactive_users.filter(
id__gte=results_inactive_users[-1].id
).count()
if offset == queryset_inactive_users.count():
offset = None
else:
offset = 0
context["inactive_users"] = {
"inactive_users_count": self.count,
"inactive_users": inactive_users,
"offset": offset,
}
context["admin_email"] = settings.ADMIN_EMAIL
context["roles"] = ROLES
context["status"] = [("True", "Active"), ("False", "In Active")]
return Response(context)
class UserDetailView(APIView):
permission_classes = (IsAuthenticated,)
def get_object(self, pk):
# Security fix: Filter by org to prevent cross-org enumeration
# Lookup by user ID since frontend sends user.id, not profile.id
return get_object_or_404(Profile, user__id=pk, org=self.request.profile.org)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
responses={
200: inline_serializer(
name="UserDetailResponse",
fields={
"error": serializers.BooleanField(),
"data": serializers.DictField(),
},
)
},
)
def get(self, request, pk, format=None):
profile_obj = self.get_object(pk)
if (
self.request.profile.role != "ADMIN"
and not self.request.profile.is_admin
and self.request.profile.id != profile_obj.id
):
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
# Org check now handled by get_object_or_404 in get_object()
assigned_data = Profile.objects.filter(
org=request.profile.org, is_active=True
).values("id", "user__email")
context = {}
context["profile_obj"] = ProfileSerializer(profile_obj).data
# Security fix: Add org filter to prevent cross-org data leakage
opportunity_list = Opportunity.objects.filter(
assigned_to=profile_obj, org=request.profile.org
)
context["opportunity_list"] = OpportunitySerializer(
opportunity_list, many=True
).data
contacts = Contact.objects.filter(
assigned_to=profile_obj, org=request.profile.org
)
context["contacts"] = ContactSerializer(contacts, many=True).data
cases = Case.objects.filter(assigned_to=profile_obj, org=request.profile.org)
context["cases"] = CaseSerializer(cases, many=True).data
context["assigned_data"] = assigned_data
comments = profile_obj.user_comments.all()
context["comments"] = CommentSerializer(comments, many=True).data
context["countries"] = COUNTRIES
return Response(
{"error": False, "data": context},
status=status.HTTP_200_OK,
)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
request=UserCreateSwaggerSerializer,
responses={
200: inline_serializer(
name="UserUpdateResponse",
fields={
"error": serializers.BooleanField(),
"message": serializers.CharField(),
},
)
},
)
def put(self, request, pk, format=None):
params = request.data
profile = self.get_object(pk)
address_obj = profile.address
if (
self.request.profile.role != "ADMIN"
and not self.request.user.is_superuser
and self.request.profile.id != profile.id
):
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
if profile.org != request.profile.org:
return Response(
{"error": True, "errors": "User company doesnot match with header...."},
status=status.HTTP_403_FORBIDDEN,
)
serializer = CreateUserSerializer(
data=params, instance=profile.user, org=request.profile.org
)
address_serializer = BillingAddressSerializer(data=params, instance=address_obj)
profile_serializer = CreateProfileSerializer(data=params, instance=profile)
data = {}
if not serializer.is_valid():
data["contact_errors"] = serializer.errors
if not address_serializer.is_valid():
data["address_errors"] = (address_serializer.errors,)
if not profile_serializer.is_valid():
data["profile_errors"] = (profile_serializer.errors,)
if data:
data["error"] = True
return Response(
data,
status=status.HTTP_400_BAD_REQUEST,
)
if address_serializer.is_valid():
address_obj = address_serializer.save()
user = serializer.save()
user.email = user.email
user.save()
if profile_serializer.is_valid():
profile = profile_serializer.save()
return Response(
{"error": False, "message": "User Updated Successfully"},
status=status.HTTP_200_OK,
)
return Response(
{"error": True, "errors": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
request=UserCreateSwaggerSerializer,
description="Partial User Update",
responses={
200: inline_serializer(
name="UserPatchResponse",
fields={
"error": serializers.BooleanField(),
"message": serializers.CharField(),
},
)
},
)
def patch(self, request, pk, format=None):
"""Handle partial updates to a user."""
params = request.data
profile = self.get_object(pk)
if (
self.request.profile.role != "ADMIN"
and not self.request.user.is_superuser
and self.request.profile.id != profile.id
):
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
if profile.org != request.profile.org:
return Response(
{
"error": True,
"errors": "User company does not match with header....",
},
status=status.HTTP_403_FORBIDDEN,
)
serializer = CreateUserSerializer(
data=params, instance=profile.user, org=request.profile.org, partial=True
)
profile_serializer = CreateProfileSerializer(
data=params, instance=profile, partial=True
)
data = {}
if not serializer.is_valid():
data["contact_errors"] = serializer.errors
if not profile_serializer.is_valid():
data["profile_errors"] = profile_serializer.errors
if data:
data["error"] = True
return Response(data, status=status.HTTP_400_BAD_REQUEST)
if serializer.is_valid():
user = serializer.save()
if profile_serializer.is_valid():
profile = profile_serializer.save()
return Response(
{"error": False, "message": "User Updated Successfully"},
status=status.HTTP_200_OK,
)
return Response(
{"error": True, "errors": serializer.errors},
status=status.HTTP_400_BAD_REQUEST,
)
@extend_schema(
tags=["users"],
parameters=swagger_params.organization_params,
responses={
200: inline_serializer(
name="UserDeleteResponse", fields={"status": serializers.CharField()}
)
},
)
def delete(self, request, pk, format=None):
if self.request.profile.role != "ADMIN" and not self.request.profile.is_admin:
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
self.object = self.get_object(pk)
if self.object.id == request.profile.id:
return Response(
{"error": True, "errors": "Permission Denied"},
status=status.HTTP_403_FORBIDDEN,
)
deleted_by = self.request.profile.user.email
send_email_user_delete.delay(
self.object.user.email,
deleted_by=deleted_by,
)
self.object.delete()
return Response({"status": "success"}, status=status.HTTP_200_OK)
class UserStatusView(APIView):
permission_classes = (IsAuthenticated,)
@extend_schema(
tags=["users"],
description="User Status View",
parameters=swagger_params.organization_params,
request=UserUpdateStatusSwaggerSerializer,
responses={
200: inline_serializer(
name="UserStatusResponse",
fields={
"active_profiles": ProfileSerializer(many=True),
"inactive_profiles": ProfileSerializer(many=True),
},
)
},
)
def post(self, request, pk, format=None):
if self.request.profile.role != "ADMIN" and not self.request.user.is_superuser:
return Response(
{
"error": True,
"errors": "You do not have permission to perform this action",
},
status=status.HTTP_403_FORBIDDEN,
)
params = request.data
profiles = Profile.objects.filter(org=request.profile.org)
# Lookup by user ID since frontend sends user.id, not profile.id
profile = profiles.get(user__id=pk)
if params.get("status"):
user_status = params.get("status")
if user_status == "Active":
profile.is_active = True
elif user_status == "Inactive":
profile.is_active = False
else:
return Response(
{"error": True, "errors": "Please enter Valid Status for user"},
status=status.HTTP_400_BAD_REQUEST,
)
profile.save()
context = {}
active_profiles = profiles.filter(is_active=True)
inactive_profiles = profiles.filter(is_active=False)
context["active_profiles"] = ProfileSerializer(active_profiles, many=True).data
context["inactive_profiles"] = ProfileSerializer(
inactive_profiles, many=True
).data
return Response(context)