From b8a41ad5a04b0c10fd6eabaa61a19d1ec2827f2e Mon Sep 17 00:00:00 2001 From: Sangeetha Date: Wed, 3 Dec 2025 16:06:46 +0530 Subject: [PATCH] [WEB-5560] fix: restrict guest users to view all details of a workspace members (#8215) * fix: separate retrieve method in WorkspaceMemberViewSet * fix: non project members accessing member detail: * chore: error handle * fix: role based response * fix: use Enum --- apps/api/plane/app/views/project/member.py | 34 ++++++++++++++++++++ apps/api/plane/app/views/workspace/member.py | 19 +++++++++++ 2 files changed, 53 insertions(+) diff --git a/apps/api/plane/app/views/project/member.py b/apps/api/plane/app/views/project/member.py index 69d45226ce..3ab7061e15 100644 --- a/apps/api/plane/app/views/project/member.py +++ b/apps/api/plane/app/views/project/member.py @@ -164,6 +164,40 @@ class ProjectMemberViewSet(BaseViewSet): serializer = ProjectMemberRoleSerializer(project_members, fields=("id", "member", "role"), many=True) return Response(serializer.data, status=status.HTTP_200_OK) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) + def retrieve(self, request, slug, project_id, pk): + requesting_project_member = ProjectMember.objects.get( + project_id=project_id, + workspace__slug=slug, + member=request.user, + is_active=True, + ) + + project_member = ( + ProjectMember.objects.filter( + pk=pk, + project_id=project_id, + workspace__slug=slug, + member__is_bot=False, + is_active=True, + ) + .select_related("project", "member", "workspace") + .first() + ) + + if not project_member: + return Response( + {"error": "Project member not found"}, + status=status.HTTP_404_NOT_FOUND, + ) + + if requesting_project_member.role > ROLE.GUEST.value: + serializer = ProjectMemberAdminSerializer(project_member) + else: + serializer = ProjectMemberRoleSerializer(project_member, fields=("id", "member", "role")) + + return Response(serializer.data, status=status.HTTP_200_OK) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def partial_update(self, request, slug, project_id, pk): project_member = ProjectMember.objects.get(pk=pk, workspace__slug=slug, project_id=project_id, is_active=True) diff --git a/apps/api/plane/app/views/workspace/member.py b/apps/api/plane/app/views/workspace/member.py index d81a647f69..3394cb253f 100644 --- a/apps/api/plane/app/views/workspace/member.py +++ b/apps/api/plane/app/views/workspace/member.py @@ -50,6 +50,25 @@ class WorkSpaceMemberViewSet(BaseViewSet): serializer = WorkSpaceMemberSerializer(workspace_members, fields=("id", "member", "role"), many=True) return Response(serializer.data, status=status.HTTP_200_OK) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE") + def retrieve(self, request, slug, pk): + workspace_member = WorkspaceMember.objects.get(member=request.user, workspace__slug=slug, is_active=True) + + try: + # Get the specific workspace member by pk + member = self.get_queryset().get(pk=pk) + except WorkspaceMember.DoesNotExist: + return Response( + {"error": "Workspace member not found"}, + status=status.HTTP_404_NOT_FOUND, + ) + + if workspace_member.role > ROLE.GUEST.value: + serializer = WorkspaceMemberAdminSerializer(member, fields=("id", "member", "role")) + else: + serializer = WorkSpaceMemberSerializer(member, fields=("id", "member", "role")) + return Response(serializer.data, status=status.HTTP_200_OK) + @allow_permission(allowed_roles=[ROLE.ADMIN], level="WORKSPACE") def partial_update(self, request, slug, pk): workspace_member = WorkspaceMember.objects.get(