136 lines
4.7 KiB
Python
136 lines
4.7 KiB
Python
from django.contrib.auth import get_user_model
|
||
from django.contrib.auth.password_validation import validate_password
|
||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||
from django.db import transaction
|
||
from django.utils import timezone
|
||
from rest_framework import serializers
|
||
|
||
from .models import Invitation
|
||
from .permissions import default_permissions, effective_permissions, management_permissions
|
||
|
||
|
||
User = get_user_model()
|
||
|
||
|
||
class UserSerializer(serializers.ModelSerializer):
|
||
is_admin = serializers.SerializerMethodField()
|
||
permissions = serializers.SerializerMethodField()
|
||
management_permissions = serializers.SerializerMethodField()
|
||
color = serializers.RegexField(r'^#[0-9a-fA-F]{6}$', required=False)
|
||
|
||
class Meta:
|
||
model = User
|
||
fields = [
|
||
'id',
|
||
'username',
|
||
'first_name',
|
||
'last_name',
|
||
'email',
|
||
'is_active',
|
||
'is_staff',
|
||
'is_superuser',
|
||
'is_admin',
|
||
'permissions',
|
||
'management_permissions',
|
||
'date_joined',
|
||
'color',
|
||
]
|
||
read_only_fields = ['id', 'date_joined', 'is_admin']
|
||
|
||
def get_is_admin(self, obj):
|
||
return obj.is_superuser or obj.is_staff
|
||
|
||
def get_permissions(self, obj):
|
||
if not hasattr(self, '_permission_defaults'):
|
||
self._permission_defaults = default_permissions()
|
||
return effective_permissions(obj, self._permission_defaults)
|
||
|
||
def get_management_permissions(self, obj):
|
||
return management_permissions(obj)
|
||
|
||
def validate(self, attrs):
|
||
actor = self.context['request'].user
|
||
if not actor.is_superuser:
|
||
if self.instance.is_superuser:
|
||
raise serializers.ValidationError('Seul un superutilisateur peut modifier ce compte.')
|
||
if attrs.get('is_superuser', False):
|
||
raise serializers.ValidationError({'is_superuser': 'Seul un superutilisateur peut accorder ce rôle.'})
|
||
if self.instance.pk == actor.pk:
|
||
if not attrs.get('is_active', self.instance.is_active):
|
||
raise serializers.ValidationError({'is_active': 'Vous ne pouvez pas désactiver votre propre compte.'})
|
||
if not (attrs.get('is_staff', self.instance.is_staff) or attrs.get('is_superuser', self.instance.is_superuser)):
|
||
raise serializers.ValidationError('Vous ne pouvez pas retirer votre propre accès administrateur.')
|
||
return attrs
|
||
|
||
|
||
class RegisterSerializer(serializers.ModelSerializer):
|
||
password = serializers.CharField(write_only=True)
|
||
password_confirm = serializers.CharField(write_only=True)
|
||
invite = serializers.UUIDField(write_only=True)
|
||
|
||
class Meta:
|
||
model = User
|
||
fields = [
|
||
'username',
|
||
'password',
|
||
'password_confirm',
|
||
'first_name',
|
||
'last_name',
|
||
'invite',
|
||
]
|
||
|
||
def validate(self, attrs):
|
||
if attrs['password'] != attrs['password_confirm']:
|
||
raise serializers.ValidationError({
|
||
'password_confirm': (
|
||
'Les mots de passe ne correspondent pas.'
|
||
)
|
||
})
|
||
|
||
candidate = User(**{field: attrs.get(field, '') for field in ('username', 'first_name', 'last_name')})
|
||
try:
|
||
validate_password(attrs['password'], candidate)
|
||
except DjangoValidationError as error:
|
||
raise serializers.ValidationError({'password': error.messages}) from error
|
||
return attrs
|
||
|
||
def validate_invite(self, value):
|
||
try:
|
||
invitation = Invitation.objects.get(token=value)
|
||
except Invitation.DoesNotExist as error:
|
||
raise serializers.ValidationError(
|
||
'Ce lien d’invitation est invalide.'
|
||
) from error
|
||
|
||
if not invitation.is_valid:
|
||
raise serializers.ValidationError(
|
||
'Ce lien d’invitation est expiré ou déjà utilisé.'
|
||
)
|
||
|
||
return value
|
||
|
||
def create(self, validated_data):
|
||
invitation_token = validated_data.pop('invite')
|
||
validated_data.pop('password_confirm')
|
||
|
||
with transaction.atomic():
|
||
invitation = (
|
||
Invitation.objects
|
||
.select_for_update()
|
||
.get(token=invitation_token)
|
||
)
|
||
|
||
if not invitation.is_valid:
|
||
raise serializers.ValidationError({
|
||
'invite': (
|
||
'Ce lien d’invitation est expiré ou déjà utilisé.'
|
||
)
|
||
})
|
||
|
||
user = User.objects.create_user(**validated_data)
|
||
|
||
invitation.used_at = timezone.now()
|
||
invitation.save(update_fields=['used_at'])
|
||
|
||
return user
|