""" Security-related serializers for API endpoints """ from rest_framework import serializers from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from ..models import ( DataClassification, Role, User, MFADevice, AuditLog, SSOProvider, AccessPolicy ) User = get_user_model() class DataClassificationSerializer(serializers.ModelSerializer): """Serializer for data classification levels""" class Meta: model = DataClassification fields = [ 'id', 'name', 'level', 'description', 'color_code', 'requires_clearance', 'created_at', 'updated_at' ] read_only_fields = ['id', 'created_at', 'updated_at'] class PermissionSerializer(serializers.ModelSerializer): """Serializer for permissions""" class Meta: model = Permission fields = ['id', 'name', 'codename', 'content_type'] class RoleSerializer(serializers.ModelSerializer): """Serializer for roles""" permissions = PermissionSerializer(many=True, read_only=True) permission_ids = serializers.ListField( child=serializers.IntegerField(), write_only=True, required=False ) data_classification_access = DataClassificationSerializer(many=True, read_only=True) classification_ids = serializers.ListField( child=serializers.IntegerField(), write_only=True, required=False ) class Meta: model = Role fields = [ 'id', 'name', 'description', 'permissions', 'permission_ids', 'data_classification_access', 'classification_ids', 'is_active', 'created_at', 'updated_at' ] read_only_fields = ['id', 'created_at', 'updated_at'] def create(self, validated_data): permission_ids = validated_data.pop('permission_ids', []) classification_ids = validated_data.pop('classification_ids', []) role = Role.objects.create(**validated_data) if permission_ids: role.permissions.set(permission_ids) if classification_ids: role.data_classification_access.set(classification_ids) return role def update(self, instance, validated_data): permission_ids = validated_data.pop('permission_ids', None) classification_ids = validated_data.pop('classification_ids', None) for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() if permission_ids is not None: instance.permissions.set(permission_ids) if classification_ids is not None: instance.data_classification_access.set(classification_ids) return instance class UserSerializer(serializers.ModelSerializer): """Serializer for users""" roles = RoleSerializer(many=True, read_only=True) role_ids = serializers.ListField( child=serializers.IntegerField(), write_only=True, required=False ) clearance_level = DataClassificationSerializer(read_only=True) clearance_level_id = serializers.IntegerField(write_only=True, required=False) mfa_enabled = serializers.BooleanField(read_only=True) is_account_locked = serializers.BooleanField(read_only=True) password = serializers.CharField(write_only=True, required=False) class Meta: model = User fields = [ 'id', 'username', 'email', 'first_name', 'last_name', 'employee_id', 'department', 'clearance_level', 'clearance_level_id', 'roles', 'role_ids', 'attributes', 'mfa_enabled', 'mfa_secret', 'last_login_ip', 'failed_login_attempts', 'is_account_locked', 'sso_provider', 'sso_identifier', 'is_active', 'is_staff', 'is_superuser', 'date_joined', 'last_login', 'created_at', 'updated_at', 'password' ] read_only_fields = [ 'id', 'mfa_enabled', 'mfa_secret', 'last_login_ip', 'failed_login_attempts', 'is_account_locked', 'sso_provider', 'sso_identifier', 'date_joined', 'last_login', 'created_at', 'updated_at' ] def create(self, validated_data): password = validated_data.pop('password', None) role_ids = validated_data.pop('role_ids', []) clearance_level_id = validated_data.pop('clearance_level_id', None) if clearance_level_id: validated_data['clearance_level_id'] = clearance_level_id user = User.objects.create_user(**validated_data) if password: user.set_password(password) user.save() if role_ids: user.roles.set(role_ids) return user def update(self, instance, validated_data): password = validated_data.pop('password', None) role_ids = validated_data.pop('role_ids', None) clearance_level_id = validated_data.pop('clearance_level_id', None) if clearance_level_id: validated_data['clearance_level_id'] = clearance_level_id for attr, value in validated_data.items(): setattr(instance, attr, value) if password: instance.set_password(password) instance.save() if role_ids is not None: instance.roles.set(role_ids) return instance class MFADeviceSerializer(serializers.ModelSerializer): """Serializer for MFA devices""" user = serializers.StringRelatedField(read_only=True) secret_key = serializers.CharField(read_only=True) # Never expose secret class Meta: model = MFADevice fields = [ 'id', 'user', 'device_type', 'name', 'is_active', 'is_primary', 'last_used', 'created_at', 'updated_at' ] read_only_fields = ['id', 'user', 'secret_key', 'created_at', 'updated_at'] class MFASetupSerializer(serializers.Serializer): """Serializer for MFA setup""" device_name = serializers.CharField(max_length=100) device_type = serializers.ChoiceField(choices=MFADevice.DEVICE_TYPES) def validate_device_name(self, value): """Validate device name is unique for user""" user = self.context['request'].user if MFADevice.objects.filter(user=user, name=value).exists(): raise serializers.ValidationError("Device name already exists") return value class MFAVerificationSerializer(serializers.Serializer): """Serializer for MFA verification""" token = serializers.CharField(max_length=10, min_length=6) device_id = serializers.UUIDField(required=False) class AuditLogSerializer(serializers.ModelSerializer): """Serializer for audit logs""" user = serializers.StringRelatedField(read_only=True) class Meta: model = AuditLog fields = [ 'id', 'timestamp', 'user', 'action_type', 'resource_type', 'resource_id', 'ip_address', 'user_agent', 'details', 'severity', 'hash_value' ] read_only_fields = ['id', 'timestamp', 'hash_value'] class SSOProviderSerializer(serializers.ModelSerializer): """Serializer for SSO providers""" configuration = serializers.JSONField() attribute_mapping = serializers.JSONField() class Meta: model = SSOProvider fields = [ 'id', 'name', 'provider_type', 'is_active', 'configuration', 'attribute_mapping', 'created_at', 'updated_at' ] read_only_fields = ['id', 'created_at', 'updated_at'] class AccessPolicySerializer(serializers.ModelSerializer): """Serializer for access policies""" conditions = serializers.JSONField() actions = serializers.JSONField() class Meta: model = AccessPolicy fields = [ 'id', 'name', 'description', 'policy_type', 'conditions', 'resource_type', 'actions', 'priority', 'is_active', 'created_at', 'updated_at' ] read_only_fields = ['id', 'created_at', 'updated_at'] class LoginSerializer(serializers.Serializer): """Serializer for user login""" username = serializers.CharField() password = serializers.CharField() mfa_token = serializers.CharField(required=False, allow_blank=True) remember_me = serializers.BooleanField(default=False) class PasswordChangeSerializer(serializers.Serializer): """Serializer for password change""" current_password = serializers.CharField() new_password = serializers.CharField(min_length=8) confirm_password = serializers.CharField() def validate(self, data): """Validate password change""" if data['new_password'] != data['confirm_password']: raise serializers.ValidationError("Passwords do not match") return data class UserProfileSerializer(serializers.ModelSerializer): """Serializer for user profile""" roles = RoleSerializer(many=True, read_only=True) clearance_level = DataClassificationSerializer(read_only=True) mfa_devices = MFADeviceSerializer(many=True, read_only=True) class Meta: model = User fields = [ 'id', 'username', 'email', 'first_name', 'last_name', 'employee_id', 'department', 'clearance_level', 'roles', 'mfa_enabled', 'mfa_devices', 'is_superuser', 'is_staff', 'is_active', 'date_joined', 'last_login', 'created_at', 'updated_at' ] read_only_fields = [ 'id', 'username', 'employee_id', 'clearance_level', 'roles', 'mfa_enabled', 'is_superuser', 'is_staff', 'is_active', 'date_joined', 'last_login', 'created_at', 'updated_at' ]