diff --git a/.env.dev b/.env.dev index 5c21f22..3a7990a 100644 --- a/.env.dev +++ b/.env.dev @@ -2,3 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/compute_auth REACT_APP_UPLOAD_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://dev-ota-admin.cloud.fiskerinc.com REACT_APP_SUPERSET_URL=http://superset-dev.fisker.internal +REACT_APP_CERT_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/certificate diff --git a/.env.local b/.env.local index 2d4b6dd..234f2fa 100644 --- a/.env.local +++ b/.env.local @@ -2,3 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth REACT_APP_UPLOAD_SERVICE_URL=http://localhost/ota_update REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000 REACT_APP_SUPERSET_URL=http://superset-dev.fisker.internal +REACT_APP_CERT_SERVICE_URL=http://localhost/certificate diff --git a/.env.prd b/.env.prd index d7fa640..d89cfe0 100644 --- a/.env.prd +++ b/.env.prd @@ -2,3 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://gw.cloud.fiskerinc.com/compute_auth REACT_APP_UPLOAD_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cloud.fiskerinc.com REACT_APP_SUPERSET_URL=http://superset.fisker.internal +REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate diff --git a/.env.stg b/.env.stg index b6162e6..3903193 100644 --- a/.env.stg +++ b/.env.stg @@ -2,3 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/compute_auth REACT_APP_UPLOAD_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://stg-ota-admin.cloud.fiskerinc.com REACT_APP_SUPERSET_URL=http://superset-stg.fisker.internal +REACT_APP_CERT_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/certificate diff --git a/.env.template b/.env.template index 2d4b6dd..234f2fa 100644 --- a/.env.template +++ b/.env.template @@ -2,3 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth REACT_APP_UPLOAD_SERVICE_URL=http://localhost/ota_update REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000 REACT_APP_SUPERSET_URL=http://superset-dev.fisker.internal +REACT_APP_CERT_SERVICE_URL=http://localhost/certificate diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js index 2cc4738..9a9157b 100644 --- a/src/components/App/App.test.js +++ b/src/components/App/App.test.js @@ -37,22 +37,35 @@ const check = async (path, selector, compare) => { const sleepAndCheck = async (path, selector, compare) => { const container = await renderRoute(path); - await waitFor(() => { }); + await waitFor(() => {}); expect(container.querySelector(selector).innerHTML).toEqual(compare); expect(container).toMatchSnapshot(); }; describe("App", () => { + const rxMakeStyles = /makeStyles-(\w+)-(\d+)/gi; + beforeAll(() => { // Stablize Table Pagination control ids expect.addSnapshotSerializer({ test: function (val) { - return val && typeof val === "string" && val.indexOf("mui-") >= 0; + return val && typeof val === "string" && val.indexOf("mui-") > -1; }, print: function (val) { let str = val; str = str.replace(/mui-\d*/g, "mui-00000"); + return `"${str}"`; + }, + }); + expect.addSnapshotSerializer({ + test: (val) => { + return val && typeof val === "string" && val.search(rxMakeStyles) > -1; + }, + print: function (val) { + let str = val; + str = str.replace(rxMakeStyles, "makeStyles-$1-0000"); + return `"${str}"`; }, }); @@ -107,6 +120,10 @@ describe("App", () => { ); }); + it("Route /tools/certificates/add unauthenticated", async () => { + await check("/tools/certificates/add", "span.MuiButton-label", "Sign In"); + }); + it("Route /page-not-found unauthenticated", async () => { await check("/page-not-found", "h1", "Page Not Found"); }); @@ -159,4 +176,9 @@ describe("App", () => { setToken(TEST_AUTH_OBJECT); await check("/vehicle-status/FISKER123", "h6", "Vehicle FISKER123 Details"); }); + + it("Route /tools/certificates/add authenticated", async () => { + setToken(TEST_AUTH_OBJECT); + await check("/tools/certificates/add", "h6", "Create Certificate"); + }); }); diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index f041185..097aefb 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -6,10 +6,10 @@ exports[`App Route / authenticated 1`] = ` data-testid="mocked-userprovider" >
Fisker Admin Portal
@@ -212,7 +212,7 @@ exports[`App Route / authenticated 1`] = `
  • + +
  • + +
    + +
    +
    + + Tools + +
    + +
    +
  • +
    +
    @@ -440,10 +504,10 @@ exports[`App Route /home authenticated 1`] = ` data-testid="mocked-userprovider" >
    @@ -874,10 +1002,10 @@ exports[`App Route /package-create authenticated 1`] = ` data-testid="mocked-userprovider" >
    VIN sorted ascending @@ -3043,10 +3299,10 @@ exports[`App Route /package-deploy unauthenticated 1`] = ` data-testid="mocked-userprovider" >
    ID sorted ascending @@ -4226,6 +4610,23 @@ exports[`App Route /packages authenticated 1`] = ` /> + + + @@ -4370,10 +4771,10 @@ exports[`App Route /packages unauthenticated 1`] = ` data-testid="mocked-userprovider" >
    @@ -4426,10 +4827,10 @@ exports[`App Route /page-not-found authenticated 1`] = ` data-testid="mocked-userprovider" >

    `; +exports[`App Route /tools/certificates/add authenticated 1`] = ` +
    +
    +
    +
    +
    +
    +
    + Create Certificate +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + +
    + +
    + + +
    +
    + +
    + + + +
    + + +
    +
    +
    +
    +
    +
    +`; + +exports[`App Route /tools/certificates/add unauthenticated 1`] = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +`; + exports[`App Route /vehicle-add authenticated 1`] = `
    Fisker Admin Portal
    @@ -4987,7 +6111,7 @@ exports[`App Route /vehicle-add authenticated 1`] = `
  • + +
  • + +
    + +
    +
    + + Tools + +
    + +
    +
  • +
    +

    VIN sorted ascending @@ -7093,10 +8409,10 @@ exports[`App Route /vehicles unauthenticated 1`] = ` data-testid="mocked-userprovider" > + + + + + + diff --git a/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap b/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap index f2dd2d2..9262594 100644 --- a/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap +++ b/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap @@ -193,6 +193,23 @@ exports[`CANFiltersTab Render 1`] = ` /> + + + + + + + + + diff --git a/src/components/Certificates/Add/CreateForm.jsx b/src/components/Certificates/Add/CreateForm.jsx new file mode 100644 index 0000000..4a67e96 --- /dev/null +++ b/src/components/Certificates/Add/CreateForm.jsx @@ -0,0 +1,90 @@ +import React, { useRef, useState } from "react"; +import { + Button, + FormControlLabel, + FormLabel, + Radio, + RadioGroup, + TextField, +} from "@material-ui/core"; + +import useStyles from "../../useStyles"; +import { CertTypes } from "../../Contexts/CertificateContext"; + +const CreateForm = ({ onCreate, busy }) => { + const classes = useStyles(); + const vinEl = useRef(null); + const [certType, setCertType] = useState(CertTypes.TREX); + + const onSubmit = async (event) => { + event.preventDefault(); + + if (onCreate) + onCreate({ + vin: vinEl.current.value, + type: certType, + }); + }; + + const onCertTypeChange = (event) => { + setCertType(event.target.value); + }; + + return ( +
    + + + Type + + } + label="TREX" + /> + } + label="HMI" + /> + } + label="Charging" + /> + + + +
    + ); +}; + +export default CreateForm; diff --git a/src/components/Certificates/Add/DownloadCerts.jsx b/src/components/Certificates/Add/DownloadCerts.jsx new file mode 100644 index 0000000..4e206c9 --- /dev/null +++ b/src/components/Certificates/Add/DownloadCerts.jsx @@ -0,0 +1,50 @@ +import { Button } from "@material-ui/core"; +import React from "react"; + +import DownloadFileLink from "../../Controls/DownloadFileLink"; +import useStyles from "../../useStyles"; + +const CertMimeType = "application/x-pem-file"; + +const DownloadCerts = ({ vin, publicCert, privateCert, onChangeView }) => { + const classes = useStyles(); + + const onNewCert = (event) => { + event.preventDefault(); + if (!onChangeView) return; + onChangeView(); + }; + return ( +
    +

    Download Certifcates

    +
      +
    • + +
    • +
    • + +
    • +
    + +
    + ); +}; + +export default DownloadCerts; diff --git a/src/components/Certificates/Add/DownloadCerts.test.jsx b/src/components/Certificates/Add/DownloadCerts.test.jsx new file mode 100644 index 0000000..0b4af34 --- /dev/null +++ b/src/components/Certificates/Add/DownloadCerts.test.jsx @@ -0,0 +1,25 @@ +import React from "react"; +import { render, waitFor } from "@testing-library/react"; + +import DownloadCerts from "./DownloadCerts"; + +describe("DownloadCerts", () => { + beforeAll(() => { + global.URL.createObjectURL = jest.fn(); + global.URL.revokeObjectURL = jest.fn(); + }); + + it("Render", async () => { + const { container } = render( + + ); + await waitFor(() => { + /* render */ + }); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Certificates/Add/__snapshots__/DownloadCerts.test.jsx.snap b/src/components/Certificates/Add/__snapshots__/DownloadCerts.test.jsx.snap new file mode 100644 index 0000000..024f045 --- /dev/null +++ b/src/components/Certificates/Add/__snapshots__/DownloadCerts.test.jsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DownloadCerts Render 1`] = ` +
    +
    +

    + Download Certifcates +

    + + +
    +
    +`; diff --git a/src/components/Certificates/Add/index.jsx b/src/components/Certificates/Add/index.jsx new file mode 100644 index 0000000..6cf9ed0 --- /dev/null +++ b/src/components/Certificates/Add/index.jsx @@ -0,0 +1,85 @@ +import React, { useEffect, useState } from "react"; + +import { + useCertificateContext, + CertificateProvider, +} from "../../Contexts/CertificateContext"; +import { useStatusContext } from "../../Contexts/StatusContext"; +import { useUserContext } from "../../Contexts/UserContext"; +import { logger } from "../../../services/monitoring"; +import CreateForm from "./CreateForm"; +import DownloadCerts from "./DownloadCerts"; + +const VIEW_FORM = 0; +const VIEW_DOWNLOAD = 1; + +const MainForm = () => { + const { busy, createCert } = useCertificateContext(); + const { setMessage, setTitle, setSitePath } = useStatusContext(); + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + const [view, setView] = useState(VIEW_FORM); + const [pubCert, setPubCert] = useState(null); + const [privCert, setPrivCert] = useState(null); + const [vin, setVIN] = useState(null); + + useEffect(() => { + setTitle("Create Certificate"); + setSitePath([ + { + label: "Tools", + link: "/tools/certificates/add", + }, + { + label: "Create Certificate", + }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onCreate = async (data) => { + try { + const result = await createCert(data, token); + + setPubCert(result.public_key); + setPrivCert(result.private_key); + setVIN(data.vin); + setMessage(`Created ${data.vin} certificate`); + setView(VIEW_DOWNLOAD); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + const onChangeView = () => { + setPubCert(null); + setPrivCert(null); + setVIN(null); + + setView(VIEW_FORM); + }; + + if (view === VIEW_DOWNLOAD) + return ( + + ); + + return ; +}; + +const CertificateCreate = () => ( + + + +); + +export default CertificateCreate; diff --git a/src/components/Contexts/CertificateContext.jsx b/src/components/Contexts/CertificateContext.jsx new file mode 100644 index 0000000..6f27396 --- /dev/null +++ b/src/components/Contexts/CertificateContext.jsx @@ -0,0 +1,50 @@ +import React, { useContext, useState } from "react"; + +import api from "../../services/certificatesAPI"; + +const CertificateContext = React.createContext(); + +export const CertTypes = { + TREX: "TREX", + HMI: "HMI", + Charging: "CHARGE", +}; + +const validateCreate = (data) => { + if (!data.type) throw new Error("type is required"); + if (!data.vin) throw new Error("vin is required"); +}; + +export const CertificateProvider = ({ children }) => { + const [busy, setBusy] = useState(false); + + const createCert = async (data, token) => { + try { + setBusy(true); + + validateCreate(data); + + const result = await api.create(data, token); + if (result.error) { + throw new Error(`Create certificate error. ${result.message}`); + } + + return result; + } finally { + setBusy(false); + } + }; + + return ( + + {children} + + ); +}; + +export const useCertificateContext = () => useContext(CertificateContext); diff --git a/src/components/Controls/DownloadFileLink/__snapshots__/index.test.jsx.snap b/src/components/Controls/DownloadFileLink/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..cdcdea8 --- /dev/null +++ b/src/components/Controls/DownloadFileLink/__snapshots__/index.test.jsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DownloadFileLink Render 1`] = ` + +`; diff --git a/src/components/Controls/DownloadFileLink/index.jsx b/src/components/Controls/DownloadFileLink/index.jsx new file mode 100644 index 0000000..1ebfd90 --- /dev/null +++ b/src/components/Controls/DownloadFileLink/index.jsx @@ -0,0 +1,32 @@ +import React, { useEffect, useState } from "react"; + +const DownloadFileLink = ({ data, filename, mimetype }) => { + const [link, setLink] = useState(""); + + const releaseLink = () => { + if (link === "") return; + URL.revokeObjectURL(link); + }; + + const makeFile = () => { + const file = new Blob([data], { type: mimetype ?? "text/plain" }); + + releaseLink(); + setLink(URL.createObjectURL(file)); + }; + + useEffect(() => { + if (!data) return; + makeFile(); + return releaseLink; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data, filename, mimetype]); + + return ( + + {filename} + + ); +}; + +export default DownloadFileLink; diff --git a/src/components/Controls/DownloadFileLink/index.test.jsx b/src/components/Controls/DownloadFileLink/index.test.jsx new file mode 100644 index 0000000..64fad29 --- /dev/null +++ b/src/components/Controls/DownloadFileLink/index.test.jsx @@ -0,0 +1,21 @@ +import React from "react"; +import { render, waitFor } from "@testing-library/react"; + +import DownloadFileLink from "."; + +describe("DownloadFileLink", () => { + beforeAll(() => { + global.URL.createObjectURL = jest.fn(); + global.URL.revokeObjectURL = jest.fn(); + }); + + it("Render", async () => { + const { container } = render( + + ); + await waitFor(() => { + /* render */ + }); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Status/CANFilters/Table/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/CANFilters/Table/__snapshots__/index.test.jsx.snap index b695e50..5822db7 100644 --- a/src/components/Fleets/Status/CANFilters/Table/__snapshots__/index.test.jsx.snap +++ b/src/components/Fleets/Status/CANFilters/Table/__snapshots__/index.test.jsx.snap @@ -195,6 +195,23 @@ exports[`FleetCANFiltersTable Render 1`] = ` /> + + +
    + + + + + + diff --git a/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap index 2456483..ac685b2 100644 --- a/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap +++ b/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap @@ -153,7 +153,23 @@ exports[`FleetVehiclesTable Render 1`] = ` - No actions + + + - No actions + + + diff --git a/src/components/Fleets/Status/__snapshots__/CANFiltersTab.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/CANFiltersTab.test.jsx.snap index 8bed748..5c29255 100644 --- a/src/components/Fleets/Status/__snapshots__/CANFiltersTab.test.jsx.snap +++ b/src/components/Fleets/Status/__snapshots__/CANFiltersTab.test.jsx.snap @@ -194,6 +194,23 @@ exports[`CANFiltersTab Render 1`] = ` /> + + + + + + + + + diff --git a/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap index cea9afa..ccd495f 100644 --- a/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap +++ b/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap @@ -152,7 +152,23 @@ exports[`VehiclesTab Render 1`] = ` - No actions + + + - No actions + + + diff --git a/src/components/Layouts/SideMenu.jsx b/src/components/Layouts/SideMenu.jsx index cde94da..77f2c49 100644 --- a/src/components/Layouts/SideMenu.jsx +++ b/src/components/Layouts/SideMenu.jsx @@ -1,10 +1,11 @@ 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 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 ListItemLink from "../ListItemLink"; import ListItemExternalLink from "../ListItemExternalLink"; @@ -43,6 +44,19 @@ const menuData = [ icon: , roles: [Roles.READ, Roles.CREATE], }, + { + label: "Tools", + to: "/tools/certificates/add", + icon: , + roles: [Roles.CERTIFICATES], + submenus: [ + { + label: "Certificate", + to: "/tools/certificates/add", + roles: [Roles.CERTIFICATES], + }, + ], + }, ]; const MenuItem = ({ item, children }) => { diff --git a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap index 387ba7f..d40f672 100644 --- a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap +++ b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap @@ -190,6 +190,70 @@ exports[`SideMenu Authenticated 1`] = ` /> + +
  • + +
    + +
    +
    + + Tools + +
    + +
    +
  • +
    + diff --git a/src/components/Routes/SiteRoutes.jsx b/src/components/Routes/SiteRoutes.jsx index 513757e..54c7086 100644 --- a/src/components/Routes/SiteRoutes.jsx +++ b/src/components/Routes/SiteRoutes.jsx @@ -6,8 +6,8 @@ import { MessageBar } from "../MessageBar"; import { useUserContext } from "../Contexts/UserContext"; import { Roles } from "../../utils/roles"; -const CANFilterCreate = React.lazy(() => import("../CANFilter/Add")) -const CANFilterUpdate = React.lazy(() => import("../CANFilter/Update")) +const CANFilterCreate = React.lazy(() => import("../CANFilter/Add")); +const CANFilterUpdate = React.lazy(() => import("../CANFilter/Update")); const CarsList = React.lazy(() => import("../Cars/List")); const CarStatus = React.lazy(() => import("../Cars/Status")); const CarUpdateStatus = React.lazy(() => import("../Cars/UpdateStatus")); @@ -15,9 +15,15 @@ const FleetsList = React.lazy(() => import("../Fleets/Table")); const FleetStatus = React.lazy(() => import("../Fleets/Status")); const FleetAddForm = React.lazy(() => import("../Fleets/Add")); const FleetUpdateForm = React.lazy(() => import("../Fleets/Update")); -const FleetAddVehicleForm = React.lazy(() => import("../Fleets/Status/Vehicles/Add")); -const FleetAddCANFilterForm = React.lazy(() => import("../Fleets/Status/CANFilters/Add")); -const FleetUpdateCANFilterForm = React.lazy(() => import("../Fleets/Status/CANFilters/Update")); +const FleetAddVehicleForm = React.lazy(() => + import("../Fleets/Status/Vehicles/Add") +); +const FleetAddCANFilterForm = React.lazy(() => + import("../Fleets/Status/CANFilters/Add") +); +const FleetUpdateCANFilterForm = React.lazy(() => + import("../Fleets/Status/CANFilters/Update") +); const Home = React.lazy(() => import("../Home")); const Manifests = React.lazy(() => import("../Manifest/List")); const ManifestDeploy = React.lazy(() => import("../Manifest/Deploy")); @@ -26,7 +32,8 @@ const ManifestCreate = React.lazy(() => import("../Manifest/Create")); const PageNotFound = React.lazy(() => import("../404")); const SSOForm = React.lazy(() => import("../SSOForm")); const VehicleAddForm = React.lazy(() => import("../Cars/Add")); -const VehicleUpdateForm = React.lazy(() => import("../Cars/Update")) +const VehicleUpdateForm = React.lazy(() => import("../Cars/Update")); +const CertificateCreate = React.lazy(() => import("../Certificates/Add")); const SiteRoutes = () => { const { token, groups } = useUserContext(); @@ -191,6 +198,14 @@ const SiteRoutes = () => { groups={groups} roles={[Roles.CREATE]} /> + } + type={TYPES.PROTECTED} + token={token} + groups={groups} + roles={[Roles.CERTIFICATES]} + /> diff --git a/src/services/certificatesAPI.js b/src/services/certificatesAPI.js new file mode 100644 index 0000000..7527b4b --- /dev/null +++ b/src/services/certificatesAPI.js @@ -0,0 +1,17 @@ +import { getAuthHeaderOptions, fetchRespHandler } from "../utils/http"; + +const API_ENDPOINT = process.env.REACT_APP_CERT_SERVICE_URL; + +const certificatesAPI = { + create: async (data, token) => + fetch(`${API_ENDPOINT}/create`, { + method: "POST", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + body: JSON.stringify(data), + }).then(fetchRespHandler), +}; + +export default certificatesAPI; diff --git a/src/utils/roles.js b/src/utils/roles.js index 5932fe9..3207034 100644 --- a/src/utils/roles.js +++ b/src/utils/roles.js @@ -3,8 +3,9 @@ import { parsePayload } from "./jwt"; export const Roles = { READ: "a729bbd4-2038-4649-9127-16782bb1e701", CREATE: "efcc3025-e2d8-4212-8227-805c7be39d2c", - DELETE: "8f78dce7-f5f9-4033-a10c-c9c7408bfcfe" -} + DELETE: "8f78dce7-f5f9-4033-a10c-c9c7408bfcfe", + CERTIFICATES: "746f34b0-9ba0-4b5d-8d84-0256a9c8e390", +}; export const hasRoleToken = (roles, token) => { if (!roles || roles.length === 0) return true; @@ -14,7 +15,7 @@ export const hasRoleToken = (roles, token) => { if (!groups) return false; return hasRole(roles, groups); -} +}; export const getGroups = (token) => { const payload = parsePayload(token); @@ -22,7 +23,7 @@ export const getGroups = (token) => { if (!payload || !payload["custom:groups"]) return null; return payload["custom:groups"]; -} +}; export const hasRole = (roles, groups) => { if (!roles || roles.length === 0) return true; @@ -33,4 +34,4 @@ export const hasRole = (roles, groups) => { } return false; -} +}; diff --git a/src/utils/testing.js b/src/utils/testing.js index b4169af..6bdc7d2 100644 --- a/src/utils/testing.js +++ b/src/utils/testing.js @@ -1,8 +1,9 @@ -export const TEST_TOKEN = "eyJraWQiOiJlUTNuZFJLaUVcL084VUZ5RHFsYjN0S1RzWG00SzVPMlc4NXd3VWkzT2tNZz0iLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiOUlyV2RLaUxJU0FZUnFha1F2b2xmZyIsInN1YiI6IjJiMDk1NTY2LTllNDYtNGQ4ZS1iMTA5LTI0MTM1ZGYyMmVlNiIsImNvZ25pdG86Z3JvdXBzIjpbInVzLXdlc3QtMl9BV3dqTFh5bTJfQXp1cmVBRCJdLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTIuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0yX0FXd2pMWHltMiIsImNvZ25pdG86dXNlcm5hbWUiOiJhenVyZWFkX2p3dUBmaXNrZXJpbmMuY29tIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJjdXN0b206Z3JvdXBzIjoiWzhkODI3OGE1LTljMGUtNGM3Zi05MThhLTgxMWZkMWQyMzZlNCwgNmMzY2Y5OGQtMGFkYS00OGM2LWFlOTQtYjE3MWNmYTI3NWZjLCA1NmVmNGJlYy1kNzM5LTRkZGYtYTAwMy1lY2M4MTMwODViOGQsIGVmY2MzMDI1LWUyZDgtNDIxMi04MjI3LTgwNWM3YmUzOWQyYywgNTUxNWE5OGYtNDY2OC00MTIxLThlOGQtZmVlMjgyNTY5OWNmLCA4Njk1NmEyZi04ZDQ2LTQ3ZmYtOWIyOS1mOTkwNzlhZTNjMWQsIGM0ZDQzNjFjLTg4ODItNDdiNC04NjQxLWZkM2FiNjhhZTcyMiwgN2JjZGNkYjItMzI3OS00NGJmLWE5OTgtNzcxYmFiNGIzM2UxXSIsImF1ZCI6IjdjazJ0Zm9xYXZjNzJjNDVoaDd0Z2U0MmtkIiwiY3VzdG9tOnNlc3Npb24tZHVyYXRpb24iOiI5MDAiLCJpZGVudGl0aWVzIjpbeyJ1c2VySWQiOiJqd3VAZmlza2VyaW5jLmNvbSIsInByb3ZpZGVyTmFtZSI6IkF6dXJlQUQiLCJwcm92aWRlclR5cGUiOiJTQU1MIiwiaXNzdWVyIjoiaHR0cHM6XC9cL3N0cy53aW5kb3dzLm5ldFwvNWFhNGI2NDAtYzlmYy00YTliLWIzYTMtZDRhN2QwMDhmYjVlXC8iLCJwcmltYXJ5IjoidHJ1ZSIsImRhdGVDcmVhdGVkIjoiMTYxNDM2NDk3NDU4NSJ9XSwidG9rZW5fdXNlIjoiaWQiLCJhdXRoX3RpbWUiOjE2MTU4MjEzMDksImV4cCI6MTYxNTkyNzM0OSwiaWF0IjoxNjE1OTIzNzUwLCJmYW1pbHlfbmFtZSI6Ild1IiwiZW1haWwiOiJqd3VAZmlza2VyaW5jLmNvbSJ9.R3k-YGK0MrUdW030Xj2WxM7mdsm1tlobeDq3YRMIKMtdkJsf5qjwM_wqVPbErH-8OrFLW7YIPuMo2Rh5PCGvg4I6kL-tWfDOY4o5b5r_VdiifXov0be_ukdt5pZblhgg0dYSLmFaFZsxNjEng8-obl_FnWp6VtG1lnRGwORY3pFe88W7OM3zLMC0g-otfAEQ2KSOaV9bfUoRAaZaGlHe8ooIQx8Qoer9qYsnymK0Sk7jSZKwhtFsziSarhreHmBkCLaWBHDjc9PQDtBvO8wg1KMKmM-6oewA0xTKPtsuHxnvtVANYaR7Nqp9cbF940YRf2IK5FB7KWFtcR7Y6igLXw"; +export const TEST_TOKEN = + "eyJraWQiOiJlUTNuZFJLaUVcL084VUZ5RHFsYjN0S1RzWG00SzVPMlc4NXd3VWkzT2tNZz0iLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoieDIzcUJvUDN6d1RnLTd6T3VJRUZUZyIsInN1YiI6IjJiMDk1NTY2LTllNDYtNGQ4ZS1iMTA5LTI0MTM1ZGYyMmVlNiIsImNvZ25pdG86Z3JvdXBzIjpbInVzLXdlc3QtMl9BV3dqTFh5bTJfQXp1cmVBRCJdLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTIuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0yX0FXd2pMWHltMiIsImNvZ25pdG86dXNlcm5hbWUiOiJhenVyZWFkX2p3dUBmaXNrZXJpbmMuY29tIiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJub25jZSI6InBqUXF6WHJ0M0NzMENqY3J2UXZHWFdZLUFKTXp5bTlIT0JHazh0YzdBb0s4bC1ycEFrc0xkSUZYZm9XcnZ3MFBtTjFzNEtGamdPMnZzdDRCdEZCdC1BSDcwNkQ5VDctN3VWODQ0Z1d0MXU5SnhLQUhXZ3JDM244YV9TQy1MV3pNNExaRVhYOHFscUJyYTBEdnJxOFFEd09HLWdrRVVsQWY5ZmJoRmprMy04cyIsImN1c3RvbTpncm91cHMiOiJbMjkxNGU2N2YtZmI4NS00Yjc4LWI3OWQtNjU2ZjRmMzdmYWExLCA4ZDgyNzhhNS05YzBlLTRjN2YtOTE4YS04MTFmZDFkMjM2ZTQsIDc0NmYzNGIwLTliYTAtNGI1ZC04ZDg0LTAyNTZhOWM4ZTM5MCwgNmMzY2Y5OGQtMGFkYS00OGM2LWFlOTQtYjE3MWNmYTI3NWZjLCBhY2JkNzJjOS05ZmYzLTQ2YTgtODNiYS1jNmZhNWRmM2YyNjQsIDVlNTlhNjE5LWM4OTAtNDQ3Mi05MWMyLWQ1ZWFlNWQ4ZmExOCwgNTZlZjRiZWMtZDczOS00ZGRmLWEwMDMtZWNjODEzMDg1YjhkLCA5MjliMDQ3MC1mN2ViLTRlMTgtOWY5Ny0yMmFjMmM1OTFhMTAsIDEzMWU2MjU3LWZkYjctNDI2YS05ODI1LTFjZDkxODgwMmZiYSwgMWFjNzk0Y2MtNzZhYy00N2Y2LWJlZTYtZDY2NjY5OThmMGZkLCA4Nzc3MGFlYS05MTYyLTQwOWUtYWE3MC0xMGQwZDFkZTU5MDIsIDVkZWE2YzMyLTY1NzUtNDUyNy05MjU0LWU1MGQyN2FlNWU5MiwgYmFmYzE3YTctZWM2NC00OWQ5LWEyYTctZGFmYzI4ZGNiMDM3LCAyNDQ5YzA2Ni05MTQ2LTQ0YTctYjRlNi00ODI4MDMxZDk1OGQsIGVmY2MzMDI1LWUyZDgtNDIxMi04MjI3LTgwNWM3YmUzOWQyYywgOGY3OGRjZTctZjVmOS00MDMzLWExMGMtYzljNzQwOGJmY2ZlLCBjZjY1MzE4My1jODI5LTRlZWQtYTZjZS00NTNmYTEwMTdjZDksIDc4M2M1OTc5LWY1ZTctNGNiNi1iMTRlLWMzNTUzZGRlOTU2YSwgNTUxNWE5OGYtNDY2OC00MTIxLThlOGQtZmVlMjgyNTY5OWNmLCA4Njk1NmEyZi04ZDQ2LTQ3ZmYtOWIyOS1mOTkwNzlhZTNjMWQsIGM0ZDQzNjFjLTg4ODItNDdiNC04NjQxLWZkM2FiNjhhZTcyMiwgOTcyYWQwOTUtMTZiNy00MGFkLWE0NjQtZjVkYmY0MTdhOGNkLCA3YmNkY2RiMi0zMjc5LTQ0YmYtYTk5OC03NzFiYWI0YjMzZTFdIiwiYXVkIjoiN2NrMnRmb3FhdmM3MmM0NWhoN3RnZTQya2QiLCJjdXN0b206c2Vzc2lvbi1kdXJhdGlvbiI6IjkwMCIsImlkZW50aXRpZXMiOlt7InVzZXJJZCI6Imp3dUBmaXNrZXJpbmMuY29tIiwicHJvdmlkZXJOYW1lIjoiQXp1cmVBRCIsInByb3ZpZGVyVHlwZSI6IlNBTUwiLCJpc3N1ZXIiOiJodHRwczpcL1wvc3RzLndpbmRvd3MubmV0XC81YWE0YjY0MC1jOWZjLTRhOWItYjNhMy1kNGE3ZDAwOGZiNWVcLyIsInByaW1hcnkiOiJ0cnVlIiwiZGF0ZUNyZWF0ZWQiOiIxNjE0MzY0OTc0NTg1In1dLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTY1MDMyMjc3MywiZXhwIjoxNjUwMzI2MzczLCJpYXQiOjE2NTAzMjI3NzMsImZhbWlseV9uYW1lIjoiV3UiLCJlbWFpbCI6Imp3dUBmaXNrZXJpbmMuY29tIn0.MCWWFAERmOVcFAHNqxpFeVKVMtZAHxY-_pbEtivnq8zhKUH8psDWorz68jT1Rt8_gqrevWIwWRMWkoiZQFGbCl0uxxZqIULrj2yeMyFAx-YTBDB-hZXqeDQ5RRxyJd3y1Opc0Rtusi7ENTigY90FAoRGHsA9l6cMlXStBSC9hWzRTPliMGTiHlzJCAW78ZqwKuiteE4LdsP0ROnjqrHcSpw6j5H2DvG1LhRAbektmhpBegQ5ncVtfWAF1W-WaUgzDoTnwZll0NTYaJVn1PUX7AoDUM9wi23y7QbMJm3Pb5nt39gDxZMMpJGzTsTe7CBR5frWphV6xyWEa8irERgSnQ"; export const TEST_AUTH_OBJECT = { idToken: { jwtToken: TEST_TOKEN, }, }; export const TEST_EXPECTED_GROUPS = - "[8d8278a5-9c0e-4c7f-918a-811fd1d236e4, 6c3cf98d-0ada-48c6-ae94-b171cfa275fc, 56ef4bec-d739-4ddf-a003-ecc813085b8d, efcc3025-e2d8-4212-8227-805c7be39d2c, 5515a98f-4668-4121-8e8d-fee2825699cf, 86956a2f-8d46-47ff-9b29-f99079ae3c1d, c4d4361c-8882-47b4-8641-fd3ab68ae722, 7bcdcdb2-3279-44bf-a998-771bab4b33e1]"; + "[2914e67f-fb85-4b78-b79d-656f4f37faa1, 8d8278a5-9c0e-4c7f-918a-811fd1d236e4, 746f34b0-9ba0-4b5d-8d84-0256a9c8e390, 6c3cf98d-0ada-48c6-ae94-b171cfa275fc, acbd72c9-9ff3-46a8-83ba-c6fa5df3f264, 5e59a619-c890-4472-91c2-d5eae5d8fa18, 56ef4bec-d739-4ddf-a003-ecc813085b8d, 929b0470-f7eb-4e18-9f97-22ac2c591a10, 131e6257-fdb7-426a-9825-1cd918802fba, 1ac794cc-76ac-47f6-bee6-d6666998f0fd, 87770aea-9162-409e-aa70-10d0d1de5902, 5dea6c32-6575-4527-9254-e50d27ae5e92, bafc17a7-ec64-49d9-a2a7-dafc28dcb037, 2449c066-9146-44a7-b4e6-4828031d958d, efcc3025-e2d8-4212-8227-805c7be39d2c, 8f78dce7-f5f9-4033-a10c-c9c7408bfcfe, cf653183-c829-4eed-a6ce-453fa1017cd9, 783c5979-f5e7-4cb6-b14e-c3553dde956a, 5515a98f-4668-4121-8e8d-fee2825699cf, 86956a2f-8d46-47ff-9b29-f99079ae3c1d, c4d4361c-8882-47b4-8641-fd3ab68ae722, 972ad095-16b7-40ad-a464-f5dbf417a8cd, 7bcdcdb2-3279-44bf-a998-771bab4b33e1]";
    - No actions + + +
    - No actions + + +