Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12991,8 +12991,9 @@ function canJoinChat(
return false;
}

// For restricted visibility rooms, the user must be a workspace member to join
if (isUserCreatedPolicyRoom(report) && report?.visibility === CONST.REPORT.VISIBILITY.RESTRICTED && !getPolicyRole(policy, currentUserEmail ?? deprecatedCurrentUserEmail)) {
// For restricted visibility rooms, the user must be a non-guest workspace member to join
const policyRole = getPolicyRole(policy, currentUserEmail ?? deprecatedCurrentUserEmail);
if (isUserCreatedPolicyRoom(report) && report?.visibility === CONST.REPORT.VISIBILITY.RESTRICTED && (!policyRole || policyRole === CONST.POLICY.ROLE.GUEST)) {
return false;
}

Expand Down Expand Up @@ -14972,13 +14973,13 @@ function getStatusBadgeBackgroundColor(theme: ThemeColors, stateNum?: number, st
* Checks if a workspace member is leaving a workspace room
* This is used to determine if we need to show special handling when a workspace member leaves a room
*/
function isWorkspaceMemberLeavingWorkspaceRoom(report: OnyxEntry<Report>, isPolicyEmployee: boolean, isPolicyAdminParam: boolean): boolean {
function isWorkspaceMemberLeavingWorkspaceRoom(report: OnyxEntry<Report>, isPolicyEmployee: boolean, isPolicyAdminParam: boolean, isPolicyGuestParam = false): boolean {
if (!report) {
return false;
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const hasAccessPolicyExpenseChat = isPolicyExpenseChat(report) && (report.isOwnPolicyExpenseChat || isPolicyAdminParam);
return (report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED || hasAccessPolicyExpenseChat) && isPolicyEmployee;
return (report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED || hasAccessPolicyExpenseChat) && isPolicyEmployee && !isPolicyGuestParam;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import useOnyx from '@hooks/useOnyx';
import {useAllPersonalDetails} from '@hooks/usePersonalDetails';
import useReportIsArchived from '@hooks/useReportIsArchived';

import {isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil} from '@libs/PolicyUtils';
import {isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, isPolicyGuest as isPolicyGuestUtil} from '@libs/PolicyUtils';
import {
canLeaveChat,
findLastAccessedReport,
Expand Down Expand Up @@ -59,6 +59,7 @@ function ReportDetailsMenuLeaveItem({reportID}: ReportDetailsMenuLeaveItemProps)

const isPolicyAdmin = isPolicyAdminUtil(policy);
const isPolicyEmployee = isPolicyEmployeeUtil(report.policyID, policy);
const isPolicyGuest = isPolicyGuestUtil(policy);
const isRootGroupChat = isRootGroupChatUtil(report, isReportArchived);

const leaveChat = () => {
Expand All @@ -78,7 +79,7 @@ function ReportDetailsMenuLeaveItem({reportID}: ReportDetailsMenuLeaveItemProps)
return;
}

const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin);
const isWorkspaceMemberLeavingWorkspaceRoom = isWorkspaceMemberLeavingWorkspaceRoomUtil(report, isPolicyEmployee, isPolicyAdmin, isPolicyGuest);
leaveRoom(report, currentUserAccountID, conciergeReportID, introSelected, isSelfTourViewed, personalDetails, isWorkspaceMemberLeavingWorkspaceRoom, lastAccessedReportID);
};

Expand Down
77 changes: 71 additions & 6 deletions tests/unit/ReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15493,6 +15493,7 @@ describe('ReportUtils', () => {
});

it('should respect workspace membership for restricted visibility rooms', async () => {
// Given a workspace and reports with restricted and public visibility
const policyID = '123456';
await Onyx.set(ONYXKEYS.SESSION, {email: currentUserEmail, accountID: currentUserAccountID});

Expand All @@ -15509,6 +15510,13 @@ describe('ReportUtils', () => {
employeeList: {},
};

const policyWithGuestUser: Policy = {
...createRandomPolicy(3),
id: policyID,
role: CONST.POLICY.ROLE.GUEST,
employeeList: {},
};

const restrictedReport: Report = {
...createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_ROOM),
type: CONST.REPORT.TYPE.CHAT,
Expand All @@ -15528,9 +15536,16 @@ describe('ReportUtils', () => {
visibility: CONST.REPORT.VISIBILITY.PUBLIC,
};

// When checking whether a user can join restricted rooms
// Then regular workspace members can join, but non-members and guests cannot
expect(canJoinChat(restrictedReport, undefined, policyWithoutCurrentUser, undefined)).toBe(false);
expect(canJoinChat(restrictedReport, undefined, policyWithCurrentUser, undefined)).toBe(true);
expect(canJoinChat(restrictedReport, undefined, policyWithGuestUser, undefined)).toBe(false);

// When checking whether a user can join public rooms
// Then non-members and guests can both join
expect(canJoinChat(publicReport, undefined, policyWithoutCurrentUser, undefined)).toBe(true);
expect(canJoinChat(publicReport, undefined, policyWithGuestUser, undefined)).toBe(true);
});
});

Expand Down Expand Up @@ -17266,56 +17281,106 @@ describe('ReportUtils', () => {

describe('isWorkspaceMemberLeavingWorkspaceRoom', () => {
test('should return false when not a policy employee', () => {
// Given a restricted policy expense chat and a user who is not a policy employee
const report = {
...createRandomReport(1, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
visibility: CONST.REPORT.VISIBILITY.RESTRICTED,
isOwnPolicyExpenseChat: true,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, false, true)).toBe(false);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, false, true);

// Then it should return false
expect(result).toBe(false);
});

test('should return true for restricted room when policy employee', () => {
// Given a restricted room and a user who is a policy employee
const report = {
...createRandomReport(2, undefined),
visibility: CONST.REPORT.VISIBILITY.RESTRICTED,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, true, false)).toBe(true);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, false);

// Then it should return true
expect(result).toBe(true);
});

test('should return true for policy expense chat when own chat and policy employee', () => {
// Given a public policy expense chat owned by the user who is a policy employee
const report = {
...createRandomReport(3, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
visibility: CONST.REPORT.VISIBILITY.PUBLIC,
isOwnPolicyExpenseChat: true,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, true, false)).toBe(true);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, false);

// Then it should return true
expect(result).toBe(true);
});

test('should return true for policy expense chat when policy admin and policy employee', () => {
// Given a public policy expense chat not owned by the user, but the user is a policy admin and employee
const report = {
...createRandomReport(4, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
Comment thread
ShridharGoel marked this conversation as resolved.
visibility: CONST.REPORT.VISIBILITY.PUBLIC,
isOwnPolicyExpenseChat: false,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, true, true)).toBe(true);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, true);

// Then it should return true
expect(result).toBe(true);
});

test('should return false for non-restricted, non-policy expense chat', () => {
// Given a public group chat
const report = {
...createRandomReport(5, CONST.REPORT.CHAT_TYPE.GROUP),
visibility: CONST.REPORT.VISIBILITY.PUBLIC,
isOwnPolicyExpenseChat: false,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, true, false)).toBe(false);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, false);

// Then it should return false
expect(result).toBe(false);
});

test('should return false for non-restricted policy expense chat when not own chat and not admin', () => {
// Given a public policy expense chat not owned by the user and the user is not an admin
const report = {
...createRandomReport(6, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT),
visibility: CONST.REPORT.VISIBILITY.PUBLIC,
isOwnPolicyExpenseChat: false,
};
expect(isWorkspaceMemberLeavingWorkspaceRoom(report, true, false)).toBe(false);

// When checking if the member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, false);

// Then it should return false
expect(result).toBe(false);
});

test('should return false for restricted room when user is a policy guest', () => {
// Given a restricted room and a user who is a policy guest
const report = {
...createRandomReport(7, undefined),
visibility: CONST.REPORT.VISIBILITY.RESTRICTED,
};

// When checking if the guest member is leaving the workspace room
const result = isWorkspaceMemberLeavingWorkspaceRoom(report, true, false, true);

// Then it should return false
expect(result).toBe(false);
});
});

Expand Down
Loading