mirror of
https://github.com/i701/sarlink-portal-api.git
synced 2025-06-28 04:13:58 +00:00
Refactor and enhance device management and authentication features
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 4m12s
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 4m12s
- Updated the `reverse_dhivehi_string` function to correct the range for combining characters. - Added new device handling in the health check view and integrated the `add_new_devices_to_omada` task. - Improved date handling in `CreateTemporaryUserView` to ensure proper string conversion. - Enhanced OTP sending by converting mobile numbers to strings. - Implemented MAC address validation in the `Device` model using a custom validator. - Removed unnecessary fields from the `CreateDeviceSerializer`. - Normalized MAC address format in the `DeviceListCreateAPIView`. - Updated the `djangopasswordlessknox` package to improve code consistency and readability. - Added migration to enforce MAC address validation in the database.
This commit is contained in:
@ -6,7 +6,11 @@ from django.core.validators import RegexValidator
|
||||
from rest_framework import serializers
|
||||
from djangopasswordlessknox.models import CallbackToken
|
||||
from djangopasswordlessknox.settings import api_settings
|
||||
from djangopasswordlessknox.utils import authenticate_by_token, verify_user_alias, validate_token_age
|
||||
from djangopasswordlessknox.utils import (
|
||||
authenticate_by_token,
|
||||
verify_user_alias,
|
||||
validate_token_age,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
User = get_user_model()
|
||||
@ -14,11 +18,11 @@ User = get_user_model()
|
||||
|
||||
class TokenField(serializers.CharField):
|
||||
default_error_messages = {
|
||||
'required': _('Invalid Token'),
|
||||
'invalid': _('Invalid Token'),
|
||||
'blank': _('Invalid Token'),
|
||||
'max_length': _('Tokens are {max_length} digits long.'),
|
||||
'min_length': _('Tokens are {min_length} digits long.')
|
||||
"required": _("Invalid Token"),
|
||||
"invalid": _("Invalid Token"),
|
||||
"blank": _("Invalid Token"),
|
||||
"max_length": _("Tokens are {max_length} digits long."),
|
||||
"min_length": _("Tokens are {min_length} digits long."),
|
||||
}
|
||||
|
||||
|
||||
@ -48,10 +52,10 @@ class AbstractBaseAliasAuthenticationSerializer(serializers.Serializer):
|
||||
except User.DoesNotExist:
|
||||
# If no user is found, raise an error
|
||||
msg = ""
|
||||
if self.alias_type == 'email':
|
||||
msg = _('No user found with this email.')
|
||||
elif self.alias_type == 'mobile':
|
||||
msg = _('No user found with this mobile number.')
|
||||
if self.alias_type == "email":
|
||||
msg = _("No user found with this email.")
|
||||
elif self.alias_type == "mobile":
|
||||
msg = _("No user found with this mobile number.")
|
||||
raise serializers.ValidationError(msg)
|
||||
else:
|
||||
# If new aliases should not register new users.
|
||||
@ -63,23 +67,23 @@ class AbstractBaseAliasAuthenticationSerializer(serializers.Serializer):
|
||||
if user:
|
||||
if not user.is_active:
|
||||
# If valid, return attrs so we can create a token in our logic controller
|
||||
msg = _('User account is disabled.')
|
||||
msg = _("User account is disabled.")
|
||||
raise serializers.ValidationError(msg)
|
||||
else:
|
||||
msg = _('No account is associated with this alias.')
|
||||
msg = _("No account is associated with this alias.")
|
||||
raise serializers.ValidationError(msg)
|
||||
else:
|
||||
msg = _('Missing %s.') % self.alias_type
|
||||
msg = _("Missing %s.") % self.alias_type
|
||||
raise serializers.ValidationError(msg)
|
||||
|
||||
attrs['user'] = user
|
||||
attrs["user"] = user
|
||||
return attrs
|
||||
|
||||
|
||||
class EmailAuthSerializer(AbstractBaseAliasAuthenticationSerializer):
|
||||
@property
|
||||
def alias_type(self):
|
||||
return 'email'
|
||||
return "email"
|
||||
|
||||
email = serializers.EmailField()
|
||||
|
||||
@ -87,11 +91,13 @@ class EmailAuthSerializer(AbstractBaseAliasAuthenticationSerializer):
|
||||
class MobileAuthSerializer(AbstractBaseAliasAuthenticationSerializer):
|
||||
@property
|
||||
def alias_type(self):
|
||||
return 'mobile'
|
||||
return "mobile"
|
||||
|
||||
phone_regex = RegexValidator(regex=r'^[7|9][0-9]{6}$',
|
||||
message="Mobile number must be entered in the format:"
|
||||
" '7xxxxxx' or '9xxxxxx'.")
|
||||
phone_regex = RegexValidator(
|
||||
regex=r"^[7|9][0-9]{6}$",
|
||||
message="Mobile number must be entered in the format:"
|
||||
" '7xxxxxx' or '9xxxxxx'.",
|
||||
)
|
||||
mobile = serializers.CharField(validators=[phone_regex], max_length=15)
|
||||
|
||||
|
||||
@ -105,14 +111,14 @@ class AbstractBaseAliasVerificationSerializer(serializers.Serializer):
|
||||
Abstract class that returns a callback token based on the field given
|
||||
Returns a token if valid, None or a message if not.
|
||||
"""
|
||||
|
||||
@property
|
||||
def alias_type(self):
|
||||
# The alias type, either email or mobile
|
||||
raise NotImplementedError
|
||||
|
||||
def validate(self, attrs):
|
||||
|
||||
msg = _('There was a problem with your request.')
|
||||
msg = _("There was a problem with your request.")
|
||||
|
||||
if self.alias_type:
|
||||
# Get request.user
|
||||
@ -125,31 +131,31 @@ class AbstractBaseAliasVerificationSerializer(serializers.Serializer):
|
||||
if user:
|
||||
if not user.is_active:
|
||||
# If valid, return attrs so we can create a token in our logic controller
|
||||
msg = _('User account is disabled.')
|
||||
msg = _("User account is disabled.")
|
||||
|
||||
else:
|
||||
if hasattr(user, self.alias_type):
|
||||
# Has the appropriate alias type
|
||||
attrs['user'] = user
|
||||
attrs["user"] = user
|
||||
return attrs
|
||||
else:
|
||||
msg = _('This user doesn\'t have an %s.' % self.alias_type)
|
||||
msg = _("This user doesn't have an %s." % self.alias_type)
|
||||
raise serializers.ValidationError(msg)
|
||||
else:
|
||||
msg = _('Missing %s.') % self.alias_type
|
||||
msg = _("Missing %s.") % self.alias_type
|
||||
raise serializers.ValidationError(msg)
|
||||
|
||||
|
||||
class EmailVerificationSerializer(AbstractBaseAliasVerificationSerializer):
|
||||
@property
|
||||
def alias_type(self):
|
||||
return 'email'
|
||||
return "email"
|
||||
|
||||
|
||||
class MobileVerificationSerializer(AbstractBaseAliasVerificationSerializer):
|
||||
@property
|
||||
def alias_type(self):
|
||||
return 'mobile'
|
||||
return "mobile"
|
||||
|
||||
|
||||
"""
|
||||
@ -173,13 +179,13 @@ class AbstractBaseCallbackTokenSerializer(serializers.Serializer):
|
||||
Abstract class inspired by DRF's own token serializer.
|
||||
Returns a user if valid, None or a message if not.
|
||||
"""
|
||||
|
||||
token = TokenField(min_length=6, max_length=6, validators=[token_age_validator])
|
||||
|
||||
|
||||
class CallbackTokenAuthSerializer(AbstractBaseCallbackTokenSerializer):
|
||||
|
||||
def validate(self, attrs):
|
||||
callback_token = attrs.get('token', None)
|
||||
callback_token = attrs.get("token", None)
|
||||
|
||||
token = CallbackToken.objects.get(key=callback_token, is_active=True)
|
||||
|
||||
@ -189,27 +195,29 @@ class CallbackTokenAuthSerializer(AbstractBaseCallbackTokenSerializer):
|
||||
user = authenticate_by_token(token)
|
||||
if user:
|
||||
if not user.is_active:
|
||||
msg = _('User account is disabled.')
|
||||
msg = _("User account is disabled.")
|
||||
raise serializers.ValidationError(msg)
|
||||
|
||||
if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED \
|
||||
or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED:
|
||||
if (
|
||||
api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED
|
||||
or api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED
|
||||
):
|
||||
# Mark this alias as verified
|
||||
user = User.objects.get(pk=token.user.pk)
|
||||
success = verify_user_alias(user, token)
|
||||
|
||||
if success is False:
|
||||
msg = _('Error validating user alias.')
|
||||
msg = _("Error validating user alias.")
|
||||
raise serializers.ValidationError(msg)
|
||||
|
||||
attrs['user'] = user
|
||||
attrs["user"] = user
|
||||
return attrs
|
||||
|
||||
else:
|
||||
msg = _('Invalid Token')
|
||||
msg = _("Invalid Token")
|
||||
raise serializers.ValidationError(msg)
|
||||
else:
|
||||
msg = _('Missing authentication token.')
|
||||
msg = _("Missing authentication token.")
|
||||
raise serializers.ValidationError(msg)
|
||||
|
||||
|
||||
@ -222,7 +230,7 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer):
|
||||
def validate(self, attrs):
|
||||
try:
|
||||
user_id = self.context.get("user_id")
|
||||
callback_token = attrs.get('token', None)
|
||||
callback_token = attrs.get("token", None)
|
||||
|
||||
token = CallbackToken.objects.get(key=callback_token, is_active=True)
|
||||
user = User.objects.get(pk=user_id)
|
||||
@ -235,23 +243,31 @@ class CallbackTokenVerificationSerializer(AbstractBaseCallbackTokenSerializer):
|
||||
if success is False:
|
||||
logger.debug("djangopasswordlessknox: Error verifying alias.")
|
||||
|
||||
attrs['user'] = user
|
||||
attrs["user"] = user
|
||||
return attrs
|
||||
else:
|
||||
msg = _('This token is invalid. Try again later.')
|
||||
logger.debug("djangopasswordlessknox: User token mismatch when verifying alias.")
|
||||
msg = _("This token is invalid. Try again later.")
|
||||
logger.debug(
|
||||
"djangopasswordlessknox: User token mismatch when verifying alias."
|
||||
)
|
||||
|
||||
except CallbackToken.DoesNotExist:
|
||||
msg = _('Missing authentication token.')
|
||||
logger.debug("djangopasswordlessknox: Tried to validate alias with bad token.")
|
||||
msg = _("Missing authentication token.")
|
||||
logger.debug(
|
||||
"djangopasswordlessknox: Tried to validate alias with bad token."
|
||||
)
|
||||
pass
|
||||
except User.DoesNotExist:
|
||||
msg = _('Missing user.')
|
||||
logger.debug("djangopasswordlessknox: Tried to validate alias with bad user.")
|
||||
msg = _("Missing user.")
|
||||
logger.debug(
|
||||
"djangopasswordlessknox: Tried to validate alias with bad user."
|
||||
)
|
||||
pass
|
||||
except PermissionDenied:
|
||||
msg = _('Insufficient permissions.')
|
||||
logger.debug("djangopasswordlessknox: Permission denied while validating alias.")
|
||||
msg = _("Insufficient permissions.")
|
||||
logger.debug(
|
||||
"djangopasswordlessknox: Permission denied while validating alias."
|
||||
)
|
||||
pass
|
||||
|
||||
raise serializers.ValidationError(msg)
|
||||
|
Reference in New Issue
Block a user