dev: rename is_deactivated to is_active and user deactivation apis

This commit is contained in:
pablohashescobar 2023-11-10 15:12:20 +05:30
parent a531334e76
commit 6947a0ea43
15 changed files with 124 additions and 76 deletions

View File

@ -21,7 +21,7 @@ class ProjectBasePermission(BasePermission):
return WorkspaceMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
is_deactivated=False,
is_active=True,
).exists()
## Only workspace owners or admins can create the projects
@ -30,7 +30,7 @@ class ProjectBasePermission(BasePermission):
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
is_deactivated=False,
is_active=True,
).exists()
## Only Project Admins can update project attributes
@ -39,7 +39,7 @@ class ProjectBasePermission(BasePermission):
member=request.user,
role=Admin,
project_id=view.project_id,
is_deactivated=False,
is_active=True,
).exists()
@ -53,7 +53,7 @@ class ProjectMemberPermission(BasePermission):
return ProjectMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
is_deactivated=False,
is_active=True,
).exists()
## Only workspace owners or admins can create the projects
if request.method == "POST":
@ -61,7 +61,7 @@ class ProjectMemberPermission(BasePermission):
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
is_deactivated=False,
is_active=True,
).exists()
## Only Project Admins can update project attributes
@ -70,7 +70,7 @@ class ProjectMemberPermission(BasePermission):
member=request.user,
role__in=[Admin, Member],
project_id=view.project_id,
is_deactivated=False,
is_active=True,
).exists()
@ -85,7 +85,7 @@ class ProjectEntityPermission(BasePermission):
workspace__slug=view.workspace_slug,
member=request.user,
project_id=view.project_id,
is_deactivated=False,
is_active=True,
).exists()
## Only project members or admins can create and edit the project attributes
@ -94,7 +94,7 @@ class ProjectEntityPermission(BasePermission):
member=request.user,
role__in=[Admin, Member],
project_id=view.project_id,
is_deactivated=False,
is_active=True,
).exists()
@ -107,5 +107,5 @@ class ProjectLitePermission(BasePermission):
workspace__slug=view.workspace_slug,
member=request.user,
project_id=view.project_id,
is_deactivated=False,
is_active=True,
).exists()

View File

@ -32,7 +32,7 @@ class WorkSpaceBasePermission(BasePermission):
member=request.user,
workspace__slug=view.workspace_slug,
role__in=[Owner, Admin],
is_deactivated=False,
is_active=True,
).exists()
# allow only owner to delete the workspace
@ -41,7 +41,7 @@ class WorkSpaceBasePermission(BasePermission):
member=request.user,
workspace__slug=view.workspace_slug,
role=Owner,
is_deactivated=False,
is_active=True,
).exists()
@ -54,7 +54,7 @@ class WorkSpaceAdminPermission(BasePermission):
member=request.user,
workspace__slug=view.workspace_slug,
role__in=[Owner, Admin],
is_deactivated=False,
is_active=True,
).exists()
@ -68,14 +68,14 @@ class WorkspaceEntityPermission(BasePermission):
return WorkspaceMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
is_deactivated=False,
is_active=True,
).exists()
return WorkspaceMember.objects.filter(
member=request.user,
workspace__slug=view.workspace_slug,
role__in=[Owner, Admin],
is_deactivated=False,
is_active=True,
).exists()
@ -88,5 +88,5 @@ class WorkspaceViewerPermission(BasePermission):
member=request.user,
workspace__slug=view.workspace_slug,
role__gte=10,
is_deactivated=False,
is_active=True,
).exists()

View File

