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:
arpanetus
2022-11-09 06:46:33 +06:00
committed by GitHub
parent 94950d583e
commit f2f046968e
76 changed files with 1321 additions and 917 deletions

View File

@@ -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;

View File

@@ -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();
});
});

View File

@@ -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>
`;