fix: authentication views updated with new workflow (#4547)

* dev: update email check endpoint

* fix: auth magic login check

* chore: updated the error code handler and handled authentication workflow

* dev: add magic link login

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: guru_sainath <gurusainath007@gmail.com>
This commit is contained in:
sriram veeraghanta 2024-05-22 14:49:06 +05:30 committed by GitHub
parent 509d5fe554
commit 9013497a5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 275 additions and 235 deletions

View file

@ -2,13 +2,12 @@ from django.urls import path
from .views import (
CSRFTokenEndpoint,
EmailCheckSignInEndpoint,
EmailCheckSignUpEndpoint,
ForgotPasswordEndpoint,
SetUserPasswordEndpoint,
ResetPasswordEndpoint,
ChangePasswordEndpoint,
# App
EmailCheckEndpoint,
GitHubCallbackEndpoint,
GitHubOauthInitiateEndpoint,
GoogleCallbackEndpoint,
@ -22,7 +21,7 @@ from .views import (
ForgotPasswordSpaceEndpoint,
ResetPasswordSpaceEndpoint,
# Space
EmailCheckEndpoint,
EmailCheckSpaceEndpoint,
GitHubCallbackSpaceEndpoint,
GitHubOauthInitiateSpaceEndpoint,
GoogleCallbackSpaceEndpoint,
@ -154,18 +153,13 @@ urlpatterns = [
),
# Email Check
path(
"sign-up/email-check/",
EmailCheckSignUpEndpoint.as_view(),
name="email-check-sign-up",
),
path(
"sign-in/email-check/",
EmailCheckSignInEndpoint.as_view(),
name="email-check-sign-in",
"email-check/",
EmailCheckEndpoint.as_view(),
name="email-check",
),
path(
"spaces/email-check/",
EmailCheckEndpoint.as_view(),
EmailCheckSpaceEndpoint.as_view(),
name="email-check",
),
# Password

View file

@ -4,7 +4,7 @@ from .common import (
SetUserPasswordEndpoint,
)
from .app.check import EmailCheckSignInEndpoint, EmailCheckSignUpEndpoint
from .app.check import EmailCheckEndpoint
from .app.email import (
SignInAuthEndpoint,
@ -47,7 +47,7 @@ from .space.magic import (
from .space.signout import SignOutAuthSpaceEndpoint
from .space.check import EmailCheckEndpoint
from .space.check import EmailCheckSpaceEndpoint
from .space.password_management import (
ForgotPasswordSpaceEndpoint,

View file

@ -1,3 +1,6 @@
# Python imports
import os
# Django imports
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
@ -16,8 +19,12 @@ from plane.authentication.adapter.error import (
AUTHENTICATION_ERROR_CODES,
)
from plane.authentication.rate_limit import AuthenticationThrottle
from plane.license.utils.instance_value import (
get_configuration_value,
)
class EmailCheckSignUpEndpoint(APIView):
class EmailCheckEndpoint(APIView):
permission_classes = [
AllowAny,
@ -28,128 +35,99 @@ class EmailCheckSignUpEndpoint(APIView):
]
def post(self, request):
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 user already exist
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES[
"USER_ALREADY_EXIST"
],
error_message="USER_ALREADY_EXIST",
)
# 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(
{"status": True},
exc.get_error_dict(),
status=status.HTTP_400_BAD_REQUEST,
)
(EMAIL_HOST, ENABLE_MAGIC_LINK_LOGIN) = get_configuration_value(
[
{
"key": "EMAIL_HOST",
"default": os.environ.get("EMAIL_HOST", ""),
},
{
"key": "ENABLE_MAGIC_LINK_LOGIN",
"default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"),
},
]
)
smtp_configured = bool(EMAIL_HOST)
is_magic_login_enabled = ENABLE_MAGIC_LINK_LOGIN == "1"
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:
validate_email(email)
except ValidationError:
exc = AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"],
error_message="INVALID_EMAIL",
)
return Response(
exc.get_error_dict(),
status=status.HTTP_400_BAD_REQUEST,
)
# Check if a user already exists with the given email
existing_user = User.objects.filter(email=email).first()
# 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,
"status": (
"MAGIC_CODE"
if existing_user.is_password_autoset
and smtp_configured
and is_magic_login_enabled
else "CREDENTIAL"
),
},
status=status.HTTP_200_OK,
)
except ValidationError:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"],
error_message="INVALID_EMAIL",
)
except AuthenticationException as e:
return Response(
e.get_error_dict(), status=status.HTTP_400_BAD_REQUEST
)
class EmailCheckSignInEndpoint(APIView):
permission_classes = [
AllowAny,
]
throttle_classes = [
AuthenticationThrottle,
]
def post(self, request):
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 true
return Response(
{
"status": True,
"is_password_autoset": existing_user.is_password_autoset,
},
status=status.HTTP_200_OK,
)
# Raise error
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"],
error_message="USER_DOES_NOT_EXIST",
)
except ValidationError:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"],
error_message="INVALID_EMAIL",
)
except AuthenticationException as e:
return Response(
e.get_error_dict(), status=status.HTTP_400_BAD_REQUEST
)
# Else return response
return Response(
{
"existing": False,
"status": (
"MAGIC_CODE"
if smtp_configured and is_magic_login_enabled
else "CREDENTIAL"
),
},
status=status.HTTP_200_OK,
)

View file

@ -1,3 +1,6 @@
# Python imports
import os
# Django imports
from django.core.validators import validate_email
from django.core.exceptions import ValidationError
@ -16,8 +19,10 @@ from plane.authentication.adapter.error import (
AuthenticationException,
)
from plane.authentication.rate_limit import AuthenticationThrottle
from plane.license.utils.instance_value import get_configuration_value
class EmailCheckEndpoint(APIView):
class EmailCheckSpaceEndpoint(APIView):
permission_classes = [
AllowAny,
@ -42,6 +47,22 @@ class EmailCheckEndpoint(APIView):
status=status.HTTP_400_BAD_REQUEST,
)
(EMAIL_HOST, ENABLE_MAGIC_LINK_LOGIN) = get_configuration_value(
[
{
"key": "EMAIL_HOST",
"default": os.environ.get("EMAIL_HOST", ""),
},
{
"key": "ENABLE_MAGIC_LINK_LOGIN",
"default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"),
},
]
)
smtp_configured = bool(EMAIL_HOST)
is_magic_login_enabled = ENABLE_MAGIC_LINK_LOGIN == "1"
email = request.data.get("email", False)
# Return error if email is not present
@ -86,12 +107,25 @@ class EmailCheckEndpoint(APIView):
return Response(
{
"existing": True,
"is_password_autoset": existing_user.is_password_autoset,
"status": (
"MAGIC_CODE"
if existing_user.is_password_autoset
and smtp_configured
and is_magic_login_enabled
else "CREDENTIAL"
),
},
status=status.HTTP_200_OK,
)
# Else return response
return Response(
{"existing": False, "is_password_autoset": False},
{
"existing": False,
"status": (
"MAGIC_CODE"
if smtp_configured and is_magic_login_enabled
else "CREDENTIAL"
),
},
status=status.HTTP_200_OK,
)