diff --git a/taiga/auth/services.py b/taiga/auth/services.py index 5ddda3f1..72589eac 100644 --- a/taiga/auth/services.py +++ b/taiga/auth/services.py @@ -31,7 +31,7 @@ from django.utils.translation import ugettext as _ from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail from taiga.base import exceptions as exc -from taiga.users.serializers import UserSerializer +from taiga.users.serializers import UserAdminSerializer from taiga.users.services import get_and_validate_user from .tokens import get_token_for_user @@ -186,7 +186,7 @@ def make_auth_response_data(user) -> dict: using python dict containing a representation of the logged user. """ - serializer = UserSerializer(user) + serializer = UserAdminSerializer(user) data = dict(serializer.data) data["auth_token"] = get_token_for_user(user, "authentication") return data diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 2cb34264..04a0ee86 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -390,11 +390,22 @@ class ProjectTemplateViewSet(ModelCrudViewSet): class MembershipViewSet(ModelCrudViewSet): model = models.Membership + admin_serializer_class = serializers.MembershipAdminSerializer serializer_class = serializers.MembershipSerializer permission_classes = (permissions.MembershipPermission,) filter_backends = (filters.CanViewProjectFilterBackend,) filter_fields = ("project", "role") + def get_serializer_class(self): + project_id = self.request.QUERY_PARAMS.get("project", None) + if project_id is None: + return self.serializer_class + + project = get_object_or_404(models.Project, pk=project_id) + if permissions_service.is_project_owner(self.request.user, project): + return self.admin_serializer_class + return self.serializer_class + @list_route(methods=["POST"]) def bulk_create(self, request, **kwargs): serializer = serializers.MembersBulkSerializer(data=request.DATA) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index b8634380..5d9d3a4d 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -168,8 +168,10 @@ class MembershipSerializer(ModelSerializer): class Meta: model = models.Membership + # IMPORTANT: Maintain the MembershipAdminSerializer Meta up to date + # with this info (excluding here user_email and email) read_only_fields = ("user",) - exclude = ("token",) + exclude = ("token", "user_email", "email") def get_photo(self, project): return get_photo_or_gravatar_url(project.user) @@ -227,6 +229,15 @@ class MembershipSerializer(ModelSerializer): return attrs +class MembershipAdminSerializer(MembershipSerializer): + class Meta: + model = models.Membership + # IMPORTANT: Maintain the MembershipSerializer Meta up to date + # with this info (excluding there user_email and email) + read_only_fields = ("user",) + exclude = ("token",) + + class ProjectMembershipSerializer(ModelSerializer): role_name = serializers.CharField(source='role.name', required=False) full_name = serializers.CharField(source='user.get_full_name', required=False) diff --git a/taiga/users/api.py b/taiga/users/api.py index b582ac20..93f79be3 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -49,10 +49,18 @@ from .signals import user_cancel_account as user_cancel_account_signal class UsersViewSet(ModelCrudViewSet): permission_classes = (permissions.UserPermission,) + admin_serializer_class = serializers.UserAdminSerializer serializer_class = serializers.UserSerializer queryset = models.User.objects.all() filter_backends = (MembersFilterBackend,) + def get_serializer_class(self): + if self.action in ["partial_update", "update", "retrieve"]: + user = self.get_object() + if self.request.user == user: + return self.admin_serializer_class + return self.serializer_class + def create(self, *args, **kwargs): raise exc.NotSupported() @@ -165,7 +173,7 @@ class UsersViewSet(ModelCrudViewSet): request.user.photo = avatar request.user.save(update_fields=["photo"]) - user_data = serializers.UserSerializer(request.user).data + user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) @@ -177,7 +185,7 @@ class UsersViewSet(ModelCrudViewSet): self.check_permissions(request, "remove_avatar", None) request.user.photo = None request.user.save(update_fields=["photo"]) - user_data = serializers.UserSerializer(request.user).data + user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) @detail_route(methods=["GET"]) @@ -258,7 +266,7 @@ class UsersViewSet(ModelCrudViewSet): Get me. """ self.check_permissions(request, "me", None) - user_data = serializers.UserSerializer(request.user).data + user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) @list_route(methods=["POST"]) diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index acd42741..5bb0d1ff 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -40,10 +40,12 @@ class UserSerializer(ModelSerializer): class Meta: model = User - fields = ("id", "username", "full_name", "full_name_display", "email", + # IMPORTANT: Maintain the UserAdminSerializer Meta up to date + # with this info (including there the email) + fields = ("id", "username", "full_name", "full_name_display", "color", "bio", "default_language", "default_timezone", "is_active", "photo", "big_photo") - read_only_fields = ("id", "email") + read_only_fields = ("id",) def validate_username(self, attrs, source): value = attrs[source] @@ -73,6 +75,17 @@ class UserSerializer(ModelSerializer): return get_big_photo_or_gravatar_url(user) +class UserAdminSerializer(UserSerializer): + class Meta: + model = User + # IMPORTANT: Maintain the UserSerializer Meta up to date + # with this info (including here the email) + fields = ("id", "username", "full_name", "full_name_display", "email", + "color", "bio", "default_language", + "default_timezone", "is_active", "photo", "big_photo") + read_only_fields = ("id", "email") + + class RecoverySerializer(Serializer): token = serializers.CharField(max_length=200) password = serializers.CharField(min_length=6)