CEC-2977 Filter portal access based on auth provider (#231)
* fix/fleet-vehicles-search * fix/fleet-name-update * Update hasRole logic, create RoleWrap component * Add MAGNA and MAGNAGROUP env vars * Add Permissions Co-authored-by: jwu-fisker <jwu@fiskerinc.com>
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
import React from "react";
|
||||
import { List } from "@material-ui/core";
|
||||
import HomeIcon from "@material-ui/icons/Home";
|
||||
import DirectionsCarIcon from "@material-ui/icons/DirectionsCar";
|
||||
import CommuteIcon from "@material-ui/icons/Commute";
|
||||
import CloudDownloadIcon from "@material-ui/icons/CloudDownload";
|
||||
import AssessmentIcon from "@material-ui/icons/Assessment";
|
||||
import BuildIcon from "@material-ui/icons/Build";
|
||||
import CloudDownloadIcon from "@material-ui/icons/CloudDownload";
|
||||
import CommuteIcon from "@material-ui/icons/Commute";
|
||||
import DirectionsCarIcon from "@material-ui/icons/DirectionsCar";
|
||||
import HomeIcon from "@material-ui/icons/Home";
|
||||
import SettingsInputCompositeIcon from "@material-ui/icons/SettingsInputComposite";
|
||||
import { default as React, useEffect, useState } from "react";
|
||||
|
||||
import { hasRole, Permissions } from "../../utils/roles";
|
||||
import { useUserContext } from "../Contexts/UserContext";
|
||||
import { Roles, hasRole } from "../../utils/roles";
|
||||
import { ExpandableSideMenuItem, MenuItem } from "./MenuItem";
|
||||
import SupersetDashboardList from "../SupersetDashboardList/SupersetDashboardList";
|
||||
import { ExpandableSideMenuItem, MenuItem } from "./MenuItem";
|
||||
|
||||
const menuData = [
|
||||
{
|
||||
@@ -24,65 +24,60 @@ const menuData = [
|
||||
label: "Deployments",
|
||||
to: "/packages",
|
||||
icon: <CloudDownloadIcon />,
|
||||
roles: [Roles.READ, Roles.CREATE],
|
||||
rolesPerProvider: Permissions.FiskerMagnaRead,
|
||||
},
|
||||
{
|
||||
label: "Vehicles",
|
||||
to: "/vehicles",
|
||||
icon: <DirectionsCarIcon />,
|
||||
roles: [Roles.READ, Roles.CREATE],
|
||||
rolesPerProvider: Permissions.FiskerMagnaRead,
|
||||
},
|
||||
{
|
||||
label: "Fleets",
|
||||
to: "/fleets",
|
||||
icon: <CommuteIcon />,
|
||||
roles: [Roles.READ, Roles.CREATE],
|
||||
rolesPerProvider: Permissions.FiskerRead,
|
||||
},
|
||||
{
|
||||
label: "Datascope",
|
||||
to: null,
|
||||
icon: <AssessmentIcon />,
|
||||
roles: [Roles.READ, Roles.CREATE],
|
||||
component: SupersetDashboardList
|
||||
rolesPerProvider: Permissions.FiskerRead,
|
||||
component: SupersetDashboardList,
|
||||
},
|
||||
{
|
||||
label: "Suppliers",
|
||||
to: "/suppliers",
|
||||
icon: <SettingsInputCompositeIcon />,
|
||||
roles: [Roles.APPROVESUPPLIERS],
|
||||
rolesPerProvider: Permissions.FiskerSupplierAdmin,
|
||||
},
|
||||
{
|
||||
label: "Tools",
|
||||
to: "/tools/certificates/add",
|
||||
icon: <BuildIcon />,
|
||||
roles: [Roles.CERTIFICATES, Roles.APPROVESUPPLIERS],
|
||||
rolesPerProvider: Permissions.FiskerTools,
|
||||
submenus: [
|
||||
{
|
||||
label: "Certificate",
|
||||
to: "/tools/certificates/add",
|
||||
roles: [Roles.CERTIFICATES],
|
||||
rolesPerProvider: Permissions.FiskerCertificate,
|
||||
},
|
||||
{
|
||||
label: "SMS",
|
||||
to: "/tools/sms/send",
|
||||
roles: [Roles.CREATE],
|
||||
}
|
||||
rolesPerProvider: Permissions.FiskerCreate,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
const SideMenu = () => {
|
||||
const { groups } = useUserContext();
|
||||
|
||||
const menu = menuData.reduce((result, item) => {
|
||||
if (hasRole(item.roles, groups)) {
|
||||
result.push(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
const { groups, providers } = useUserContext();
|
||||
const [menu, setMenu] = useState(menuData);
|
||||
|
||||
useEffect(() => {
|
||||
filterAccessible(groups, providers, setMenu);
|
||||
}, [groups, providers]);
|
||||
|
||||
return (
|
||||
<List>
|
||||
@@ -96,4 +91,14 @@ const SideMenu = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SideMenu;
|
||||
const filterAccessible = (groups, providers, setMenu) => {
|
||||
const filteredMenu = menuData.reduce((result, item) => {
|
||||
if (hasRole(groups, item.rolesPerProvider, providers)) {
|
||||
result.push(item);
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
setMenu(filteredMenu);
|
||||
};
|
||||
|
||||
export default SideMenu;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
jest.mock("../Contexts/UserContext");
|
||||
jest.mock("../../services/superset")
|
||||
jest.mock("../../services/superset");
|
||||
|
||||
import { render, waitFor, screen } from "@testing-library/react";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { UserProvider, setToken } from "../Contexts/UserContext";
|
||||
import { TEST_AUTH_OBJECT } from "../../utils/testing";
|
||||
import SideMenu from "./SideMenu";
|
||||
import addSnapshotSerializer from "../../utils/snapshot";
|
||||
import { TEST_AUTH_OBJECT_FISKER } from "../../utils/testing";
|
||||
import { setToken, UserProvider } from "../Contexts/UserContext";
|
||||
import SideMenu from "./SideMenu";
|
||||
|
||||
const renderMenu = async () => {
|
||||
const { container } = render(
|
||||
@@ -23,15 +23,20 @@ const renderMenu = async () => {
|
||||
describe("SideMenu", () => {
|
||||
beforeAll(() => {
|
||||
addSnapshotSerializer(expect);
|
||||
});
|
||||
|
||||
it("Unauthenticated", async () => {
|
||||
setToken({ idToken: { jwtToken: null } });
|
||||
const container = await renderMenu();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("Authenticated", async () => {
|
||||
setToken(TEST_AUTH_OBJECT);
|
||||
setToken(TEST_AUTH_OBJECT_FISKER);
|
||||
const container = await renderMenu();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Datascope')).toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByText("Datascope")).toBeInTheDocument();
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -213,6 +213,42 @@ exports[`SideMenu Authenticated 1`] = `
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||
href="/suppliers"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="MuiListItemIcon-root"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M5 2c0-.55-.45-1-1-1s-1 .45-1 1v4H1v6h6V6H5V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2H9v2zm-8 0c0 1.3.84 2.4 2 2.82V23h2v-4.18C6.16 18.4 7 17.3 7 16v-2H1v2zM21 6V2c0-.55-.45-1-1-1s-1 .45-1 1v4h-2v6h6V6h-2zm-8-4c0-.55-.45-1-1-1s-1 .45-1 1v4H9v6h6V6h-2V2zm4 14c0 1.3.84 2.4 2 2.82V23h2v-4.18c1.16-.41 2-1.51 2-2.82v-2h-6v2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="MuiListItemText-root"
|
||||
>
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Suppliers
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<span>
|
||||
<li>
|
||||
<a
|
||||
@@ -303,3 +339,52 @@ exports[`SideMenu Authenticated 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`SideMenu Unauthenticated 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-testid="mocked-userprovider"
|
||||
>
|
||||
<ul
|
||||
class="MuiList-root MuiList-padding"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiListItem-root MuiListItem-gutters MuiListItem-button"
|
||||
href="/home"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="MuiListItemIcon-root"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="MuiListItemText-root"
|
||||
>
|
||||
<span
|
||||
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
|
||||
>
|
||||
Home
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user