@ -105,7 +105,7 @@ class ProjectListSerializer(DynamicBaseSerializer):
def get_members(self, obj):
project_members = ProjectMember.objects.filter(
project_id=obj.id,
is_deactivated=False,
is_active=True,
).values(
"id",
"member_id",

View File

@ -26,7 +26,11 @@ urlpatterns = [
path(
"users/me/",
UserEndpoint.as_view(
{"get": "retrieve", "patch": "partial_update", "delete": "destroy"}
{
"get": "retrieve",
"patch": "partial_update",
"delete": "deactivate",
}
),
name="users",
),

View File

@ -319,6 +319,13 @@ class MagicSignInEndpoint(BaseAPIView):
if str(token) == str(user_token):
if User.objects.filter(email=email).exists():
user = User.objects.get(email=email)
if not user.is_active:
return Response(
{
"error": "Your account has been deactivated. Please contact your site administrator."
},
status=status.HTTP_403_FORBIDDEN,
)
# Send event to Jitsu for tracking
if settings.ANALYTICS_BASE_API:
_ = requests.post(

View File

@ -215,7 +215,7 @@ class InboxIssueViewSet(BaseViewSet):
workspace__slug=slug,
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
)
# Only project members admins and created_by users can access this endpoint
if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(
@ -337,7 +337,7 @@ class InboxIssueViewSet(BaseViewSet):
workspace__slug=slug,
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
)
if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(

View File

@ -525,7 +525,7 @@ class IssueCommentViewSet(BaseViewSet):
workspace__slug=self.kwargs.get("slug"),
project_id=self.kwargs.get("project_id"),
member_id=self.request.user.id,
is_deactivated=False,
is_active=True,
)
)
)
@ -1160,7 +1160,7 @@ class IssueSubscriberViewSet(BaseViewSet):
ProjectMember.objects.filter(
workspace__slug=slug,
project_id=project_id,
is_deactivated=False,
is_active=True,
)
.annotate(
is_subscribed=Exists(
@ -1405,7 +1405,7 @@ class IssueCommentPublicViewSet(BaseViewSet):
workspace__slug=self.kwargs.get("slug"),
project_id=self.kwargs.get("project_id"),
member_id=self.request.user.id,
is_deactivated=False,
is_active=True,
)
)
)
@ -1446,7 +1446,7 @@ class IssueCommentPublicViewSet(BaseViewSet):
if not ProjectMember.objects.filter(
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
).exists():
# Add the user for workspace tracking
_ = ProjectPublicMember.objects.get_or_create(
@ -1560,7 +1560,7 @@ class IssueReactionPublicViewSet(BaseViewSet):
if not ProjectMember.objects.filter(
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
).exists():
# Add the user for workspace tracking
_ = ProjectPublicMember.objects.get_or_create(
@ -1656,7 +1656,7 @@ class CommentReactionPublicViewSet(BaseViewSet):
if not ProjectMember.objects.filter(
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
).exists():
# Add the user for workspace tracking
_ = ProjectPublicMember.objects.get_or_create(
@ -1743,7 +1743,7 @@ class IssueVotePublicViewSet(BaseViewSet):
if not ProjectMember.objects.filter(
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
).exists():
_ = ProjectPublicMember.objects.get_or_create(
project_id=project_id,

View File

@ -88,7 +88,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
workspace__slug=slug,
member=request.user,
role__lt=15,
is_deactivated=False,
is_active=True,
).exists():
notifications = Notification.objects.none()
else:
@ -261,7 +261,7 @@ class MarkAllReadNotificationViewSet(BaseViewSet):
workspace__slug=slug,
member=request.user,
role__lt=15,
is_deactivated=False,
is_active=True,
).exists():
notifications = Notification.objects.none()
else:

View File

@ -168,7 +168,6 @@ class OauthEndpoint(BaseAPIView):
)
## Login Case
if not user.is_active:
return Response(
{

View File

@ -110,7 +110,7 @@ class ProjectViewSet(BaseViewSet):
member=self.request.user,
project_id=OuterRef("pk"),
workspace__slug=self.kwargs.get("slug"),
is_deactivated=False,
is_active=True,
)
)
)
@ -118,7 +118,7 @@ class ProjectViewSet(BaseViewSet):
total_members=ProjectMember.objects.filter(
project_id=OuterRef("id"),
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
@ -140,7 +140,7 @@ class ProjectViewSet(BaseViewSet):
member_role=ProjectMember.objects.filter(
project_id=OuterRef("pk"),
member_id=self.request.user.id,
is_deactivated=False,
is_active=True,
).values("role")
)
.annotate(
@ -161,7 +161,7 @@ class ProjectViewSet(BaseViewSet):
member=request.user,
project_id=OuterRef("pk"),
workspace__slug=self.kwargs.get("slug"),
is_deactivated=False,
is_active=True,
).values("sort_order")
projects = (
self.get_queryset()
@ -171,7 +171,7 @@ class ProjectViewSet(BaseViewSet):
"project_projectmember",
queryset=ProjectMember.objects.filter(
workspace__slug=slug,
is_deactivated=False,
is_active=True,
).select_related("member"),
)
)
@ -372,7 +372,7 @@ class InviteProjectEndpoint(BaseAPIView):
project_id=project_id,
member__email=email,
member__is_bot=False,
is_deactivated=False,
is_active=True,
).exists():
return Response(
{"error": "User is already member of Project"},
@ -506,7 +506,7 @@ class ProjectMemberViewSet(BaseViewSet):
ProjectMember.objects.filter(
workspace__slug=slug,
member_id__in=[member.get("member_id") for member in members],
is_deactivated=False,
is_active=True,
)
.values("member_id", "sort_order")
.order_by("sort_order")
@ -554,14 +554,14 @@ class ProjectMemberViewSet(BaseViewSet):
member=request.user,
workspace__slug=slug,
project_id=project_id,
is_deactivated=False,
is_active=True,
)
project_members = ProjectMember.objects.filter(
project_id=project_id,
workspace__slug=slug,
member__is_bot=False,
is_deactivated=False,
is_active=True,
).select_related("project", "member", "workspace")
if project_member.role > 10:
@ -572,7 +572,7 @@ class ProjectMemberViewSet(BaseViewSet):
def partial_update(self, request, slug, project_id, pk):
project_member = ProjectMember.objects.get(
pk=pk, workspace__slug=slug, project_id=project_id, is_deactivated=False,
pk=pk, workspace__slug=slug, project_id=project_id, is_active=True,
)
if request.user.id == project_member.member_id:
return Response(
@ -581,7 +581,7 @@ class ProjectMemberViewSet(BaseViewSet):
)
# Check while updating user roles
requested_project_member = ProjectMember.objects.get(
project_id=project_id, workspace__slug=slug, member=request.user, is_deactivated=False,
project_id=project_id, workspace__slug=slug, member=request.user, is_active=True,
)
if (
"role" in request.data
@ -608,14 +608,14 @@ class ProjectMemberViewSet(BaseViewSet):
project_id=project_id,
pk=pk,
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
# check requesting user role
requesting_project_member = ProjectMember.objects.get(
workspace__slug=slug,
member=request.user,
project_id=project_id,
is_deactivated=False,
is_active=True,
)
# User cannot remove himself
if str(project_member.id) == str(requesting_project_member.id):
@ -641,7 +641,7 @@ class ProjectMemberViewSet(BaseViewSet):
workspace__slug=slug,
project_id=project_id,
member=request.user,
is_deactivated=False,
is_active=True,
)
# Check if the leaving user is the only admin of the project
@ -651,7 +651,7 @@ class ProjectMemberViewSet(BaseViewSet):
workspace__slug=slug,
project_id=project_id,
role=20,
is_deactivated=False,
is_active=True,
).count()
> 1
):
@ -807,7 +807,7 @@ class ProjectJoinEndpoint(BaseAPIView):
workspace_member = WorkspaceMember.objects.get(
member=request.user,
workspace__slug=slug,
is_deactivated=False,
is_active=True,
)
workspace_role = workspace_member.role
@ -853,7 +853,7 @@ class ProjectUserViewsEndpoint(BaseAPIView):
project = Project.objects.get(pk=project_id, workspace__slug=slug)
project_member = ProjectMember.objects.filter(
member=request.user, project=project, is_deactivated=False,
member=request.user, project=project, is_active=True,
).first()
if project_member is None:
@ -877,7 +877,7 @@ class ProjectUserViewsEndpoint(BaseAPIView):
class ProjectMemberUserEndpoint(BaseAPIView):
def get(self, request, slug, project_id):
project_member = ProjectMember.objects.get(
project_id=project_id, workspace__slug=slug, member=request.user, is_deactivated=False,
project_id=project_id, workspace__slug=slug, member=request.user, is_active=True,
)
serializer = ProjectMemberSerializer(project_member)

