@@ -0,0 +1,6 @@ +from django.utils.translation import gettext as _ + + +class AccessDenied(Exception): + def __str__(self): + return _('You do not have sufficient rights to perform this action') @@ -0,0 +1,11 @@ +from authentication.exceptions.email_exceptions.letter_not_found import ( + LetterNotFound +) +from authentication.exceptions.email_exceptions.letter_unknown import ( + LetterUnknownException +) + +__all__ = ( + 'LetterNotFound', + 'LetterUnknownException' +) \ No newline at end of file @@ -0,0 +1,6 @@ +from django.utils.translation import gettext as _ + + +class LetterNotFound(Exception): + def __str__(self): + return _('Letter template not found') @@ -0,0 +1,6 @@ +from django.utils.translation import gettext as _ + + +class LetterUnknownException(Exception): + def __str__(self): + return _('There was an unknown error while sending an email') @@ -10,6 +10,7 @@ from authentication.exceptions.business_host_exceptions import ( AlreadyAccount, AlreadyHasPlan, ) +from authentication.exceptions.business_host_exceptions.access_denied import AccessDenied from authentication.exceptions.business_host_exceptions.already_host import ( AlreadyHost, ) @@ -246,3 +247,14 @@ class BusinessHostService: self.user.host_account.save() return BusinessHostSelector(self.user).get_allowed_models() + + def reinvite_business_account(self, business_account: BusinessAccount) -> None: + if ( + self.user.account_type == business_account.user.account_type + and self.user.account_type == 'business_admin' + ): + raise AccessDenied + password = generate_token(15) + business_account.user.set_password(password) + business_account.user.save() + EmailService(self.user).send_reinvited_email(business_account, password) @@ -4,9 +4,12 @@ import dns.resolver from django.conf import settings from django.core.mail import EmailMessage, send_mail +from django.template import TemplateDoesNotExist +from django.template.loader import get_template from django.utils.html import format_html, strip_tags from django.utils.translation import gettext_lazy as _ +from authentication.exceptions.email_exceptions import LetterNotFound, LetterUnknownException from authentication.exceptions.user import DomainNotFound from authentication.models import BusinessAccount, BusinessUserHost from authentication.models.user import CustomUserModel @@ -139,6 +142,20 @@ class EmailService: ) mail.send(fail_silently=True) + def send_reinvited_email(self, account: BusinessAccount, password: str | None = None) -> None: + try: + template = get_template('authentication/reinvited_employee_letter.html') + html_message = template.render(context={'email': account.user.email, 'password': password}) + except TemplateDoesNotExist: + raise LetterNotFound + except Exception as exc: + raise LetterUnknownException from exc + self.send_email( + subject='Ваш аккаунт на платформе AIR', + message=html_message, + user_email=account.user.email + ) + def send_corporate_invitation_email(self, account: BusinessAccount): if self.user.host_account is None: raise Exception(_('Regular users cannot send invitation letters')) @@ -0,0 +1,13 @@ + + + + + Ваш аккаунт на платформе AIR: + + +

Новые данные для входа на платформу AIR:

+

E-mail: {{ email }}

+

Пароль: {{ password }}

+

Всегда с вами, команда AIR

