diff --git a/apiserver/plane/authentication/adapter/base.py b/apiserver/plane/authentication/adapter/base.py index 97d0bf908..c8e7bd316 100644 --- a/apiserver/plane/authentication/adapter/base.py +++ b/apiserver/plane/authentication/adapter/base.py @@ -100,6 +100,12 @@ class Adapter: user.save() Profile.objects.create(user=user) + if not user.is_active: + raise AuthenticationException( + AUTHENTICATION_ERROR_CODES["USER_ACCOUNT_DEACTIVATED"], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + # Update user details user.last_login_medium = self.provider user.last_active = timezone.now() diff --git a/apiserver/plane/authentication/views/app/check.py b/apiserver/plane/authentication/views/app/check.py index 0abefd79f..2b7e4075a 100644 --- a/apiserver/plane/authentication/views/app/check.py +++ b/apiserver/plane/authentication/views/app/check.py @@ -24,62 +24,59 @@ class EmailCheckSignUpEndpoint(APIView): ] def post(self, request): - # Check instance configuration - instance = Instance.objects.first() - if instance is None or not instance.is_setup_done: - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], - error_message="INSTANCE_NOT_CONFIGURED", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) - - email = request.data.get("email", False) - - # Return error if email is not present - if not email: - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], - error_message="EMAIL_REQUIRED", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) - - # Validate email try: + # Check instance configuration + instance = Instance.objects.first() + if instance is None or not instance.is_setup_done: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "INSTANCE_NOT_CONFIGURED" + ], + error_message="INSTANCE_NOT_CONFIGURED", + ) + email = request.data.get("email", False) + + # Return error if email is not present + if not email: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], + error_message="EMAIL_REQUIRED", + ) + + # Validate email validate_email(email) + + existing_user = User.objects.filter(email=email).first() + + if existing_user: + # check if the account is the deactivated + if not existing_user.is_active: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ALREADY_EXIST" + ], + error_message="USER_ALREADY_EXIST", + ) + return Response( + {"status": True}, + status=status.HTTP_200_OK, + ) except ValidationError: - exc = AuthenticationException( + raise AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) + except AuthenticationException as e: return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, + e.get_error_dict(), status=status.HTTP_400_BAD_REQUEST ) - existing_user = User.objects.filter(email=email).first() - - if existing_user: - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], - error_message="USER_ALREADY_EXIST", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) - return Response( - {"status": True}, - status=status.HTTP_200_OK, - ) - class EmailCheckSignInEndpoint(APIView): @@ -88,61 +85,59 @@ class EmailCheckSignInEndpoint(APIView): ] def post(self, request): - # Check instance configuration - instance = Instance.objects.first() - if instance is None or not instance.is_setup_done: - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], - error_message="INSTANCE_NOT_CONFIGURED", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) - - email = request.data.get("email", False) - - # Return error if email is not present - if not email: - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], - error_message="EMAIL_REQUIRED", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) - - # Validate email try: + # Check instance configuration + instance = Instance.objects.first() + if instance is None or not instance.is_setup_done: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "INSTANCE_NOT_CONFIGURED" + ], + error_message="INSTANCE_NOT_CONFIGURED", + ) + + email = request.data.get("email", False) + + # Return error if email is not present + if not email: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], + error_message="EMAIL_REQUIRED", + ) + + # Validate email validate_email(email) + + existing_user = User.objects.filter(email=email).first() + + # If existing user + if existing_user: + # Raise different exception when user is not active + if not existing_user.is_active: + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + + return Response( + { + "status": True, + "is_password_autoset": existing_user.is_password_autoset, + }, + status=status.HTTP_200_OK, + ) + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], + error_message="USER_DOES_NOT_EXIST", + ) except ValidationError: - exc = AuthenticationException( + raise AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) + except AuthenticationException as e: return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, + e.get_error_dict(), status=status.HTTP_400_BAD_REQUEST ) - - existing_user = User.objects.filter(email=email).first() - - if existing_user: - return Response( - { - "status": True, - "is_password_autoset": existing_user.is_password_autoset, - }, - status=status.HTTP_200_OK, - ) - exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], - error_message="USER_DOES_NOT_EXIST", - ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) diff --git a/apiserver/plane/authentication/views/app/email.py b/apiserver/plane/authentication/views/app/email.py index 4093be108..52fcdbc24 100644 --- a/apiserver/plane/authentication/views/app/email.py +++ b/apiserver/plane/authentication/views/app/email.py @@ -90,7 +90,9 @@ class SignInAuthEndpoint(View): ) return HttpResponseRedirect(url) - if not User.objects.filter(email=email).exists(): + existing_user = User.objects.filter(email=email).first() + + if not existing_user: exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], error_message="USER_DOES_NOT_EXIST", @@ -105,6 +107,22 @@ class SignInAuthEndpoint(View): ) return HttpResponseRedirect(url) + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), + ) + return HttpResponseRedirect(url) + try: provider = EmailProvider( request=request, key=email, code=password, is_signup=False @@ -197,7 +215,26 @@ class SignUpAuthEndpoint(View): ) return HttpResponseRedirect(url) - if User.objects.filter(email=email).exists(): + existing_user = User.objects.filter(email=email).first() + + if existing_user: + # Existing User + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_app=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) + exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], error_message="USER_ALREADY_EXIST", diff --git a/apiserver/plane/authentication/views/app/magic.py b/apiserver/plane/authentication/views/app/magic.py index 0fa529674..3335eda7d 100644 --- a/apiserver/plane/authentication/views/app/magic.py +++ b/apiserver/plane/authentication/views/app/magic.py @@ -95,7 +95,26 @@ class MagicSignInEndpoint(View): ) return HttpResponseRedirect(url) - if not User.objects.filter(email=email).exists(): + # Existing User + existing_user = User.objects.filter(email=email).first() + + if not existing_user: + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), + ) + return HttpResponseRedirect(url) + exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], error_message="USER_DOES_NOT_EXIST", @@ -167,8 +186,25 @@ class MagicSignUpEndpoint(View): "?" + urlencode(params), ) return HttpResponseRedirect(url) + # Existing user + existing_user = User.objects.filter(email=email).first() + if not existing_user: + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_app=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) - if User.objects.filter(email=email).exists(): exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], error_message="USER_ALREADY_EXIST", diff --git a/apiserver/plane/authentication/views/app/password_management.py b/apiserver/plane/authentication/views/app/password_management.py index b26b57760..dd14ceb91 100644 --- a/apiserver/plane/authentication/views/app/password_management.py +++ b/apiserver/plane/authentication/views/app/password_management.py @@ -63,23 +63,13 @@ class ForgotPasswordEndpoint(APIView): status=status.HTTP_400_BAD_REQUEST, ) - (EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD) = ( - get_configuration_value( - [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, - { - "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER"), - }, - { - "key": "EMAIL_HOST_PASSWORD", - "default": os.environ.get("EMAIL_HOST_PASSWORD"), - }, - ] - ) + (EMAIL_HOST,) = get_configuration_value( + [ + { + "key": "EMAIL_HOST", + "default": os.environ.get("EMAIL_HOST"), + }, + ] ) if not (EMAIL_HOST): diff --git a/apiserver/plane/authentication/views/space/check.py b/apiserver/plane/authentication/views/space/check.py index 49baad081..83f52e28f 100644 --- a/apiserver/plane/authentication/views/space/check.py +++ b/apiserver/plane/authentication/views/space/check.py @@ -68,6 +68,17 @@ class EmailCheckEndpoint(APIView): # If existing user if existing_user: + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + return Response( + exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST + ) + return Response( { "existing": True, diff --git a/apiserver/plane/authentication/views/space/email.py b/apiserver/plane/authentication/views/space/email.py index e11ab29b5..73690e2fa 100644 --- a/apiserver/plane/authentication/views/space/email.py +++ b/apiserver/plane/authentication/views/space/email.py @@ -83,7 +83,10 @@ class SignInAuthSpaceEndpoint(View): ) return HttpResponseRedirect(url) - if not User.objects.filter(email=email).exists(): + # Existing User + existing_user = User.objects.filter(email=email).first() + + if not existing_user: exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], error_message="USER_DOES_NOT_EXIST", @@ -98,6 +101,22 @@ class SignInAuthSpaceEndpoint(View): ) return HttpResponseRedirect(url) + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_space=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) + try: provider = EmailProvider( request=request, key=email, code=password, is_signup=False @@ -185,7 +204,26 @@ class SignUpAuthSpaceEndpoint(View): ) return HttpResponseRedirect(url) - if User.objects.filter(email=email).exists(): + # Existing User + existing_user = User.objects.filter(email=email).first() + + if existing_user: + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_space=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) + exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], error_message="USER_ALREADY_EXIST", diff --git a/apiserver/plane/authentication/views/space/magic.py b/apiserver/plane/authentication/views/space/magic.py index 45a8e3755..650b8955a 100644 --- a/apiserver/plane/authentication/views/space/magic.py +++ b/apiserver/plane/authentication/views/space/magic.py @@ -90,11 +90,14 @@ class MagicSignInSpaceEndpoint(View): ) return HttpResponseRedirect(url) - if not User.objects.filter(email=email).exists(): - params = { - "error_code": "USER_DOES_NOT_EXIST", - "error_message": "User could not be found with the given email.", - } + existing_user = User.objects.filter(email=email).first() + + if not existing_user: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], + error_message="USER_DOES_NOT_EXIST", + ) + params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( @@ -103,6 +106,22 @@ class MagicSignInSpaceEndpoint(View): ) return HttpResponseRedirect(url) + # Active User + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_space=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) try: provider = MagicCodeProvider( request=request, key=f"magic_{email}", code=code @@ -155,8 +174,25 @@ class MagicSignUpSpaceEndpoint(View): "?" + urlencode(params), ) return HttpResponseRedirect(url) + # Existing User + existing_user = User.objects.filter(email=email).first() + if existing_user: + if not existing_user.is_active: + exc = AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "USER_ACCOUNT_DEACTIVATED" + ], + error_message="USER_ACCOUNT_DEACTIVATED", + ) + params = exc.get_error_dict() + if next_path: + params["next_path"] = str(next_path) + url = urljoin( + base_host(request=request, is_space=True), + "?" + urlencode(params), + ) + return HttpResponseRedirect(url) - if User.objects.filter(email=email).exists(): exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], error_message="USER_ALREADY_EXIST", diff --git a/apiserver/plane/authentication/views/space/signout.py b/apiserver/plane/authentication/views/space/signout.py index 655d8b1c8..58bf54b80 100644 --- a/apiserver/plane/authentication/views/space/signout.py +++ b/apiserver/plane/authentication/views/space/signout.py @@ -1,6 +1,3 @@ -# Python imports -from urllib.parse import urlencode, urljoin - # Django imports from django.views import View from django.contrib.auth import logout