View File

@ -13,13 +13,7 @@ from plane.api.serializers import (
)
from plane.api.views.base import BaseViewSet, BaseAPIView
from plane.db.models import (
User,
Workspace,
WorkspaceMemberInvite,
Issue,
IssueActivity,
)
from plane.db.models import User, IssueActivity, WorkspaceMember
from plane.utils.paginator import BasePaginator
@ -41,10 +35,28 @@ class UserEndpoint(BaseViewSet):
serialized_data = UserMeSettingsSerializer(request.user).data
return Response(serialized_data, status=status.HTTP_200_OK)
def deactivate(self, request):
# Check all workspace user is active
user = self.get_object()
if WorkspaceMember.objects.filter(
member=request.user, is_deactivated=False
).exists():
return Response(
{
"error": "User cannot deactivate account as user is active in some workspaces"
},
status=status.HTTP_400_BAD_REQUEST,
)
# Deactivate the user
user.is_active = False
user.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class UpdateUserOnBoardedEndpoint(BaseAPIView):
def patch(self, request):
user = User.objects.get(pk=request.user.id)
user = User.objects.get(pk=request.user.id, is_active=True)
user.is_onboarded = request.data.get("is_onboarded", False)
user.save()
return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK)
@ -52,7 +64,7 @@ class UpdateUserOnBoardedEndpoint(BaseAPIView):
class UpdateUserTourCompletedEndpoint(BaseAPIView):
def patch(self, request):
user = User.objects.get(pk=request.user.id)
user = User.objects.get(pk=request.user.id, is_active=True)
user.is_tour_completed = request.data.get("is_tour_completed", False)
user.save()
return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK)

View File

