Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PBAC Update user roles and permissions #567

Open
wants to merge 125 commits into
base: 3.2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
dcf3008
Merge branch '3.2.0' of https://github.com/CBIIT/crdc-datahub-ui into…
Alejandro-Vega Dec 17, 2024
8ff6060
Centralized PBAC management based on user permissions. Also added tests
Alejandro-Vega Dec 17, 2024
70aca1f
Update roles and permission types
Alejandro-Vega Dec 17, 2024
b3cf5f5
Update Access Request with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
c8afdce
Update API Token Dialog with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
781f0af
Update Collaborators with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
fc7126d
Update create data submission dialog with new permission check and up…
Alejandro-Vega Dec 17, 2024
6776a83
Update cross validation button with new permission check and updated …
Alejandro-Vega Dec 17, 2024
8734bf3
Updated failing Data Submission tests with new permission check and u…
Alejandro-Vega Dec 17, 2024
2eac021
Updated Data Upload with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
1b7d2b5
Updated Delete Node Data with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
7ba19ff
Updated Metadata Upload with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
6fc8a85
Updated Validation Controls with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
fb61975
Updated header/navigation with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
b936051
Update Data Submission workflow actions with new permission check and…
Alejandro-Vega Dec 17, 2024
1e2bed1
Updated Operation Dashboard with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
e520599
Updated Operation Dashboard View with new permission check and update…
Alejandro-Vega Dec 17, 2024
07c407f
Updated Form View with new permission check and updated roles
Alejandro-Vega Dec 17, 2024
8b00b6b
Updated Submission Request List View with new permission check and up…
Alejandro-Vega Dec 17, 2024
07e7faa
Updated Manage studies controller with new permission check and updat…
Alejandro-Vega Dec 17, 2024
0d3f847
Updated Manage Users controller with new permission check and updated…
Alejandro-Vega Dec 17, 2024
d8915ad
Updated useProfileFields hook with new permission check and updated r…
Alejandro-Vega Dec 17, 2024
4f853ca
Updated utility function for default validation type with new permiss…
Alejandro-Vega Dec 17, 2024
c271caf
Updated form mode utility logic with new permission check and updated…
Alejandro-Vega Dec 17, 2024
9ef4653
Update getMyUser query to return permissions
Alejandro-Vega Dec 18, 2024
fb0c6f5
Only allow Federal Lead to submit under certain submission statuses
Alejandro-Vega Dec 18, 2024
ea86277
Restrict form actions based on permissions and if form owner
Alejandro-Vega Dec 18, 2024
2af0290
Update header/navigation to use permission array instead
Alejandro-Vega Dec 18, 2024
bbbd6de
Merge branch '3.2.0' into pbac-update-user-roles
Alejandro-Vega Dec 19, 2024
e4c87b0
Fix incorrect tests
Alejandro-Vega Dec 19, 2024
68f5ee5
Update role-permissions mapping with just permissions
Alejandro-Vega Dec 19, 2024
0f3189c
Merge branch 'pbac-update-user-roles' of https://github.com/CBIIT/crd…
Alejandro-Vega Dec 19, 2024
394917d
Merge branch '3.2.0' into pbac-update-user-roles
Alejandro-Vega Dec 19, 2024
d21e159
Update permission config to check for constant if no additional condi…
Alejandro-Vega Dec 19, 2024
1151e29
Simplify tests for permissions
Alejandro-Vega Dec 19, 2024
834ab9d
Merge branch 'pbac-update-user-roles' into CRDCDH-2099
Alejandro-Vega Dec 19, 2024
4a53393
Update Submission Request submit permission logic
Alejandro-Vega Dec 19, 2024
73c547f
Simplify permission logic in FormView
Alejandro-Vega Dec 19, 2024
31550d9
Include implicit permission for submission owner
Alejandro-Vega Dec 20, 2024
9f9c56d
Remove redirect when user is unauthorized to view a Submission Reques…
Alejandro-Vega Dec 20, 2024
1c33aaa
Update test to support implicit permission
Alejandro-Vega Dec 20, 2024
1000de7
Update so that a user is only allowed to edit their own Submission Re…
Alejandro-Vega Dec 20, 2024
0de04ce
Remove unecessary log objects
Alejandro-Vega Dec 23, 2024
60f30f5
Add test coverage for submission_request:submit permission
Alejandro-Vega Dec 23, 2024
30fd20c
Merge branch '3.2.0' into pbac-update-user-roles
Alejandro-Vega Dec 26, 2024
79eb103
init: Permission Based Access Control for User Management
amattu2 Dec 26, 2024
010fcf6
Remove mock data
amattu2 Dec 26, 2024
cb58bb2
init: Unit tests for PermissionsPanel
amattu2 Dec 26, 2024
647e9d9
Merge branch 'pbac-update-user-roles' into CRDCDH-2105
amattu2 Dec 27, 2024
48af8a7
Merge branch 'pbac-update-user-roles' into CRDCDH-2099
amattu2 Dec 27, 2024
ac67379
fix: Missing useMemo dependencies
amattu2 Dec 27, 2024
3c7dc67
Merge pull request #569 from CBIIT/CRDCDH-2099
Alejandro-Vega Dec 27, 2024
386e90b
feat: Implement a No Options fallback placeholder
amattu2 Dec 27, 2024
f776d1f
Fix failing base test
amattu2 Dec 27, 2024
bd8c7b0
fix: Error logged during initial PBAC loading
amattu2 Dec 27, 2024
08e9a11
Implement component behavior tests
amattu2 Dec 27, 2024
8f4453f
Add coverage for requirements
amattu2 Dec 27, 2024
fb598ac
Cleanup PBAC panel implementation
amattu2 Dec 27, 2024
d670ff7
fix: Apollo normalizing permissions incorrectly
amattu2 Dec 27, 2024
636d00d
fix: PBAC data not sent to API
amattu2 Dec 27, 2024
9466aa6
Add validate permission to data submission resource
Alejandro-Vega Dec 27, 2024
045c8ae
Update metadata upload permissions
Alejandro-Vega Dec 27, 2024
0b94323
Create simple utility functions and update enabling submit logic and …
Alejandro-Vega Dec 27, 2024
fd27d9d
Update validation logic to support
Alejandro-Vega Dec 27, 2024
d92dd6b
Update Data Submission workflow action button logic
Alejandro-Vega Dec 27, 2024
e30fbe2
Remove unused `role` prop
amattu2 Dec 30, 2024
2bdf90e
Finish coverage
amattu2 Dec 30, 2024
3f04f0e
Deduplicate PBAC grouping logic
amattu2 Dec 30, 2024
69abfde
feat: Support partial sorting of PBAC groups
amattu2 Dec 30, 2024
8febde5
Standardize ProfileView buttons
amattu2 Dec 30, 2024
fc4b98a
fix: Center PBAC Panel and adjust margins
amattu2 Dec 30, 2024
bbe6497
Remove redundant test
amattu2 Dec 30, 2024
8d24a30
Merge branch 'pbac-update-user-roles' into CRDCDH-2105
amattu2 Dec 30, 2024
4ea1a2b
Update AuthPermission to allow for checking for permission key only, …
Alejandro-Vega Dec 30, 2024
4c83ed6
Update permissions to only check for permission key
Alejandro-Vega Dec 30, 2024
f043093
Simplify calling hasPermission since logic was moved to permission ma…
Alejandro-Vega Dec 30, 2024
7e9ece7
Update admin submit to show when available and user has permission key
Alejandro-Vega Dec 30, 2024
0f6ad8c
Fix cross validation tab not showing
Alejandro-Vega Dec 30, 2024
1c1b6f9
Fixed invalid tabs in URL, will redirect to default tab
Alejandro-Vega Dec 30, 2024
7db0513
Uncomment role specific tests
Alejandro-Vega Dec 30, 2024
7c9371b
Remove unused utils
Alejandro-Vega Dec 30, 2024
6bcc616
Added dashboard utils and tests. Also, updated to skip adding studies…
Alejandro-Vega Dec 30, 2024
ebd2725
Update content parameters to use new utility functions to add paramet…
Alejandro-Vega Dec 30, 2024
5d26765
Update utility function comments
Alejandro-Vega Dec 30, 2024
e82724d
Update user and collaborator types
Alejandro-Vega Dec 31, 2024
2af0b4f
Remove any mention of Can View permissions and user org
Alejandro-Vega Dec 31, 2024
de44310
Remove org and access columns from collaborators table
Alejandro-Vega Dec 31, 2024
ca9a60d
Check entire studies list for "All" study instead of first index
Alejandro-Vega Dec 31, 2024
3a65096
Remove redundant permission check while generating API token
Alejandro-Vega Dec 31, 2024
49843c4
Remove redundant dep and variable
Alejandro-Vega Dec 31, 2024
22fa12d
Merge pull request #574 from CBIIT/CRDCDH-2118
amattu2 Dec 31, 2024
1f7d50a
Update validation deps
Alejandro-Vega Dec 31, 2024
ac50ace
feat: Support PBAC at controller level
amattu2 Dec 31, 2024
0c3a798
fix: Remove redundant AuthStatus in view component
amattu2 Dec 31, 2024
abde83c
Rename header configuration variables
amattu2 Dec 31, 2024
f564fa8
Refactor NavBar sublinks to support PBAC
amattu2 Dec 31, 2024
cc0f321
fix: Mobile navbar includes CLI Tool
amattu2 Dec 31, 2024
99a161f
Update incorrect JSDoc
amattu2 Dec 31, 2024
4553245
fix: Change expand icon
amattu2 Dec 31, 2024
ee86b10
Remove validate permission and merge with create
Alejandro-Vega Dec 31, 2024
421d99a
Merge pull request #572 from CBIIT/CRDCDH-2105
Alejandro-Vega Dec 31, 2024
8cf513c
Merge branch 'pbac-update-user-roles' of https://github.com/CBIIT/crd…
Alejandro-Vega Dec 31, 2024
29463e1
Merge pull request #573 from CBIIT/CRDCDH-2098
amattu2 Dec 31, 2024
0c2980d
Merge branch 'pbac-update-user-roles' into CRDCDH-2102
amattu2 Dec 31, 2024
e8e492c
fix: Reverse CLI Tool with API Token for consistency
amattu2 Dec 31, 2024
0df30f8
fix: Don't clear selected menu on submenu click
amattu2 Dec 31, 2024
d483b1c
fix: Remove unused ref
amattu2 Dec 31, 2024
8109792
Merge pull request #575 from CBIIT/CRDCDH-2102
Alejandro-Vega Jan 2, 2025
d5addb5
fix: The `useProfileFields` hook should utilize PBAC
amattu2 Jan 2, 2025
9db2a7c
Merge pull request #577 from CBIIT/CRDCDH-2102-V2
Alejandro-Vega Jan 2, 2025
a3269a5
Delete irrelevant tests for collaborator permissions
Alejandro-Vega Jan 2, 2025
9a06994
Update collaboratorName to match BE format
Alejandro-Vega Jan 2, 2025
2a63a87
Merge branch 'pbac-update-user-roles' of https://github.com/CBIIT/crd…
Alejandro-Vega Jan 2, 2025
3ef746a
CRDCDH-2186 Allow save without changes
amattu2 Jan 3, 2025
8271f51
Merge branch '3.2.0' into PBAC-Sync-With-Base
amattu2 Jan 3, 2025
ee69eae
Merge pull request #582 from CBIIT/PBAC-Sync-With-Base
Alejandro-Vega Jan 3, 2025
ae73c67
Merge pull request #581 from CBIIT/CRDCDH-2186
Alejandro-Vega Jan 6, 2025
68cf98e
Remove formatName util and references
Alejandro-Vega Jan 6, 2025
baa8d1d
Remove Organization from Collaborator
Alejandro-Vega Jan 6, 2025
17de629
Remove org from return in editSubmissionCollaborators mutation
Alejandro-Vega Jan 6, 2025
edef527
Fix return type
Alejandro-Vega Jan 6, 2025
a1d0d61
Update collaborator dialog description
Alejandro-Vega Jan 6, 2025
eb122f3
Remove org from collaborators in getSubmission query
Alejandro-Vega Jan 6, 2025
9a68a63
Merge pull request #576 from CBIIT/CRDCDH-2021
amattu2 Jan 6, 2025
88c8f56
Merge branch '3.2.0' into pbac-update-user-roles
amattu2 Jan 6, 2025
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
34 changes: 19 additions & 15 deletions src/components/APITokenDialog/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const baseAuthCtx: AuthContextState = {
user: null,
};