+ + @@ -74,6 +74,11 @@ urlpatterns = [ views.BusinessHostAPIView.as_view(), name='business-host', ), + path( + 'business-host/re-invite/', + views.ReinviteBusinessAccountAPIView.as_view(), + name='business-host-re-invite', + ), path( 'business-host/create', views.StartHostRegistrationAPIView.as_view(), @@ -3,6 +3,7 @@ from datetime import date, datetime, timedelta from decimal import Decimal from itertools import chain from logging import getLogger +from typing import Any, Tuple, Dict from uuid import UUID from django.conf import settings @@ -22,9 +23,11 @@ from rest_framework.response import Response from rest_framework.views import APIView from authentication.exceptions import BaseAlready +from authentication.exceptions.business_host_exceptions.access_denied import AccessDenied from authentication.exceptions.business_host_exceptions.not_allowed_ip import ( NotAllowedIP, ) +from authentication.exceptions.email_exceptions import LetterNotFound, LetterUnknownException from authentication.exceptions.email_token import EmailTokenNotFound from authentication.exceptions.user import ( DomainNotFound, @@ -38,12 +41,12 @@ from authentication.models.business_host import BusinessUserHost from authentication.models.choices import AccountPrivileges, InvitationStatus from authentication.models.whitelist import CompanyIPWhitelist from authentication.permissions import ( - ChangeEmployeePassPermission, HasBusinessAdminPermissions, IsAnonymous, IsBusinessSecurity, IsTelegramAirBot, IsVKMiniApp, + ChangeEmployeePassPermission, ) from authentication.selectors.business_account_selector import BusinessAccountSelector from authentication.selectors.business_host_selector import ( @@ -62,7 +65,6 @@ from authentication.serializers import ( BusinessGroupUpdateSerializer, BusinessHostSerializer, BusinessHostUpdateSerializer, - ChangePasswordSerializer, CompanyIPWhitelistSerializer, DeleteBusinessAccountSerializer, DeleteModelsSerializer, @@ -81,6 +83,7 @@ from authentication.serializers import ( UpdateProfilePictureSerializer, UpdateUserDataSerializer, UserDataSerializer, + ChangePasswordSerializer, ) from authentication.services.business_account_service import ( BusinessAccountService, @@ -323,6 +326,37 @@ class BusinessHostAPIView(APIView): ) +class ReinviteBusinessAccountAPIView(APIView): + permission_classes = (HasBusinessAdminPermissions,) + + @extend_schema( + parameters=[ + OpenApiParameter('email', str, 'path', required=True), + ], + ) + def post(self, request: Request, email: str, *args: Tuple[Any], **kwargs: Dict[Any, Any]) -> Response: + """Reinvite business account including generation of a new password""" + try: + business_account = BusinessAccountSelector.filter_by_email( + email, + BusinessAccountService.get_company_name(request.user) + ) + BusinessHostService(request.user).reinvite_business_account(business_account=business_account) + return Response({'detail': _('Business account has been reinvited')}, status=status.HTTP_200_OK) + except LetterNotFound as exc: + return Response({'detail': f'{exc}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + except LetterUnknownException as exc: + logger.exception(exc) + return Response({'detail': f'{exc}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + except AccessDenied as exc: + return Response({'detail': f'{exc}'}, status=status.HTTP_403_FORBIDDEN) + except Exception as exc: + logger.exception(exc) + return Response( + {'detail': _('Server error occured')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + class HostWorkersAPIView(APIView): permission_classes = (HasBusinessAdminPermissions,) @@ -372,8 +406,8 @@ class ChangeHostPassAPIView(APIView): ) BusinessAccountService(business_account).update_user_password(request) return Response({'detail': 'Host password has been updated'}, status=status.HTTP_200_OK) - except Exception as err: - return Response({'detail': f'{err}'}, status=status.HTTP_400_BAD_REQUEST) + except Exception as exc: + return Response({'detail': f'{exc}'}, status=status.HTTP_400_BAD_REQUEST) class HostModelStatisticsAPIView(APIView): @@ -1118,3 +1118,15 @@ msgid "Model is blocked by outdating or temporary block, please retry later" msgstr "" "Модель заблокирована, т.к перестала обновляться или временно, попробуйте " "позже" + +msgid "Business account has been reinvited" +msgstr "Повторное приглашение сотруднику успешно отправлено" + +msgid "You do not have sufficient rights to perform this action" +msgstr "У вас недостаточно прав для выполнения этого действия" + +msgid "Letter template not found" +msgstr "Шаблон письма не найден" + +msgid "There was an unknown error while sending an email" +msgstr "При отправке письма произошла неизвестная ошибка" \ No newline at end of file