[WEB-3597] fix: guest work item view access when hyper mode is enabled (#6785)

* [WEB-3597] fix: guest work item view access when hyper mode is enabled

* fix: only show work item created by the guest user if the guest_view_all_features is disabled
This commit is contained in:
Prateek Shourya 2025-03-20 19:43:40 +05:30 committed by GitHub
parent b0e941e4e2
commit 94bf90dac5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 63 additions and 10 deletions

View file

@ -177,6 +177,7 @@ class ProjectViewSet(BaseViewSet):
"module_view",
"page_view",
"inbox_view",
"guest_view_all_features",
"project_lead",
"created_at",
"updated_at",

View file

@ -25,6 +25,7 @@ export interface IPartialProject {
module_view: boolean;
page_view: boolean;
inbox_view: boolean;
guest_view_all_features?: boolean;
project_lead?: IUserLite | string | null;
// Timestamps
created_at?: Date;
@ -46,11 +47,8 @@ export interface IProject extends IPartialProject {
default_state?: string | null;
description?: string;
estimate?: string | null;
guest_view_all_features?: boolean;
anchor?: string | null;
is_favorite?: boolean;
is_issue_type_enabled?: boolean;
is_time_tracking_enabled?: boolean;
members?: string[];
network?: number;
timezone?: string;

View file

@ -2,7 +2,7 @@ import * as Comlink from "comlink";
import set from "lodash/set";
// plane
import { EIssueGroupBYServerToProperty } from "@plane/constants";
import { TIssue } from "@plane/types";
import { TIssue, TIssueParams } from "@plane/types";
// lib
import { rootStore } from "@/lib/store-context";
// services
@ -15,6 +15,7 @@ import { addIssuesBulk, syncDeletesToLocal } from "./utils/load-issues";
import { loadWorkSpaceData } from "./utils/load-workspace";
import { issueFilterCountQueryConstructor, issueFilterQueryConstructor } from "./utils/query-constructor";
import { runQuery } from "./utils/query-executor";
import { sanitizeWorkItemQueries } from "./utils/query-sanitizer.ts";
import { createTables } from "./utils/tables";
import { clearOPFS, getGroupedIssueResults, getSubGroupedIssueResults, log, logError } from "./utils/utils";
@ -269,7 +270,12 @@ export class Storage {
return issue.updated_at;
};
getIssues = async (workspaceSlug: string, projectId: string, queries: any, config: any) => {
getIssues = async (
workspaceSlug: string,
projectId: string,
queries: Partial<Record<TIssueParams, string | boolean>> | undefined,
config: any
) => {
log("#### Queries", queries);
const currentProjectStatus = this.getStatus(projectId);
@ -294,11 +300,12 @@ export class Storage {
}
}
const { cursor, group_by, sub_group_by } = queries;
const sanitizedQueries = sanitizeWorkItemQueries(workspaceSlug, projectId, queries);
const { cursor, group_by, sub_group_by } = sanitizedQueries || {};
const query = issueFilterQueryConstructor(this.workspaceSlug, projectId, queries);
const query = issueFilterQueryConstructor(this.workspaceSlug, projectId, sanitizedQueries);
log("#### Query", query);
const countQuery = issueFilterCountQueryConstructor(this.workspaceSlug, projectId, queries);
const countQuery = issueFilterCountQueryConstructor(this.workspaceSlug, projectId, sanitizedQueries);
const start = performance.now();
let issuesRaw: any[] = [];
let count: any[];
@ -313,7 +320,7 @@ export class Storage {
const { total_count } = count[0];
const [pageSize, page, offset] = cursor.split(":");
const [pageSize, page, offset] = cursor && typeof cursor === "string" ? cursor.split(":") : [];
const groupByProperty: string =
EIssueGroupBYServerToProperty[group_by as keyof typeof EIssueGroupBYServerToProperty];

View file

@ -0,0 +1,41 @@
// plane constants
import { EUserPermissions } from "@plane/constants";
import { TIssueParams } from "@plane/types";
// root store
import { rootStore } from "@/lib/store-context";
export const sanitizeWorkItemQueries = (
workspaceSlug: string,
projectId: string,
queries: Partial<Record<TIssueParams, string | boolean>> | undefined
): Partial<Record<TIssueParams, string | boolean>> | undefined => {
// Get current project details and user id and role for the project
const currentProject = rootStore.projectRoot.project.getProjectById(projectId);
const currentUserId = rootStore.user.data?.id;
const currentUserRole = rootStore.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
workspaceSlug,
projectId
);
// Only apply this restriction for guests when guest_view_all_features is disabled
if (
currentUserId &&
currentUserRole === EUserPermissions.GUEST &&
currentProject?.guest_view_all_features === false
) {
// Sanitize the created_by filter if it doesn't exist or if it exists and includes the current user id
const existingCreatedByFilter = queries?.created_by;
const shouldApplyFilter =
!existingCreatedByFilter ||
(typeof existingCreatedByFilter === "string" && existingCreatedByFilter.includes(currentUserId));
if (shouldApplyFilter) {
queries = {
...queries,
created_by: currentUserId,
};
}
}
return queries;
};

View file

@ -1,6 +1,7 @@
import { EIssueServiceType } from "@plane/constants";
// types
import {
TIssueParams,
type IIssueDisplayProperties,
type TBulkOperationsPayload,
type TIssue,
@ -75,7 +76,12 @@ export class IssueService extends APIService {
});
}
async getIssues(workspaceSlug: string, projectId: string, queries?: any, config = {}): Promise<TIssuesResponse> {
async getIssues(
workspaceSlug: string,
projectId: string,
queries?: Partial<Record<TIssueParams, string | boolean>>,
config = {}
): Promise<TIssuesResponse> {
if (getIssuesShouldFallbackToServer(queries) || this.serviceType !== EIssueServiceType.ISSUES) {
return await this.getIssuesFromServer(workspaceSlug, projectId, queries, config);
}