Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions apps/api/plane/app/permissions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,31 @@ def _wrapped_view(instance, request, *args, **kwargs):
).exists():
return view_func(instance, request, *args, **kwargs)
else:
if ProjectMember.objects.filter(
is_user_has_allowed_role = ProjectMember.objects.filter(
member=request.user,
workspace__slug=kwargs["slug"],
project_id=kwargs["project_id"],
role__in=allowed_role_values,
is_active=True,
).exists():
).exists()

# Return if the user has the allowed role else if they are workspace admin and part of the project regardless of the role
if is_user_has_allowed_role:
return view_func(instance, request, *args, **kwargs)
elif (
ProjectMember.objects.filter(
member=request.user,
workspace__slug=kwargs["slug"],
project_id=kwargs["project_id"],
is_active=True,
).exists()
and WorkspaceMember.objects.filter(
member=request.user,
workspace__slug=kwargs["slug"],
role=ROLE.ADMIN.value,
is_active=True,
).exists()
):
return view_func(instance, request, *args, **kwargs)

# Return permission denied if no conditions are met
Expand Down
35 changes: 22 additions & 13 deletions apps/api/plane/app/permissions/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@

# Module import
from plane.db.models import ProjectMember, WorkspaceMember

# Permission Mappings
Admin = 20
Member = 15
Guest = 5
from plane.db.models.project import ROLE


class ProjectBasePermission(BasePermission):
Expand All @@ -26,18 +22,31 @@ def has_permission(self, request, view):
return WorkspaceMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
role__in=[ROLE.ADMIN.value, ROLE.MEMBER.value],
is_active=True,
).exists()

## Only Project Admins can update project attributes
return ProjectMember.objects.filter(
project_member_qs = ProjectMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role=Admin,
project_id=view.project_id,
is_active=True,
).exists()
)

## Only project admins or workspace admin who is part of the project can access

if project_member_qs.filter(role=ROLE.ADMIN.value).exists():
return True
else:
return (
project_member_qs.exists()
and WorkspaceMember.objects.filter(
member=request.user,
workspace__slug=view.workspace_slug,
role=ROLE.ADMIN.value,
is_active=True,
).exists()
)


class ProjectMemberPermission(BasePermission):
Expand All @@ -55,15 +64,15 @@ def has_permission(self, request, view):
return WorkspaceMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
role__in=[ROLE.ADMIN.value, ROLE.MEMBER.value],
is_active=True,
).exists()

## Only Project Admins can update project attributes
return ProjectMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
role__in=[ROLE.ADMIN.value, ROLE.MEMBER.value],
project_id=view.project_id,
is_active=True,
).exists()
Expand Down Expand Up @@ -97,7 +106,7 @@ def has_permission(self, request, view):
return ProjectMember.objects.filter(
workspace__slug=view.workspace_slug,
member=request.user,
role__in=[Admin, Member],
role__in=[ROLE.ADMIN.value, ROLE.MEMBER.value],
project_id=view.project_id,
is_active=True,
).exists()
Expand Down
52 changes: 39 additions & 13 deletions apps/api/plane/app/views/project/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
import json

# Django imports
from django.db import IntegrityError
from django.db.models import Exists, F, OuterRef, Prefetch, Q, Subquery
from django.core.serializers.json import DjangoJSONEncoder

# Third Party imports
from rest_framework.response import Response
from rest_framework import serializers, status
from rest_framework import status
from rest_framework.permissions import AllowAny

# Module imports
Expand Down Expand Up @@ -106,15 +105,21 @@ def list_detail(self, request, slug):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
projects = self.get_queryset().order_by("sort_order", "name")
if WorkspaceMember.objects.filter(
member=request.user, workspace__slug=slug, is_active=True, role=5
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.GUEST.value,
).exists():
projects = projects.filter(
project_projectmember__member=self.request.user,
project_projectmember__is_active=True,
)

