dev: revamp pages authorization (#6094)
This commit is contained in:
parent
9f14167ef5
commit
8c04aa6f51
4 changed files with 48 additions and 30 deletions
|
|
@ -114,7 +114,7 @@ class PageViewSet(BaseViewSet):
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
||||||
def create(self, request, slug, project_id):
|
def create(self, request, slug, project_id):
|
||||||
serializer = PageSerializer(
|
serializer = PageSerializer(
|
||||||
data=request.data,
|
data=request.data,
|
||||||
|
|
@ -134,7 +134,7 @@ class PageViewSet(BaseViewSet):
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER])
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(
|
page = Page.objects.get(
|
||||||
|
|
@ -234,7 +234,7 @@ class PageViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
return Response(data, status=status.HTTP_200_OK)
|
return Response(data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def lock(self, request, slug, project_id, pk):
|
def lock(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -244,7 +244,7 @@ class PageViewSet(BaseViewSet):
|
||||||
page.save()
|
page.save()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def unlock(self, request, slug, project_id, pk):
|
def unlock(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||||
|
|
@ -255,7 +255,7 @@ class PageViewSet(BaseViewSet):
|
||||||
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def access(self, request, slug, project_id, pk):
|
def access(self, request, slug, project_id, pk):
|
||||||
access = request.data.get("access", 0)
|
access = request.data.get("access", 0)
|
||||||
page = Page.objects.filter(
|
page = Page.objects.filter(
|
||||||
|
|
@ -296,7 +296,7 @@ class PageViewSet(BaseViewSet):
|
||||||
pages = PageSerializer(queryset, many=True).data
|
pages = PageSerializer(queryset, many=True).data
|
||||||
return Response(pages, status=status.HTTP_200_OK)
|
return Response(pages, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def archive(self, request, slug, project_id, pk):
|
def archive(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
||||||
|
|
||||||
|
|
@ -323,7 +323,7 @@ class PageViewSet(BaseViewSet):
|
||||||
|
|
||||||
return Response({"archived_at": str(datetime.now())}, status=status.HTTP_200_OK)
|
return Response({"archived_at": str(datetime.now())}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def unarchive(self, request, slug, project_id, pk):
|
def unarchive(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
||||||
|
|
||||||
|
|
@ -348,7 +348,7 @@ class PageViewSet(BaseViewSet):
|
||||||
|
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN], creator=True, model=Page)
|
@allow_permission([ROLE.ADMIN], model=Page, creator=True)
|
||||||
def destroy(self, request, slug, project_id, pk):
|
def destroy(self, request, slug, project_id, pk):
|
||||||
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,7 @@ import { BreadcrumbLink, Logo } from "@/components/common";
|
||||||
// constants
|
// constants
|
||||||
import { EPageAccess } from "@/constants/page";
|
import { EPageAccess } from "@/constants/page";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useProject, useProjectPages, useUserPermissions } from "@/hooks/store";
|
import { useEventTracker, useProject, useProjectPages } from "@/hooks/store";
|
||||||
// plane web hooks
|
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
|
||||||
|
|
||||||
export const PagesListHeader = observer(() => {
|
export const PagesListHeader = observer(() => {
|
||||||
// states
|
// states
|
||||||
|
|
@ -26,16 +24,9 @@ export const PagesListHeader = observer(() => {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const pageType = searchParams.get("type");
|
const pageType = searchParams.get("type");
|
||||||
// store hooks
|
// store hooks
|
||||||
const { allowPermissions } = useUserPermissions();
|
|
||||||
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
const { currentProjectDetails, loader } = useProject();
|
||||||
const { createPage } = useProjectPages();
|
const { canCurrentUserCreatePage, createPage } = useProjectPages();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
// auth
|
|
||||||
const canUserCreatePage = allowPermissions(
|
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
|
||||||
EUserPermissionsLevel.PROJECT
|
|
||||||
);
|
|
||||||
// handle page create
|
// handle page create
|
||||||
const handleCreatePage = async () => {
|
const handleCreatePage = async () => {
|
||||||
setIsCreatingPage(true);
|
setIsCreatingPage(true);
|
||||||
|
|
@ -87,7 +78,7 @@ export const PagesListHeader = observer(() => {
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
</Header.LeftItem>
|
</Header.LeftItem>
|
||||||
{canUserCreatePage ? (
|
{canCurrentUserCreatePage ? (
|
||||||
<Header.RightItem>
|
<Header.RightItem>
|
||||||
<Button variant="primary" size="sm" onClick={handleCreatePage} loading={isCreatingPage}>
|
<Button variant="primary" size="sm" onClick={handleCreatePage} loading={isCreatingPage}>
|
||||||
{isCreatingPage ? "Adding" : "Add page"}
|
{isCreatingPage ? "Adding" : "Add page"}
|
||||||
|
|
|
||||||
|
|
@ -215,12 +215,15 @@ export class Page implements IPage {
|
||||||
*/
|
*/
|
||||||
get canCurrentUserEditPage() {
|
get canCurrentUserEditPage() {
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
|
||||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
workspaceSlug?.toString() || "",
|
workspaceSlug?.toString() || "",
|
||||||
projectId?.toString() || ""
|
projectId?.toString() || ""
|
||||||
);
|
);
|
||||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER);
|
const isPagePublic = this.access === EPageAccess.PUBLIC;
|
||||||
|
return (
|
||||||
|
(isPagePublic && !!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER) ||
|
||||||
|
(!isPagePublic && this.isCurrentUserOwner)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -228,26 +231,35 @@ export class Page implements IPage {
|
||||||
*/
|
*/
|
||||||
get canCurrentUserDuplicatePage() {
|
get canCurrentUserDuplicatePage() {
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
|
||||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
workspaceSlug?.toString() || "",
|
workspaceSlug?.toString() || "",
|
||||||
projectId?.toString() || ""
|
projectId?.toString() || ""
|
||||||
);
|
);
|
||||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER);
|
return !!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns true if the current logged in user can lock the page
|
* @description returns true if the current logged in user can lock the page
|
||||||
*/
|
*/
|
||||||
get canCurrentUserLockPage() {
|
get canCurrentUserLockPage() {
|
||||||
return this.isCurrentUserOwner;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
|
workspaceSlug?.toString() || "",
|
||||||
|
projectId?.toString() || ""
|
||||||
|
);
|
||||||
|
return this.isCurrentUserOwner || currentUserProjectRole === EUserPermissions.ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns true if the current logged in user can change the access of the page
|
* @description returns true if the current logged in user can change the access of the page
|
||||||
*/
|
*/
|
||||||
get canCurrentUserChangeAccess() {
|
get canCurrentUserChangeAccess() {
|
||||||
return this.isCurrentUserOwner;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
|
workspaceSlug?.toString() || "",
|
||||||
|
projectId?.toString() || ""
|
||||||
|
);
|
||||||
|
return this.isCurrentUserOwner || currentUserProjectRole === EUserPermissions.ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -255,7 +267,6 @@ export class Page implements IPage {
|
||||||
*/
|
*/
|
||||||
get canCurrentUserArchivePage() {
|
get canCurrentUserArchivePage() {
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
|
||||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
workspaceSlug?.toString() || "",
|
workspaceSlug?.toString() || "",
|
||||||
projectId?.toString() || ""
|
projectId?.toString() || ""
|
||||||
|
|
@ -268,7 +279,6 @@ export class Page implements IPage {
|
||||||
*/
|
*/
|
||||||
get canCurrentUserDeletePage() {
|
get canCurrentUserDeletePage() {
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
|
||||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
workspaceSlug?.toString() || "",
|
workspaceSlug?.toString() || "",
|
||||||
projectId?.toString() || ""
|
projectId?.toString() || ""
|
||||||
|
|
@ -281,7 +291,6 @@ export class Page implements IPage {
|
||||||
*/
|
*/
|
||||||
get canCurrentUserFavoritePage() {
|
get canCurrentUserFavoritePage() {
|
||||||
const { workspaceSlug, projectId } = this.store.router;
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
|
||||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
workspaceSlug?.toString() || "",
|
workspaceSlug?.toString() || "",
|
||||||
projectId?.toString() || ""
|
projectId?.toString() || ""
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import unset from "lodash/unset";
|
import unset from "lodash/unset";
|
||||||
import { makeObservable, observable, runInAction, action, reaction } from "mobx";
|
import { makeObservable, observable, runInAction, action, reaction, computed } from "mobx";
|
||||||
import { computedFn } from "mobx-utils";
|
import { computedFn } from "mobx-utils";
|
||||||
// types
|
// types
|
||||||
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
|
||||||
// helpers
|
// helpers
|
||||||
import { filterPagesByPageType, getPageName, orderPages, shouldFilterPage } from "@/helpers/page.helper";
|
import { filterPagesByPageType, getPageName, orderPages, shouldFilterPage } from "@/helpers/page.helper";
|
||||||
|
// plane web constants
|
||||||
|
import { EUserPermissions } from "@/plane-web/constants";
|
||||||
// services
|
// services
|
||||||
import { ProjectPageService } from "@/services/page";
|
import { ProjectPageService } from "@/services/page";
|
||||||
// store
|
// store
|
||||||
|
|
@ -24,6 +26,7 @@ export interface IProjectPageStore {
|
||||||
filters: TPageFilters;
|
filters: TPageFilters;
|
||||||
// computed
|
// computed
|
||||||
isAnyPageAvailable: boolean;
|
isAnyPageAvailable: boolean;
|
||||||
|
canCurrentUserCreatePage: boolean;
|
||||||
// helper actions
|
// helper actions
|
||||||
getCurrentProjectPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
getCurrentProjectPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
||||||
getCurrentProjectFilteredPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
getCurrentProjectFilteredPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
|
||||||
|
|
@ -62,6 +65,9 @@ export class ProjectPageStore implements IProjectPageStore {
|
||||||
data: observable,
|
data: observable,
|
||||||
error: observable,
|
error: observable,
|
||||||
filters: observable,
|
filters: observable,
|
||||||
|
// computed
|
||||||
|
isAnyPageAvailable: computed,
|
||||||
|
canCurrentUserCreatePage: computed,
|
||||||
// helper actions
|
// helper actions
|
||||||
updateFilters: action,
|
updateFilters: action,
|
||||||
clearAllFilters: action,
|
clearAllFilters: action,
|
||||||
|
|
@ -92,6 +98,18 @@ export class ProjectPageStore implements IProjectPageStore {
|
||||||
return Object.keys(this.data).length > 0;
|
return Object.keys(this.data).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns true if the current logged in user can create a page
|
||||||
|
*/
|
||||||
|
get canCurrentUserCreatePage() {
|
||||||
|
const { workspaceSlug, projectId } = this.store.router;
|
||||||
|
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||||
|
workspaceSlug?.toString() || "",
|
||||||
|
projectId?.toString() || ""
|
||||||
|
);
|
||||||
|
return !!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get the current project page ids based on the pageType
|
* @description get the current project page ids based on the pageType
|
||||||
* @param {TPageNavigationTabs} pageType
|
* @param {TPageNavigationTabs} pageType
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue