models.py 4.17 KB
Newer Older
1 2
from typing import List

3
from django.contrib.auth.models import AbstractUser
4 5
from django.core.exceptions import ValidationError
from django.db import models
6
from django.utils.functional import cached_property
7 8 9 10 11 12 13 14 15 16 17 18 19
from rest_framework.response import Response

from backend_app.models.abstract.base import BaseModelSerializer
from backend_app.models.abstract.base import BaseModelViewSet
from backend_app.permissions.app_permissions import IsOwner, ReadOnly
from backend_app.utils import get_user_level, OBJ_MODERATION_PERMISSIONS


def validate(user, allow_sharing_personal_info):
    """
    Custom validation to ensure that moderators, DRI and staff can't be "anonymous" on the plateform
    """
    if (
20 21
        get_user_level(user) >= OBJ_MODERATION_PERMISSIONS["moderator"]
        and not allow_sharing_personal_info
22 23 24 25 26 27
    ):
        raise ValidationError(
            {
                "allow_sharing_personal_info": "Users that are moderators, members of DRI or staff, must allow sharing of their 'identity', sorry."
            }
        )
28 29 30


class User(AbstractUser):
31 32 33 34
    """
    If you modify this model, don't forget to modify the command that handles its emptying for RGPD
    """

35 36 37 38 39 40
    @cached_property
    def cached_groups(self) -> List[str]:
        out = ["authenticated_user"]
        for group in self.groups.all():
            out.append(group.name)
        return out
41 42 43 44 45 46

    allow_sharing_personal_info = models.BooleanField(default=True, null=False)
    secondary_email = models.EmailField(null=True, blank=True)
    pseudo = models.CharField(
        blank=False, null=False, max_length=12, default="Anonymous42"
    )
47
    has_validated_cgu_rgpd = models.BooleanField(default=False, null=False)
48
    is_banned = models.BooleanField(default=False, null=False)
49

50 51 52 53 54 55
    # Handling of account deletion
    delete_next_time = models.BooleanField(
        default=False, null=False
    )  # if true, the account will be deleted at midnight
    is_deleted = models.BooleanField(default=False, null=False)

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
    def save(self, *args, **kwargs):
        """
        Custom save function to ensure consistency.
        """

        # if the object is not created yet, we can't check to what groups it belongs
        if self.pk is not None:
            validate(self, self.allow_sharing_personal_info)
        return super().save(*args, **kwargs)


class UserSerializer(BaseModelSerializer):
    def validate(self, attrs):
        """
        Also validate at the serializer level to prevent error 500
        """
        data = super().validate(attrs)
        aspi = data["allow_sharing_personal_info"]
        validate(self.get_user_from_request(), aspi)
        return data

    class Meta:
        model = User
        fields = BaseModelSerializer.Meta.fields + (
            "username",
            "first_name",
            "last_name",
            "email",
            "allow_sharing_personal_info",
            "secondary_email",
            "pseudo",
87
            "delete_next_time",
88 89
            "is_staff",
        )
90
        read_only_fields = ("username", "first_name", "last_name", "email")
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124


class UserViewset(BaseModelViewSet):
    def list(self, request, *args, **kwargs):
        # Prevent the querying of all objects.
        return Response(list())

    def retrieve(self, request, *args, **kwargs):
        # Custom behavior to return only the correct set of attributes depending
        # On allow_sharing_personal_info
        instance = self.get_object()
        serializer = self.get_serializer(instance)

        if instance == self.request.user or self.request.user.is_staff:
            out = serializer.data
        else:
            # serializer.data is a property we can't set it again, so we need a little trick for the output
            # First we copy all the values
            out = dict(serializer.data)
            # Then we "correct" them
            for key in [
                "username",
                "first_name",
                "last_name",
                "email",
                "secondary_email",
            ]:
                out[key] = None
        return Response(out)

    queryset = User.objects.all()
    permission_classes = (IsOwner | ReadOnly,)
    serializer_class = UserSerializer
    end_point_route = "users"