diff --git a/backend/tenant_account_v2/__init__.py b/backend/tenant_account_v2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/tenant_account_v2/admin.py b/backend/tenant_account_v2/admin.py new file mode 100644 index 0000000000..846f6b4061 --- /dev/null +++ b/backend/tenant_account_v2/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/backend/tenant_account_v2/apps.py b/backend/tenant_account_v2/apps.py new file mode 100644 index 0000000000..cd128f028b --- /dev/null +++ b/backend/tenant_account_v2/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TenantAccountV2Config(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "tenant_account_v2" diff --git a/backend/tenant_account_v2/constants.py b/backend/tenant_account_v2/constants.py new file mode 100644 index 0000000000..29ba63684f --- /dev/null +++ b/backend/tenant_account_v2/constants.py @@ -0,0 +1,14 @@ +class PlatformServiceConstants: + IS_ACTIVE = "is_active" + KEY = "key" + ORGANIZATION = "organization" + ID = "id" + ACTIVATE = "ACTIVATE" + DEACTIVATE = "DEACTIVATE" + ACTION = "action" + KEY_NAME = "key_name" + + +class ErrorMessage: + KEY_EXIST = "Key name already exists" + DUPLICATE_API = "It appears that a duplicate call may have been made." diff --git a/backend/tenant_account_v2/dto.py b/backend/tenant_account_v2/dto.py new file mode 100644 index 0000000000..16c37ea583 --- /dev/null +++ b/backend/tenant_account_v2/dto.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass + + +@dataclass +class OrganizationLoginResponse: + name: str + display_name: str + organization_id: str + created_at: str + + +@dataclass +class ResetUserPasswordDto: + status: bool + message: str diff --git a/backend/tenant_account_v2/enums.py b/backend/tenant_account_v2/enums.py new file mode 100644 index 0000000000..d8209ec2d3 --- /dev/null +++ b/backend/tenant_account_v2/enums.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class UserRole(Enum): + USER = "user" + ADMIN = "admin" diff --git a/backend/tenant_account_v2/invitation_urls.py b/backend/tenant_account_v2/invitation_urls.py new file mode 100644 index 0000000000..aed1f37a1d --- /dev/null +++ b/backend/tenant_account_v2/invitation_urls.py @@ -0,0 +1,20 @@ +from django.urls import path +from tenant_account_v2.invitation_views import InvitationViewSet + +invitation_list = InvitationViewSet.as_view( + { + "get": InvitationViewSet.list_invitations.__name__, + } +) + +invitation_details = InvitationViewSet.as_view( + { + "delete": InvitationViewSet.delete_invitation.__name__, + } +) + + +urlpatterns = [ + path("", invitation_list, name="invitation_list"), + path("/", invitation_details, name="invitation_details"), +] diff --git a/backend/tenant_account_v2/invitation_views.py b/backend/tenant_account_v2/invitation_views.py new file mode 100644 index 0000000000..adbd726955 --- /dev/null +++ b/backend/tenant_account_v2/invitation_views.py @@ -0,0 +1,46 @@ +import logging + +from account_v2.authentication_controller import AuthenticationController +from account_v2.dto import MemberInvitation +from rest_framework import status, viewsets +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response +from tenant_account_v2.serializer import ListInvitationsResponseSerializer +from utils.user_session import UserSessionUtils + +Logger = logging.getLogger(__name__) + + +class InvitationViewSet(viewsets.ViewSet): + @action(detail=False, methods=["GET"]) + def list_invitations(self, request: Request) -> Response: + auth_controller = AuthenticationController() + invitations: list[MemberInvitation] = auth_controller.get_user_invitations( + organization_id=UserSessionUtils.get_organization_id(request), + ) + serialized_members = ListInvitationsResponseSerializer( + invitations, many=True + ).data + return Response( + status=status.HTTP_200_OK, + data={"message": "success", "members": serialized_members}, + ) + + @action(detail=False, methods=["DELETE"]) + def delete_invitation(self, request: Request, id: str) -> Response: + auth_controller = AuthenticationController() + is_deleted: bool = auth_controller.delete_user_invitation( + organization_id=UserSessionUtils.get_organization_id(request), + invitation_id=id, + ) + if is_deleted: + return Response( + status=status.HTTP_204_NO_CONTENT, + data={"status": "success", "message": "success"}, + ) + else: + return Response( + status=status.HTTP_404_NOT_FOUND, + data={"status": "failed", "message": "failed"}, + ) diff --git a/backend/tenant_account_v2/models.py b/backend/tenant_account_v2/models.py new file mode 100644 index 0000000000..c2c5b3cdc2 --- /dev/null +++ b/backend/tenant_account_v2/models.py @@ -0,0 +1,48 @@ +from account_v2.models import User +from django.db import models +from utils.models.organization_mixin import ( + DefaultOrganizationManagerMixin, + DefaultOrganizationMixin, +) + + +class OrganizationMemberModelManager(DefaultOrganizationManagerMixin, models.Manager): + pass + + +class OrganizationMember(DefaultOrganizationMixin): + member_id = models.BigAutoField(primary_key=True) + user = models.ForeignKey( + User, + on_delete=models.CASCADE, + default=None, + related_name="organization_user", + ) + role = models.CharField() + is_login_onboarding_msg = models.BooleanField( + default=True, + db_comment="Flag to indicate whether the onboarding messages are shown", + ) + is_prompt_studio_onboarding_msg = models.BooleanField( + default=True, + db_comment="Flag to indicate whether the prompt studio messages are shown", + ) + + def __str__(self): # type: ignore + return ( + f"OrganizationMember(" + f"{self.member_id}, role: {self.role}, userId: {self.user.user_id})" + ) + + objects = OrganizationMemberModelManager() + + class Meta: + db_table = "organization_member_v2" + verbose_name = "Organization Member" + verbose_name_plural = "Organization Members" + constraints = [ + models.UniqueConstraint( + fields=["organization", "user"], + name="unique_organization_member", + ), + ] diff --git a/backend/tenant_account_v2/organization_member_service.py b/backend/tenant_account_v2/organization_member_service.py new file mode 100644 index 0000000000..ae9dd085cb --- /dev/null +++ b/backend/tenant_account_v2/organization_member_service.py @@ -0,0 +1,144 @@ +from typing import Any, Optional + +from tenant_account_v2.models import OrganizationMember +from utils.cache_service import CacheService + + +class OrganizationMemberService: + + @staticmethod + def get_user_by_email(email: str) -> Optional[OrganizationMember]: + try: + return OrganizationMember.objects.get(user__email=email) # type: ignore + except OrganizationMember.DoesNotExist: + return None + + @staticmethod + def get_user_by_user_id(user_id: str) -> Optional[OrganizationMember]: + try: + return OrganizationMember.objects.get(user__user_id=user_id) # type: ignore + except OrganizationMember.DoesNotExist: + return None + + @staticmethod + def get_user_by_id(id: str) -> Optional[OrganizationMember]: + try: + return OrganizationMember.objects.get(user=id) # type: ignore + except OrganizationMember.DoesNotExist: + return None + + @staticmethod + def get_members() -> list[OrganizationMember]: + return OrganizationMember.objects.all() + + @staticmethod + def get_members_by_user_email( + user_emails: list[str], values_list_fields: list[str] + ) -> list[dict[str, Any]]: + """Get members by user emails. + + Parameters: + user_emails (list[str]): The emails of the users to get. + values_list_fields (list[str]): The fields to include in the result. + + Returns: + list[dict[str, Any]]: The members. + """ + if not user_emails: + return [] + queryset = OrganizationMember.objects.filter(user__email__in=user_emails) + if values_list_fields is None: + users = queryset.values() + else: + users = queryset.values_list(*values_list_fields) + + return list(users) + + @staticmethod + def delete_user(user: OrganizationMember) -> None: + """Delete a user from an organization. + + Parameters: + user (OrganizationMember): The user to delete. + """ + user.delete() + + @staticmethod + def remove_users_by_user_pks(user_pks: list[str]) -> None: + """Remove a users from an organization. + + Parameters: + user_pks (list[str]): The primary keys of the users to remove. + """ + OrganizationMember.objects.filter(user__in=user_pks).delete() + + @classmethod + def remove_user_by_user_id(cls, user_id: str) -> None: + """Remove a user from an organization. + + Parameters: + user_id (str): The user_id of the user to remove. + """ + user = cls.get_user_by_user_id(user_id) + if user: + cls.delete_user(user) + + @staticmethod + def get_organization_user_cache_key(user_id: str, organization_id: str) -> str: + """Get the cache key for a user in an organization. + + Parameters: + organization_id (str): The ID of the organization. + + Returns: + str: The cache key for a user in the organization. + """ + return f"user_organization:{user_id}:{organization_id}" + + @classmethod + def check_user_membership_in_organization_cache( + cls, user_id: str, organization_id: str + ) -> bool: + """Check if a user exists in an organization. + + Parameters: + user_id (str): The ID of the user to check. + organization_id (str): The ID of the organization to check. + + Returns: + bool: True if the user exists in the organization, False otherwise. + """ + user_organization_key = cls.get_organization_user_cache_key( + user_id, organization_id + ) + return CacheService.check_a_key_exist(user_organization_key) + + @classmethod + def set_user_membership_in_organization_cache( + cls, user_id: str, organization_id: str + ) -> None: + """Set a user's membership in an organization in the cache. + + Parameters: + user_id (str): The ID of the user. + organization_id (str): The ID of the organization. + """ + user_organization_key = cls.get_organization_user_cache_key( + user_id, organization_id + ) + CacheService.set_key(user_organization_key, {}) + + @classmethod + def remove_user_membership_in_organization_cache( + cls, user_id: str, organization_id: str + ) -> None: + """Remove a user's membership in an organization from the cache. + + Parameters: + user_id (str): The ID of the user. + organization_id (str): The ID of the organization. + """ + user_organization_key = cls.get_organization_user_cache_key( + user_id, organization_id + ) + CacheService.delete_a_key(user_organization_key) diff --git a/backend/tenant_account_v2/serializer.py b/backend/tenant_account_v2/serializer.py new file mode 100644 index 0000000000..32165ef19f --- /dev/null +++ b/backend/tenant_account_v2/serializer.py @@ -0,0 +1,159 @@ +from collections import OrderedDict +from typing import Any, Optional, Union, cast + +from account_v2.constants import Common +from rest_framework import serializers +from rest_framework.exceptions import ValidationError +from rest_framework.serializers import ModelSerializer +from tenant_account_v2.models import OrganizationMember + + +class OrganizationCallbackSerializer(serializers.Serializer): + id = serializers.CharField(required=False) + + +class OrganizationLoginResponseSerializer(serializers.Serializer): + name = serializers.CharField() + display_name = serializers.CharField() + organization_id = serializers.CharField() + created_at = serializers.CharField() + + +class UserInviteResponseSerializer(serializers.Serializer): + email = serializers.CharField(required=True) + status = serializers.CharField(required=True) + message = serializers.CharField(required=False) + + +class OrganizationMemberSerializer(serializers.ModelSerializer): + email = serializers.CharField(source="user.email", read_only=True) + id = serializers.CharField(source="user.id", read_only=True) + + class Meta: + model = OrganizationMember + fields = ("id", "email", "role") + + +class LimitedUserEmailListSerializer(serializers.ListSerializer): + def __init__(self, *args: Any, **kwargs: Any) -> None: + self.max_elements: int = kwargs.pop("max_elements", Common.MAX_EMAIL_IN_REQUEST) + super().__init__(*args, **kwargs) + + def validate(self, data: list[str]) -> Any: + if len(data) > self.max_elements: + raise ValidationError( + f"Exceeded maximum number of elements ({self.max_elements})" + ) + return data + + +class LimitedUserListSerializer(serializers.ListSerializer): + def __init__(self, *args: Any, **kwargs: Any) -> None: + self.max_elements: int = kwargs.pop("max_elements", Common.MAX_EMAIL_IN_REQUEST) + super().__init__(*args, **kwargs) + + def validate( + self, data: list[dict[str, Union[str, None]]] + ) -> list[dict[str, Union[str, None]]]: + if len(data) > self.max_elements: + raise ValidationError( + f"Exceeded maximum number of elements ({self.max_elements})" + ) + + for item in data: + if not isinstance(item, dict): + raise ValidationError("Each item in the list must be a dictionary.") + if "email" not in item: + raise ValidationError("Each item in the list must have 'email' key.") + if "role" not in item: + item["role"] = None + + return data + + +class InviteUserSerializer(serializers.Serializer): + users = LimitedUserListSerializer( + required=True, + child=serializers.DictField( + child=serializers.CharField(max_length=255, required=True), + required=False, # Make 'role' field optional + ), + max_elements=Common.MAX_EMAIL_IN_REQUEST, + ) + + def get_users( + self, validated_data: dict[str, Any] + ) -> list[dict[str, Union[str, None]]]: + return validated_data.get("users", []) + + +class RemoveUserFromOrganizationSerializer(serializers.Serializer): + emails = LimitedUserEmailListSerializer( + required=True, + child=serializers.EmailField(required=True), + max_elements=Common.MAX_EMAIL_IN_REQUEST, + ) + + def get_user_emails( + self, validated_data: dict[str, Union[list[str], None]] + ) -> list[str]: + return cast(list[str], validated_data.get(Common.USER_EMAILS, [])) + + +class ChangeUserRoleRequestSerializer(serializers.Serializer): + email = serializers.EmailField(required=True) + role = serializers.CharField(required=True) + + def get_user_email( + self, validated_data: dict[str, Union[str, None]] + ) -> Optional[str]: + return validated_data.get(Common.USER_EMAIL) + + def get_user_role( + self, validated_data: dict[str, Union[str, None]] + ) -> Optional[str]: + return validated_data.get(Common.USER_ROLE) + + +class DeleteInvitationRequestSerializer(serializers.Serializer): + id = serializers.EmailField(required=True) + + def get_id(self, validated_data: dict[str, Union[str, None]]) -> Optional[str]: + return validated_data.get(Common.ID) + + +class UserInfoSerializer(serializers.Serializer): + id = serializers.CharField() + email = serializers.CharField() + name = serializers.CharField() + display_name = serializers.CharField() + family_name = serializers.CharField() + picture = serializers.CharField() + + +class GetRolesResponseSerializer(serializers.Serializer): + id = serializers.CharField() + name = serializers.CharField() + description = serializers.CharField() + + def to_representation(self, instance: Any) -> OrderedDict[str, Any]: + data: OrderedDict[str, Any] = super().to_representation(instance) + return data + + +class ListInvitationsResponseSerializer(serializers.Serializer): + id = serializers.CharField() + email = serializers.CharField() + created_at = serializers.CharField() + expires_at = serializers.CharField() + + def to_representation(self, instance: Any) -> OrderedDict[str, Any]: + data: OrderedDict[str, Any] = super().to_representation(instance) + return data + + +class UpdateFlagSerializer(ModelSerializer): + + class Meta: + model = OrganizationMember + fields = ("is_login_onboarding_msg", "is_prompt_studio_onboarding_msg") diff --git a/backend/tenant_account_v2/tests.py b/backend/tenant_account_v2/tests.py new file mode 100644 index 0000000000..a39b155ac3 --- /dev/null +++ b/backend/tenant_account_v2/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/backend/tenant_account_v2/urls.py b/backend/tenant_account_v2/urls.py new file mode 100644 index 0000000000..50e46ab087 --- /dev/null +++ b/backend/tenant_account_v2/urls.py @@ -0,0 +1,11 @@ +from django.urls import include, path +from tenant_account_v2 import invitation_urls, users_urls +from tenant_account_v2.views import get_organization, get_roles, reset_password + +urlpatterns = [ + path("roles", get_roles, name="roles"), + path("users/", include(users_urls)), + path("invitation/", include(invitation_urls)), + path("organization", get_organization, name="get_organization"), + path("reset_password", reset_password, name="reset_password"), +] diff --git a/backend/tenant_account_v2/users_urls.py b/backend/tenant_account_v2/users_urls.py new file mode 100644 index 0000000000..7b2765f3f7 --- /dev/null +++ b/backend/tenant_account_v2/users_urls.py @@ -0,0 +1,37 @@ +from django.urls import path +from tenant_account_v2.users_view import OrganizationUserViewSet + +organization_user_role = OrganizationUserViewSet.as_view( + { + "post": OrganizationUserViewSet.assign_organization_role_to_user.__name__, + "delete": OrganizationUserViewSet.remove_organization_role_from_user.__name__, + } +) + +user_profile = OrganizationUserViewSet.as_view( + { + "get": OrganizationUserViewSet.get_user_profile.__name__, + "put": OrganizationUserViewSet.update_flags.__name__, + } +) + +invite_user = OrganizationUserViewSet.as_view( + { + "post": OrganizationUserViewSet.invite_user.__name__, + } +) + +organization_users = OrganizationUserViewSet.as_view( + { + "get": OrganizationUserViewSet.get_organization_members.__name__, + "delete": OrganizationUserViewSet.remove_members_from_organization.__name__, + } +) + + +urlpatterns = [ + path("", organization_users, name="organization_user"), + path("profile/", user_profile, name="user_profile"), + path("role/", organization_user_role, name="organization_user_role"), + path("invite/", invite_user, name="invite_user"), +] diff --git a/backend/tenant_account_v2/users_view.py b/backend/tenant_account_v2/users_view.py new file mode 100644 index 0000000000..b9deeea4fa --- /dev/null +++ b/backend/tenant_account_v2/users_view.py @@ -0,0 +1,198 @@ +import logging + +from account_v2.authentication_controller import AuthenticationController +from account_v2.exceptions import BadRequestException +from rest_framework import status, viewsets +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response +from tenant_account_v2.models import OrganizationMember +from tenant_account_v2.organization_member_service import OrganizationMemberService +from tenant_account_v2.serializer import ( + ChangeUserRoleRequestSerializer, + InviteUserSerializer, + OrganizationMemberSerializer, + RemoveUserFromOrganizationSerializer, + UpdateFlagSerializer, + UserInfoSerializer, + UserInviteResponseSerializer, +) +from utils.user_session import UserSessionUtils + +Logger = logging.getLogger(__name__) + + +class OrganizationUserViewSet(viewsets.ViewSet): + @action(detail=False, methods=["POST"]) + def assign_organization_role_to_user(self, request: Request) -> Response: + serializer = ChangeUserRoleRequestSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_email = serializer.get_user_email(serializer.validated_data) + role = serializer.get_user_role(serializer.validated_data) + if not (user_email and role): + raise BadRequestException + org_id: str = UserSessionUtils.get_organization_id(request) + auth_controller = AuthenticationController() + + auth_controller = AuthenticationController() + update_status = auth_controller.add_user_role( + request.user, org_id, user_email, role + ) + if update_status: + return Response( + status=status.HTTP_200_OK, + data={"status": "success", "message": "success"}, + ) + else: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"status": "failed", "message": "failed"}, + ) + + @action(detail=False, methods=["DELETE"]) + def remove_organization_role_from_user(self, request: Request) -> Response: + serializer = ChangeUserRoleRequestSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_email = serializer.get_user_email(serializer.validated_data) + role = serializer.get_user_role(serializer.validated_data) + if not (user_email and role): + raise BadRequestException + org_id: str = UserSessionUtils.get_organization_id(request) + auth_controller = AuthenticationController() + + auth_controller = AuthenticationController() + update_status = auth_controller.remove_user_role( + request.user, org_id, user_email, role + ) + if update_status: + return Response( + status=status.HTTP_200_OK, + data={"status": "success", "message": "success"}, + ) + else: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"status": "failed", "message": "failed"}, + ) + + @action(detail=False, methods=["GET"]) + def get_user_profile(self, request: Request) -> Response: + auth_controller = AuthenticationController() + try: + user_info = auth_controller.get_user_info(request) + role = auth_controller.get_organization_members_by_user(request.user) + if not user_info: + return Response( + status=status.HTTP_404_NOT_FOUND, + data={"message": "User Not Found"}, + ) + serialized_user_info = UserInfoSerializer(user_info).data + # Temporary fix for getting user role along with user info. + # Proper implementation would be adding role field to UserInfo. + serialized_user_info["is_admin"] = auth_controller.is_admin_by_role( + role.role + ) + # changes for displying onboarding msgs + org_member = OrganizationMemberService.get_user_by_id(id=request.user.id) + serialized_user_info["login_onboarding_message_displayed"] = ( + org_member.is_login_onboarding_msg + ) + serialized_user_info["prompt_onboarding_message_displayed"] = ( + org_member.is_prompt_studio_onboarding_msg + ) + + return Response( + status=status.HTTP_200_OK, data={"user": serialized_user_info} + ) + except Exception as error: + Logger.error(f"Error while get User : {error}") + return Response( + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + data={"message": "Internal Error"}, + ) + + @action(detail=False, methods=["POST"]) + def invite_user(self, request: Request) -> Response: + serializer = InviteUserSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_list = serializer.get_users(serializer.validated_data) + auth_controller = AuthenticationController() + invite_response = auth_controller.invite_user( + admin=request.user, + org_id=UserSessionUtils.get_organization_id(request), + user_list=user_list, + ) + + response_serializer = UserInviteResponseSerializer(invite_response, many=True) + + if invite_response and len(invite_response) != 0: + response = Response( + status=status.HTTP_200_OK, + data={"message": response_serializer.data}, + ) + else: + response = Response( + status=status.HTTP_400_BAD_REQUEST, + data={"message": "failed"}, + ) + return response + + @action(detail=False, methods=["DELETE"]) + def remove_members_from_organization(self, request: Request) -> Response: + serializer = RemoveUserFromOrganizationSerializer(data=request.data) + + serializer.is_valid(raise_exception=True) + user_emails = serializer.get_user_emails(serializer.validated_data) + organization_id: str = UserSessionUtils.get_organization_id(request) + + auth_controller = AuthenticationController() + is_updated = auth_controller.remove_users_from_organization( + admin=request.user, + organization_id=organization_id, + user_emails=user_emails, + ) + if is_updated: + return Response( + status=status.HTTP_200_OK, + data={"status": "success", "message": "success"}, + ) + else: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"status": "failed", "message": "failed"}, + ) + + @action(detail=False, methods=["GET"]) + def get_organization_members(self, request: Request) -> Response: + auth_controller = AuthenticationController() + if UserSessionUtils.get_organization_id(request): + members: list[OrganizationMember] = ( + auth_controller.get_organization_members_by_org_id() + ) + serialized_members = OrganizationMemberSerializer(members, many=True).data + return Response( + status=status.HTTP_200_OK, + data={"message": "success", "members": serialized_members}, + ) + return Response( + status=status.HTTP_401_UNAUTHORIZED, + data={"message": "cookie not found"}, + ) + + @action(detail=False, methods=["PUT"]) + def update_flags(self, request: Request) -> Response: + serializer = UpdateFlagSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + org_member = OrganizationMemberService.get_user_by_id(id=request.user.id) + org_member.is_login_onboarding_msg = serializer.validated_data.get( + "is_login_onboarding_msg" + ) + + org_member.is_prompt_studio_onboarding_msg = serializer.validated_data.get( + "is_prompt_studio_onboarding_msg" + ) + org_member.save() + return Response( + status=status.HTTP_200_OK, + data={"status": "success", "message": "success"}, + ) diff --git a/backend/tenant_account_v2/views.py b/backend/tenant_account_v2/views.py new file mode 100644 index 0000000000..e64cfd4c2e --- /dev/null +++ b/backend/tenant_account_v2/views.py @@ -0,0 +1,89 @@ +import logging +from typing import Any + +from account_v2.authentication_controller import AuthenticationController +from account_v2.dto import UserRoleData +from account_v2.models import Organization +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.request import Request +from rest_framework.response import Response +from tenant_account_v2.dto import OrganizationLoginResponse, ResetUserPasswordDto +from tenant_account_v2.serializer import ( + GetRolesResponseSerializer, + OrganizationLoginResponseSerializer, +) +from utils.user_session import UserSessionUtils + +logger = logging.getLogger(__name__) + + +@api_view(["GET"]) +def logout(request: Request) -> Response: + auth_controller = AuthenticationController() + return auth_controller.user_logout(request) + + +@api_view(["GET"]) +def get_roles(request: Request) -> Response: + auth_controller = AuthenticationController() + roles: list[UserRoleData] = auth_controller.get_user_roles() + serialized_members = GetRolesResponseSerializer(roles, many=True).data + return Response( + status=status.HTTP_200_OK, + data={"message": "success", "members": serialized_members}, + ) + + +@api_view(["POST"]) +def reset_password(request: Request) -> Response: + auth_controller = AuthenticationController() + data: ResetUserPasswordDto = auth_controller.reset_user_password(request.user) + if data.status: + return Response( + status=status.HTTP_200_OK, + data={"status": "success", "message": data.message}, + ) + else: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"status": "failed", "message": data.message}, + ) + + +@api_view(["GET"]) +def get_organization(request: Request) -> Response: + auth_controller = AuthenticationController() + try: + organization_id = UserSessionUtils.get_organization_id(request) + org_data = auth_controller.get_organization_info(organization_id) + if not org_data: + return Response( + status=status.HTTP_404_NOT_FOUND, + data={"message": "Org Not Found"}, + ) + response = makeSignupResponse(org_data) + return Response( + status=status.HTTP_201_CREATED, + data={"message": "success", "organization": response}, + ) + + except Exception as error: + logger.error(f"Error while get User : {error}") + return Response( + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + data={"message": "Internal Error"}, + ) + + +def makeSignupResponse( + organization: Organization, +) -> Any: + return OrganizationLoginResponseSerializer( + OrganizationLoginResponse( + organization.name, + organization.display_name, + organization.organization_id, + organization.created_at, + ) + ).data