218 lines
7.5 KiB
Python
218 lines
7.5 KiB
Python
"""
|
|
Accounts API serializers.
|
|
"""
|
|
|
|
from rest_framework import serializers
|
|
from django.contrib.auth import authenticate
|
|
from ..models import User, TwoFactorDevice, SocialAccount, UserSession, PasswordHistory
|
|
|
|
|
|
class UserSerializer(serializers.ModelSerializer):
|
|
"""
|
|
User serializer.
|
|
"""
|
|
tenant_name = serializers.CharField(source='tenant.name', read_only=True)
|
|
full_name = serializers.CharField(source='get_full_name', read_only=True)
|
|
display_name = serializers.CharField(source='get_display_name', read_only=True)
|
|
is_account_locked = serializers.ReadOnlyField()
|
|
is_password_expired = serializers.ReadOnlyField()
|
|
is_license_expired = serializers.ReadOnlyField()
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
'id', 'user_id', 'username', 'email', 'first_name', 'last_name',
|
|
'middle_name', 'preferred_name', 'full_name', 'display_name',
|
|
'phone_number', 'mobile_number', 'tenant', 'tenant_name',
|
|
'employee_id', 'department', 'job_title', 'role',
|
|
'license_number', 'license_state', 'license_expiry',
|
|
'dea_number', 'npi_number', 'timezone', 'language', 'theme',
|
|
'profile_picture', 'bio', 'is_active', 'is_verified', 'is_approved',
|
|
'approval_date', 'two_factor_enabled', 'max_concurrent_sessions',
|
|
'session_timeout_minutes', 'is_account_locked', 'is_password_expired',
|
|
'is_license_expired', 'last_login', 'date_joined', 'created_at', 'updated_at'
|
|
]
|
|
read_only_fields = [
|
|
'user_id', 'last_login', 'date_joined', 'created_at', 'updated_at'
|
|
]
|
|
extra_kwargs = {
|
|
'password': {'write_only': True}
|
|
}
|
|
|
|
def create(self, validated_data):
|
|
password = validated_data.pop('password', None)
|
|
user = User.objects.create_user(**validated_data)
|
|
if password:
|
|
user.set_password(password)
|
|
user.save()
|
|
return user
|
|
|
|
def update(self, instance, validated_data):
|
|
password = validated_data.pop('password', None)
|
|
user = super().update(instance, validated_data)
|
|
if password:
|
|
user.set_password(password)
|
|
user.save()
|
|
return user
|
|
|
|
|
|
class UserProfileSerializer(serializers.ModelSerializer):
|
|
"""
|
|
User profile serializer for self-service updates.
|
|
"""
|
|
tenant_name = serializers.CharField(source='tenant.name', read_only=True)
|
|
full_name = serializers.CharField(source='get_full_name', read_only=True)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
'id', 'username', 'email', 'first_name', 'last_name',
|
|
'middle_name', 'preferred_name', 'full_name',
|
|
'phone_number', 'mobile_number', 'tenant_name',
|
|
'timezone', 'language', 'theme', 'profile_picture', 'bio'
|
|
]
|
|
read_only_fields = ['id', 'username', 'tenant_name']
|
|
|
|
|
|
class TwoFactorDeviceSerializer(serializers.ModelSerializer):
|
|
"""
|
|
Two-factor device serializer.
|
|
"""
|
|
user_name = serializers.CharField(source='user.get_display_name', read_only=True)
|
|
|
|
class Meta:
|
|
model = TwoFactorDevice
|
|
fields = [
|
|
'device_id', 'user', 'user_name', 'name', 'device_type',
|
|
'phone_number', 'email_address', 'is_active', 'is_verified',
|
|
'verified_at', 'last_used_at', 'usage_count',
|
|
'created_at', 'updated_at'
|
|
]
|
|
read_only_fields = ['device_id', 'created_at', 'updated_at']
|
|
extra_kwargs = {
|
|
'secret_key': {'write_only': True}
|
|
}
|
|
|
|
|
|
class SocialAccountSerializer(serializers.ModelSerializer):
|
|
"""
|
|
Social account serializer.
|
|
"""
|
|
user_name = serializers.CharField(source='user.get_display_name', read_only=True)
|
|
|
|
class Meta:
|
|
model = SocialAccount
|
|
fields = [
|
|
'id', 'user', 'user_name', 'provider', 'provider_id',
|
|
'provider_email', 'display_name', 'profile_url', 'avatar_url',
|
|
'is_active', 'created_at', 'updated_at', 'last_login_at'
|
|
]
|
|
read_only_fields = ['created_at', 'updated_at']
|
|
extra_kwargs = {
|
|
'access_token': {'write_only': True},
|
|
'refresh_token': {'write_only': True}
|
|
}
|
|
|
|
|
|
class UserSessionSerializer(serializers.ModelSerializer):
|
|
"""
|
|
User session serializer.
|
|
"""
|
|
user_name = serializers.CharField(source='user.get_display_name', read_only=True)
|
|
is_expired = serializers.ReadOnlyField()
|
|
|
|
class Meta:
|
|
model = UserSession
|
|
fields = [
|
|
'session_id', 'user', 'user_name', 'session_key',
|
|
'ip_address', 'user_agent', 'device_type', 'browser',
|
|
'operating_system', 'country', 'region', 'city',
|
|
'is_active', 'login_method', 'is_expired',
|
|
'created_at', 'last_activity_at', 'expires_at', 'ended_at'
|
|
]
|
|
read_only_fields = ['session_id', 'created_at', 'last_activity_at']
|
|
|
|
|
|
class PasswordHistorySerializer(serializers.ModelSerializer):
|
|
"""
|
|
Password history serializer.
|
|
"""
|
|
user_name = serializers.CharField(source='user.get_display_name', read_only=True)
|
|
|
|
class Meta:
|
|
model = PasswordHistory
|
|
fields = ['id', 'user', 'user_name', 'created_at']
|
|
read_only_fields = ['created_at']
|
|
extra_kwargs = {
|
|
'password_hash': {'write_only': True}
|
|
}
|
|
|
|
|
|
class LoginSerializer(serializers.Serializer):
|
|
"""
|
|
Login serializer.
|
|
"""
|
|
username = serializers.CharField()
|
|
password = serializers.CharField(write_only=True)
|
|
remember_me = serializers.BooleanField(default=False)
|
|
|
|
def validate(self, attrs):
|
|
username = attrs.get('username')
|
|
password = attrs.get('password')
|
|
|
|
if username and password:
|
|
user = authenticate(username=username, password=password)
|
|
if not user:
|
|
raise serializers.ValidationError('Invalid credentials')
|
|
if not user.is_active:
|
|
raise serializers.ValidationError('User account is disabled')
|
|
if user.is_account_locked:
|
|
raise serializers.ValidationError('User account is locked')
|
|
|
|
attrs['user'] = user
|
|
else:
|
|
raise serializers.ValidationError('Must include username and password')
|
|
|
|
return attrs
|
|
|
|
|
|
class PasswordChangeSerializer(serializers.Serializer):
|
|
"""
|
|
Password change serializer.
|
|
"""
|
|
old_password = serializers.CharField(write_only=True)
|
|
new_password = serializers.CharField(write_only=True)
|
|
confirm_password = serializers.CharField(write_only=True)
|
|
|
|
def validate_old_password(self, value):
|
|
user = self.context['request'].user
|
|
if not user.check_password(value):
|
|
raise serializers.ValidationError('Old password is incorrect')
|
|
return value
|
|
|
|
def validate(self, attrs):
|
|
if attrs['new_password'] != attrs['confirm_password']:
|
|
raise serializers.ValidationError('New passwords do not match')
|
|
return attrs
|
|
|
|
def save(self):
|
|
user = self.context['request'].user
|
|
user.set_password(self.validated_data['new_password'])
|
|
user.save()
|
|
return user
|
|
|
|
|
|
class PasswordResetSerializer(serializers.Serializer):
|
|
"""
|
|
Password reset serializer.
|
|
"""
|
|
email = serializers.EmailField()
|
|
|
|
def validate_email(self, value):
|
|
try:
|
|
user = User.objects.get(email=value, is_active=True)
|
|
except User.DoesNotExist:
|
|
raise serializers.ValidationError('No active user found with this email')
|
|
return value
|
|
|