i701 83db42cc60
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 4m12s
Refactor and enhance device management and authentication features
- 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.
2025-04-25 14:37:27 +05:00

157 lines
6.8 KiB
Python

import logging
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from django.db.models import signals
from djangopasswordlessknox.models import CallbackToken
from djangopasswordlessknox.models import generate_numeric_token
from djangopasswordlessknox.settings import api_settings
from djangopasswordlessknox.services import TokenService
logger = logging.getLogger(__name__)
@receiver(signals.pre_save, sender=CallbackToken)
def invalidate_previous_tokens(sender, instance, **kwargs):
"""
Invalidates all previously issued tokens as a post_save signal.
"""
active_tokens = None
if isinstance(instance, CallbackToken):
active_tokens = (
CallbackToken.objects.active()
.filter(user=instance.user)
.exclude(id=instance.id)
)
# Invalidate tokens
if active_tokens:
for token in active_tokens:
token.is_active = False
token.save()
@receiver(signals.pre_save, sender=CallbackToken)
def check_unique_tokens(sender, instance, **kwargs):
"""
Ensures that mobile and email tokens are unique or tries once more to generate.
"""
if isinstance(instance, CallbackToken):
if CallbackToken.objects.filter(key=instance.key, is_active=True).exists():
instance.key = generate_numeric_token()
User = get_user_model()
@receiver(signals.pre_save, sender=User)
def update_alias_verification(sender, instance, **kwargs):
"""
Flags a user's email as unverified if they change it.
Optionally sends a verification token to the new endpoint.
"""
if isinstance(instance, User):
if instance.id:
if api_settings.PASSWORDLESS_USER_MARK_EMAIL_VERIFIED is True:
"""
For marking email aliases as not verified when a user changes it.
"""
email_field = api_settings.PASSWORDLESS_USER_EMAIL_FIELD_NAME
email_verified_field = (
api_settings.PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME
)
# Verify that this is an existing instance and not a new one.
try:
user_old = User.objects.get(id=instance.id) # Pre-save object
instance_email = getattr(instance, email_field) # Incoming Email
old_email = getattr(user_old, email_field) # Pre-save object email
if (
instance_email != old_email
and instance_email != ""
and instance_email is not None
):
# Email changed, verification should be flagged
setattr(instance, email_verified_field, False)
if (
api_settings.PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN
is True
):
email_subject = (
api_settings.PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT
)
email_plaintext = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE
email_html = api_settings.PASSWORDLESS_EMAIL_VERIFICATION_TOKEN_HTML_TEMPLATE_NAME
message_payload = {
"email_subject": email_subject,
"email_plaintext": email_plaintext,
"email_html": email_html,
}
success = TokenService.send_token(
instance, "email", **message_payload
)
if success:
logger.info(
"djangopasswordlessknox: Successfully sent email on updated address: %s"
% instance_email
)
else:
logger.info(
"djangopasswordlessknox: Failed to send email to updated address: %s"
% instance_email
)
except User.DoesNotExist:
# User probably is just initially being created
setattr(instance, email_verified_field, True)
if api_settings.PASSWORDLESS_USER_MARK_MOBILE_VERIFIED is True:
"""
For marking mobile aliases as not verified when a user changes it.
"""
mobile_field = api_settings.PASSWORDLESS_USER_MOBILE_FIELD_NAME
mobile_verified_field = (
api_settings.PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME
)
# Verify that this is an existing instance and not a new one.
try:
user_old = User.objects.get(id=instance.id) # Pre-save object
instance_mobile = getattr(instance, mobile_field) # Incoming mobile
old_mobile = getattr(
user_old, mobile_field
) # Pre-save object mobile
if (
instance_mobile != old_mobile
and instance_mobile != ""
and instance_mobile is not None
):
# Mobile changed, verification should be flagged
setattr(instance, mobile_verified_field, False)
if (
api_settings.PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN
is True
):
mobile_message = api_settings.PASSWORDLESS_MOBILE_MESSAGE
message_payload = {"mobile_message": mobile_message}
success = TokenService.send_token(
instance, "mobile", **message_payload
)
if success:
logger.info(
"djangopasswordlessknox: Successfully sent SMS on updated mobile: %s"
% instance_mobile
)
else:
logger.info(
"djangopasswordlessknox: Failed to send SMS to updated mobile: %s"
% instance_mobile
)
except User.DoesNotExist:
# User probably is just initially being created
setattr(instance, mobile_verified_field, True)