Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: email validation #4707

Merged
merged 4 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 100 additions & 37 deletions apiserver/plane/authentication/adapter/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# Django imports
from django.utils import timezone
from django.core.validators import validate_email
from django.core.exceptions import ValidationError

# Third party imports
from zxcvbn import zxcvbn
Expand Down Expand Up @@ -46,60 +48,124 @@ def create_update_account(self, user):
def authenticate(self):
raise NotImplementedError

def sanitize_email(self, email):
# Check if email is present
if not email:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"],
error_message="INVALID_EMAIL",
payload={"email": email},
)

# Sanitize email
email = str(email).lower().strip()

# validate email
try:
validate_email(email)
except ValidationError:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"],
error_message="INVALID_EMAIL",
payload={"email": email},
)
# Return email
return email

def validate_password(self, email):
"""Validate password strength"""
results = zxcvbn(self.code)
if results["score"] < 3:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
error_message="INVALID_PASSWORD",
payload={"email": email},
)
return

def __check_signup(self, email):
"""Check if sign up is enabled or not and raise exception if not enabled"""

# Get configuration value
(ENABLE_SIGNUP,) = get_configuration_value(
[
{
"key": "ENABLE_SIGNUP",
"default": os.environ.get("ENABLE_SIGNUP", "1"),
},
]
)

# Check if sign up is disabled and invite is present or not
if (
ENABLE_SIGNUP == "0"
and not WorkspaceMemberInvite.objects.filter(
email=email,
).exists()
):
# Raise exception
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["SIGNUP_DISABLED"],
error_message="SIGNUP_DISABLED",
payload={"email": email},
)

return True

def save_user_data(self, user):
# Update user details
user.last_login_medium = self.provider
user.last_active = timezone.now()
user.last_login_time = timezone.now()
user.last_login_ip = self.request.META.get("REMOTE_ADDR")
user.last_login_uagent = self.request.META.get("HTTP_USER_AGENT")
user.token_updated_at = timezone.now()
user.save()
return user

def complete_login_or_signup(self):
# Get email
email = self.user_data.get("email")

# Sanitize email
email = self.sanitize_email(email)

# Check if the user is present
user = User.objects.filter(email=email).first()
# Check if sign up case or login
is_signup = bool(user)
# If user is not present, create a new user
if not user:
# New user
(ENABLE_SIGNUP,) = get_configuration_value(
[
{
"key": "ENABLE_SIGNUP",
"default": os.environ.get("ENABLE_SIGNUP", "1"),
},
]
)
if (
ENABLE_SIGNUP == "0"
and not WorkspaceMemberInvite.objects.filter(
email=email,
).exists()
):
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["SIGNUP_DISABLED"],
error_message="SIGNUP_DISABLED",
payload={"email": email},
)
self.__check_signup(email)

# Initialize user
user = User(email=email, username=uuid.uuid4().hex)

# Check if password is autoset
if self.user_data.get("user").get("is_password_autoset"):
user.set_password(uuid.uuid4().hex)
user.is_password_autoset = True
user.is_email_verified = True

# Validate password
else:
# Validate password
results = zxcvbn(self.code)
if results["score"] < 3:
raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES[
"INVALID_PASSWORD"
],
error_message="INVALID_PASSWORD",
payload={"email": email},
)

self.validate_password(email)
# Set password
user.set_password(self.code)
user.is_password_autoset = False

# Set user details
avatar = self.user_data.get("user", {}).get("avatar", "")
first_name = self.user_data.get("user", {}).get("first_name", "")
last_name = self.user_data.get("user", {}).get("last_name", "")
user.avatar = avatar if avatar else ""
user.first_name = first_name if first_name else ""
user.last_name = last_name if last_name else ""
user.save()

# Create profile
Profile.objects.create(user=user)

if not user.is_active:
Expand All @@ -108,23 +174,20 @@ def complete_login_or_signup(self):
error_message="USER_ACCOUNT_DEACTIVATED",
)

# Update user details
user.last_login_medium = self.provider
user.last_active = timezone.now()
user.last_login_time = timezone.now()
user.last_login_ip = self.request.META.get("REMOTE_ADDR")
user.last_login_uagent = self.request.META.get("HTTP_USER_AGENT")
user.token_updated_at = timezone.now()
user.save()
# Save user data
user = self.save_user_data(user=user)

# Call callback if present
if self.callback:
self.callback(
user,
is_signup,
self.request,
)

# Create or update account if token data is present
if self.token_data:
self.create_update_account(user=user)

# Return user
return user
2 changes: 2 additions & 0 deletions apiserver/plane/authentication/adapter/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
"ADMIN_USER_DEACTIVATED": 5190,
# Rate limit
"RATE_LIMIT_EXCEEDED": 5900,
# Unknown
"AUTHENTICATION_FAILED": 5999,
}


Expand Down
10 changes: 5 additions & 5 deletions apiserver/plane/authentication/adapter/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ def get_user_response(self):
response.raise_for_status()
return response.json()
except requests.RequestException:
code = (
"GOOGLE_OAUTH_PROVIDER_ERROR"
if self.provider == "google"
else "GITHUB_OAUTH_PROVIDER_ERROR"
)
if self.provider == "google":
code = "GOOGLE_OAUTH_PROVIDER_ERROR"
if self.provider == "github":
code = "GITHUB_OAUTH_PROVIDER_ERROR"

raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES[code],
error_message=str(code),
Expand Down
Loading