@ -100,7 +100,7 @@ class WorkSpaceViewSet(BaseViewSet):
WorkspaceMember.objects.filter(
workspace=OuterRef("id"),
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
@ -177,7 +177,7 @@ class UserWorkSpacesEndpoint(BaseAPIView):
WorkspaceMember.objects.filter(
workspace=OuterRef("id"),
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
@ -240,7 +240,7 @@ class InviteWorkspaceEndpoint(BaseAPIView):
requesting_user = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
if len(
[
@ -260,7 +260,7 @@ class InviteWorkspaceEndpoint(BaseAPIView):
workspace_members = WorkspaceMember.objects.filter(
workspace_id=workspace.id,
member__email__in=[email.get("email") for email in emails],
is_deactivated=False,
is_active=True,
).select_related("member", "workspace", "workspace__owner")
if len(workspace_members):
@ -483,7 +483,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
.filter(
workspace__slug=self.kwargs.get("slug"),
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
.select_related("workspace", "workspace__owner")
.select_related("member")
@ -493,7 +493,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
workspace_member = WorkspaceMember.objects.get(
member=request.user,
workspace__slug=slug,
is_deactivated=False,
is_active=True,
)
# Get all active workspace members
@ -513,7 +513,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
pk=pk,
workspace__slug=slug,
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
if request.user.id == workspace_member.member_id:
return Response(
@ -525,7 +525,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
requested_workspace_member = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
# Check if role is being updated
# One cannot update role higher than his own role
@ -554,14 +554,14 @@ class WorkSpaceMemberViewSet(BaseViewSet):
workspace__slug=slug,
pk=pk,
member__is_bot=False,
is_deactivated=False,
is_active=True,
)
# check requesting user role
requesting_workspace_member = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
if str(workspace_member.id) == str(requesting_workspace_member.id):
@ -601,7 +601,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
# Deactivate the users from the projects where the user is part of
_ = ProjectMember.objects.filter(
workspace__slug=slug, member_id=workspace_member.member_id, is_deactivated=False,
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True,
).update(is_deactivated=True)
workspace_member.is_deactivated = True
@ -612,7 +612,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
workspace_member = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
# Check if the leaving user is the only admin of the workspace
@ -621,7 +621,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
and not WorkspaceMember.objects.filter(
workspace__slug=slug,
role=20,
is_deactivated=False,
is_active=True,
).count()
> 1
):
@ -655,7 +655,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
# # Deactivate the users from the projects where the user is part of
_ = ProjectMember.objects.filter(
workspace__slug=slug, member_id=workspace_member.member_id, is_deactivated=False,
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True,
).update(is_deactivated=True)
# # Deactivate the user
@ -690,7 +690,7 @@ class TeamMemberViewSet(BaseViewSet):
WorkspaceMember.objects.filter(
workspace__slug=slug,
member__id__in=request.data.get("members", []),
is_deactivated=False,
is_active=True,
)
.annotate(member_str_id=Cast("member", output_field=CharField()))
.distinct()
@ -774,7 +774,7 @@ class WorkspaceMemberUserEndpoint(BaseAPIView):
workspace_member = WorkspaceMember.objects.get(
member=request.user,
workspace__slug=slug,
is_deactivated=False,
is_active=True,
)
serializer = WorkspaceMemberMeSerializer(workspace_member)
return Response(serializer.data, status=status.HTTP_200_OK)
@ -785,7 +785,7 @@ class WorkspaceMemberUserViewsEndpoint(BaseAPIView):
workspace_member = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
workspace_member.view_props = request.data.get("view_props", {})
workspace_member.save()
@ -1113,7 +1113,7 @@ class WorkspaceUserProfileEndpoint(BaseAPIView):
requesting_workspace_member = WorkspaceMember.objects.get(
workspace__slug=slug,
member=request.user,
is_deactivated=False,
is_active=True,
)
projects = []
if requesting_workspace_member.role >= 10:

View File

@ -0,0 +1,26 @@
# Generated by Django 4.2.5 on 2023-11-10 09:41
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('db', '0047_issuemention_projectmember_is_deactivated_and_more'),
]
operations = [
migrations.AddField(
model_name='projectmember',
name='is_active',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='workspacemember',
name='is_active',
field=models.BooleanField(default=True),
),
]

View File

@ -166,7 +166,7 @@ class ProjectMember(ProjectBaseModel):
default_props = models.JSONField(default=get_default_props)
preferences = models.JSONField(default=get_default_preferences)
sort_order = models.FloatField(default=65535)
is_deactivated = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
def save(self, *args, **kwargs):
if self._state.adding:

View File

@ -99,7 +99,7 @@ class WorkspaceMember(BaseModel):
view_props = models.JSONField(default=get_default_props)
default_props = models.JSONField(default=get_default_props)
issue_props = models.JSONField(default=get_issue_props)
is_deactivated = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
class Meta:
unique_together = ["workspace", "member"]