const baseUser: User = {
const baseUser: Omit<User, "permissions"> = {
_id: "",
firstName: "",
lastName: "",
Expand All @@ -38,20 +38,27 @@ const baseUser: User = {
dataCommons: [],
createdAt: "",
updateAt: "",
notifications: [],
};

type ParentProps = {
children: React.ReactNode;
mocks?: MockedResponse[];
role?: UserRole;
permissions?: AuthPermissions[];
};

const TestParent: FC<ParentProps> = ({ role = "Submitter", mocks = [], children }) => {
const TestParent: FC<ParentProps> = ({
role = "Submitter",
permissions = ["data_submission:create"],
mocks = [],
children,
}) => {
const authState = useMemo<AuthContextState>(
() => ({
...baseAuthCtx,
isLoggedIn: true,
user: { ...baseUser, role },
user: { ...baseUser, role, permissions },
}),
[role]
);
Expand Down Expand Up @@ -264,18 +271,15 @@ describe("Implementation Requirements", () => {
expect(mockWriteText).not.toHaveBeenCalled();
});

it.each<UserRole>(["Admin", "Federal Lead", "Data Curator", "User", "fake user" as UserRole])(
"should show an error when the user role %s tries to generate a token",
async (role) => {
const { getByText } = render(<ApiTokenDialog open />, {
wrapper: (p) => <TestParent {...p} role={role} />,
});
it("should show an error when the user without the required permission tries to generate a token", async () => {
const { getByText } = render(<ApiTokenDialog open />, {
wrapper: (p) => <TestParent {...p} permissions={[]} />,
});

userEvent.click(getByText(/Create Token/, { selector: "button" }));
userEvent.click(getByText(/Create Token/, { selector: "button" }));

await waitFor(() => {
expect(getByText(/Token was unable to be created./)).toBeInTheDocument();
});
}
);
await waitFor(() => {
expect(getByText(/Token was unable to be created./)).toBeInTheDocument();
});
});
});
4 changes: 2 additions & 2 deletions src/components/APITokenDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import GenericAlert, { AlertState } from "../GenericAlert";
import { ReactComponent as CopyIconSvg } from "../../assets/icons/copy_icon.svg";
import { ReactComponent as CloseIconSvg } from "../../assets/icons/close_icon.svg";
import { useAuthContext } from "../Contexts/AuthContext";
import { GenerateApiTokenRoles } from "../../config/AuthRoles";
import { hasPermission } from "../../config/AuthPermissions";

const StyledDialog = styled(Dialog)({
"& .MuiDialog-paper": {
Expand Down Expand Up @@ -166,7 +166,7 @@ const APITokenDialog: FC<Props> = ({ onClose, open, ...rest }) => {
};

const generateToken = async () => {
if (!GenerateApiTokenRoles.includes(user?.role)) {
if (!hasPermission(user, "data_submission", "create")) {
onGenerateTokenError();
return;
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/AccessRequest/FormDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ const mockUser: User = {
userStatus: "Active",
updateAt: "",
createdAt: "",
permissions: ["access:request"],
notifications: [],
};

type MockParentProps = {
Expand Down
2 changes: 1 addition & 1 deletion src/components/AccessRequest/FormDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type Props = {
onClose: () => void;
} & Omit<DialogProps, "onClose">;

const RoleOptions: UserRole[] = ["Submitter", "Organization Owner"];
const RoleOptions: UserRole[] = ["Submitter"];

/**
* Provides a dialog for users to request access to a specific role.
Expand Down
43 changes: 17 additions & 26 deletions src/components/AccessRequest/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
ListApprovedStudiesResp,
} from "../../graphql";

const mockUser: Omit<User, "role"> = {
const mockUser: Omit<User, "role" | "permissions"> = {
_id: "",
firstName: "",
lastName: "",
Expand All @@ -27,6 +27,7 @@ const mockUser: Omit<User, "role"> = {
userStatus: "Active",
updateAt: "",
createdAt: "",
notifications: [],
};

const mockListApprovedStudies: MockedResponse<ListApprovedStudiesResp, ListApprovedStudiesInput> = {
Expand All @@ -47,15 +48,16 @@ const mockListApprovedStudies: MockedResponse<ListApprovedStudiesResp, ListAppro
type MockParentProps = {
mocks: MockedResponse[];
role: UserRole;
permissions: AuthPermissions[];
children: React.ReactNode;
};

const MockParent: FC<MockParentProps> = ({ mocks, role, children }) => {
const MockParent: FC<MockParentProps> = ({ mocks, role, permissions, children }) => {
const authValue: AuthContextState = useMemo<AuthContextState>(
() => ({
isLoggedIn: true,
status: AuthContextStatus.LOADED,
user: { ...mockUser, role },
user: { ...mockUser, role, permissions },
}),
[role]
);
Expand All @@ -70,7 +72,7 @@ const MockParent: FC<MockParentProps> = ({ mocks, role, children }) => {
describe("Accessibility", () => {
it("should not have any violations", async () => {
const { container } = render(<AccessRequest />, {
wrapper: (p) => <MockParent {...p} mocks={[]} role="User" />,
wrapper: (p) => <MockParent {...p} mocks={[]} role="User" permissions={["access:request"]} />,
});

expect(await axe(container)).toHaveNoViolations();
Expand All @@ -80,7 +82,14 @@ describe("Accessibility", () => {
describe("Basic Functionality", () => {
it("should open the dialog when the 'Request Access' button is clicked", async () => {
const { getByTestId, getByRole, queryByRole } = render(<AccessRequest />, {
wrapper: (p) => <MockParent {...p} mocks={[mockListApprovedStudies]} role="User" />,
wrapper: (p) => (
<MockParent
{...p}
mocks={[mockListApprovedStudies]}
role="User"
permissions={["access:request"]}
/>
),
});

expect(queryByRole("dialog")).not.toBeInTheDocument();
Expand All @@ -94,34 +103,16 @@ describe("Basic Functionality", () => {
describe("Implementation Requirements", () => {
it("should have a button with the text content 'Request Access'", async () => {
const { getByText } = render(<AccessRequest />, {
wrapper: (p) => <MockParent {...p} mocks={[]} role="User" />,
wrapper: (p) => <MockParent {...p} mocks={[]} role="User" permissions={["access:request"]} />,
});

expect(getByText("Request Access")).toBeInTheDocument();
expect(getByText("Request Access")).toBeEnabled();
});

it.each<UserRole>(["User", "Submitter", "Organization Owner"])(
"should render the 'Request Access' button for the '%s' role",
(role) => {
const { getByTestId } = render(<AccessRequest />, {
wrapper: (p) => <MockParent {...p} mocks={[]} role={role} />,
});

expect(getByTestId("request-access-button")).toBeInTheDocument();
}
);

it.each<UserRole>([
"Admin",
"Data Commons POC",
"Federal Lead",
"Federal Monitor",
"Data Curator",
"fake role" as UserRole,
])("should not render the 'Request Access' button for the '%s' role", (role) => {
it("should not render the 'Request Access' button without the required permission", async () => {
const { queryByTestId } = render(<AccessRequest />, {
wrapper: (p) => <MockParent {...p} mocks={[]} role={role} />,
wrapper: (p) => <MockParent {...p} mocks={[]} role="User" permissions={[]} />,
});

expect(queryByTestId("request-access-button")).not.toBeInTheDocument();
Expand Down
4 changes: 2 additions & 2 deletions src/components/AccessRequest/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC, memo, useState } from "react";
import { Button, styled } from "@mui/material";
import FormDialog from "./FormDialog";
import { useAuthContext } from "../Contexts/AuthContext";
import { CanRequestRoleChange } from "../../config/AuthRoles";
import { hasPermission } from "../../config/AuthPermissions";

const StyledButton = styled(Button)({
marginLeft: "42px",
Expand Down Expand Up @@ -38,7 +38,7 @@ const AccessRequest: FC = (): React.ReactNode => {
setDialogOpen(false);
};

if (!user?.role || !CanRequestRoleChange.includes(user.role)) {
if (!hasPermission(user, "access", "request")) {
return null;
}

Expand Down
83 changes: 11 additions & 72 deletions src/components/Collaborators/CollaboratorsDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const mockUser: User = {
userStatus: "Active",
updateAt: "",
createdAt: "",
permissions: ["data_submission:view", "data_submission:create"],
notifications: [],
};

const mockSubmission = {
Expand Down Expand Up @@ -182,10 +184,7 @@ describe("CollaboratorsDialog Component", () => {

it("calls onClose when Close button is clicked", () => {
mockUseAuthContext.mockReturnValue({
user: {
...mockUser,
role: "Admin",
},
user: { ...mockUser, _id: "some-other-user" } as User,
status: AuthStatus.LOADED,
});

Expand Down Expand Up @@ -367,20 +366,12 @@ describe("CollaboratorsDialog Component", () => {
});
});

it.each<UserRole>([
"User",
"Admin",
"Data Curator",
"Data Commons POC",
"Federal Lead",
"Federal Monitor",
"invalid-role" as UserRole,
])("should disable inputs when user is role %s", (role) => {
it("should disable inputs when user does not have required permissions", async () => {
mockUseAuthContext.mockReturnValue({
user: {
...mockUser,
role,
},
permissions: ["data_submission:view"],
} as User,
status: AuthStatus.LOADED,
});

Expand All @@ -396,76 +387,24 @@ describe("CollaboratorsDialog Component", () => {
expect(getByTestId("collaborators-dialog-close-button")).toBeInTheDocument();
});

it.each<UserRole>(["Submitter", "Organization Owner"])(
"should enable inputs when user is role %s",
(role) => {
mockUseAuthContext.mockReturnValue({
user: {
...mockUser,
role,
},
status: AuthStatus.LOADED,
});

const mockOnClose = jest.fn();
const { getByTestId, queryByTestId } = render(
<TestParent>
<CollaboratorsDialog open onClose={mockOnClose} onSave={jest.fn()} />
</TestParent>
);

expect(getByTestId("collaborators-dialog-save-button")).toBeInTheDocument();
expect(getByTestId("collaborators-dialog-cancel-button")).toBeInTheDocument();
expect(queryByTestId("collaborators-dialog-close-button")).not.toBeInTheDocument();
}
);

it("allows modification when user is Organization Owner regardless of submitterID", () => {
mockUseAuthContext.mockReturnValue({
user: { ...mockUser, role: "Organization Owner", _id: "user-99" },
status: AuthStatus.LOADED,
});

mockUseSubmissionContext.mockReturnValue({
data: { getSubmission: { ...mockSubmission, submitterID: "user-1" } },
});

const mockOnClose = jest.fn();
const { getByTestId, queryByTestId } = render(
<TestParent>
<CollaboratorsDialog open onClose={mockOnClose} onSave={jest.fn()} />
</TestParent>
);

expect(getByTestId("collaborators-dialog-save-button")).toBeInTheDocument();
expect(getByTestId("collaborators-dialog-cancel-button")).toBeInTheDocument();
expect(queryByTestId("collaborators-dialog-close-button")).not.toBeInTheDocument();
});

it("should not allow modification when user is Organization Owner of a different organization", () => {
it("should enable inputs when user has the required permissions", async () => {
mockUseAuthContext.mockReturnValue({
user: {
...mockUser,
role: "Organization Owner",
_id: "user-99",
organization: { orgID: "some-other-org" },
permissions: ["data_submission:view", "data_submission:create"],
} as User,
status: AuthStatus.LOADED,
});

mockUseSubmissionContext.mockReturnValue({
data: { getSubmission: { ...mockSubmission, submitterID: "user-1" } },
});

const mockOnClose = jest.fn();
const { getByTestId, queryByTestId } = render(
<TestParent>
<CollaboratorsDialog open onClose={mockOnClose} onSave={jest.fn()} />
</TestParent>
);

expect(queryByTestId("collaborators-dialog-save-button")).not.toBeInTheDocument();
expect(queryByTestId("collaborators-dialog-cancel-button")).not.toBeInTheDocument();
expect(getByTestId("collaborators-dialog-close-button")).toBeInTheDocument();
expect(getByTestId("collaborators-dialog-save-button")).toBeInTheDocument();
expect(getByTestId("collaborators-dialog-cancel-button")).toBeInTheDocument();
expect(queryByTestId("collaborators-dialog-close-button")).not.toBeInTheDocument();
});
});
10 changes: 4 additions & 6 deletions src/components/Collaborators/CollaboratorsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { ReactComponent as CloseIconSvg } from "../../assets/icons/close_icon.sv
import CollaboratorsTable from "./CollaboratorsTable";
import { useCollaboratorsContext } from "../Contexts/CollaboratorsContext";
import { useSubmissionContext } from "../Contexts/SubmissionContext";
import { canModifyCollaboratorsRoles } from "../../config/AuthRoles";
import { Status as AuthStatus, useAuthContext } from "../Contexts/AuthContext";
import { hasPermission } from "../../config/AuthPermissions";

const StyledDialog = styled(Dialog)({
"& .MuiDialog-paper": {
Expand Down Expand Up @@ -111,11 +111,9 @@ const CollaboratorsDialog = ({ onClose, onSave, open, ...rest }: Props) => {
const isLoading = collaboratorLoading || status === AuthStatus.LOADING;
const canModifyCollaborators = useMemo(
() =>
canModifyCollaboratorsRoles.includes(user?.role) &&
(submission?.getSubmission?.submitterID === user?._id ||
(user?.role === "Organization Owner" &&
user?.organization?.orgID === submission?.getSubmission?.organization?._id)),
[canModifyCollaboratorsRoles, user, submission?.getSubmission]
hasPermission(user, "data_submission", "create") &&
submission?.getSubmission?.submitterID === user?._id,
[user, submission?.getSubmission]
);

useEffect(() => {
Expand Down
2 changes: 2 additions & 0 deletions src/components/Collaborators/CollaboratorsTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const mockUser: User = {
userStatus: "Active",
updateAt: "",
createdAt: "",
permissions: ["data_submission:create"],
notifications: [],
};

const mockSubmission: Submission = {
Expand Down
Loading
Loading