if WorkspaceMember.objects.filter(
member=request.user, workspace__slug=slug, is_active=True, role=15
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.MEMBER.value,
).exists():
projects = projects.filter(
Q(
Expand Down Expand Up @@ -189,15 +194,21 @@ def list(self, request, slug):
)

if WorkspaceMember.objects.filter(
member=request.user, workspace__slug=slug, is_active=True, role=5
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.GUEST.value,
).exists():
projects = projects.filter(
project_projectmember__member=self.request.user,
project_projectmember__is_active=True,
)

if WorkspaceMember.objects.filter(
member=request.user, workspace__slug=slug, is_active=True, role=15
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.GUEST.value,
).exists():
projects = projects.filter(
Q(
Expand Down Expand Up @@ -250,7 +261,9 @@ def create(self, request, slug):

# Add the user as Administrator to the project
_ = ProjectMember.objects.create(
project_id=serializer.data["id"], member=request.user, role=20
project_id=serializer.data["id"],
member=request.user,
role=ROLE.ADMIN.value,
)
# Also create the issue property for the user
_ = IssueUserProperty.objects.create(
Expand All @@ -263,7 +276,7 @@ def create(self, request, slug):
ProjectMember.objects.create(
project_id=serializer.data["id"],
member_id=serializer.data["project_lead"],
role=20,
role=ROLE.ADMIN.value,
)
# Also create the issue property for the user
IssueUserProperty.objects.create(
Expand Down Expand Up @@ -341,13 +354,23 @@ def create(self, request, slug):

def partial_update(self, request, slug, pk=None):
# try:
if not ProjectMember.objects.filter(
is_workspace_admin = WorkspaceMember.objects.filter(
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.ADMIN.value,
).exists()

is_project_admin = ProjectMember.objects.filter(
member=request.user,
workspace__slug=slug,
project_id=pk,
role=20,
role=ROLE.ADMIN.value,
is_active=True,
).exists():
).exists()

# Return error for if the user is neither workspace admin nor project admin
if not is_project_admin and not is_workspace_admin:
return Response(
{"error": "You don't have the required permissions."},
status=status.HTTP_403_FORBIDDEN,
Expand Down Expand Up @@ -402,13 +425,16 @@ def partial_update(self, request, slug, pk=None):
def destroy(self, request, slug, pk):
if (
WorkspaceMember.objects.filter(
member=request.user, workspace__slug=slug, is_active=True, role=20
member=request.user,
workspace__slug=slug,
is_active=True,
role=ROLE.ADMIN.value,
).exists()
or ProjectMember.objects.filter(
member=request.user,
workspace__slug=slug,
project_id=pk,
role=20,
role=ROLE.ADMIN.value,
is_active=True,
).exists()
):
Expand Down
8 changes: 7 additions & 1 deletion apps/api/plane/db/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
# Module imports
from .base import BaseModel

ROLE_CHOICES = ((20, "Admin"), (15, "Member"), (5, "Guest"))
ROLE_CHOICES = (("Admin", 20), ("Member", 15), ("Guest", 5))


class ROLE(Enum):
ADMIN = 20
MEMBER = 15
GUEST = 5


class ProjectNetwork(Enum):
Expand Down
6 changes: 5 additions & 1 deletion apps/web/core/store/user/base-permissions.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor
*/
protected getProjectRole = computedFn((workspaceSlug: string, projectId: string): EUserPermissions | undefined => {
if (!workspaceSlug || !projectId) return undefined;
return this.workspaceProjectsPermissions?.[workspaceSlug]?.[projectId] || undefined;
const projectRole = this.workspaceProjectsPermissions?.[workspaceSlug]?.[projectId];
if (!projectRole) return undefined;
const workspaceRole = this.workspaceUserInfo?.[workspaceSlug]?.role;
if (workspaceRole === EUserWorkspaceRoles.ADMIN) return EUserPermissions.ADMIN;
else return projectRole;
});

/**
Expand Down
Loading