278 lines
9.6 KiB
Python
278 lines
9.6 KiB
Python
"""
|
|
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'
|
|
]
|