import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; import { Dialog, DialogTitle, DialogContent, IconButton, Typography, Box, Button, Stack, Avatar, } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import EditIcon from '@mui/icons-material/Edit'; import Search from '@mui/icons-material/Search'; import AddIcon from '@mui/icons-material/Add'; import ForkRightIcon from '@mui/icons-material/ForkRight'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import LaunchIcon from '@mui/icons-material/Launch'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { CloudDownloadOutlined } from '@mui/icons-material'; import { findSpecificApp } from './AppFramework'; import theme from "../theme"; import YAML from 'yaml'; import { toast } from 'react-toastify'; import { Link } from 'react-router-dom'; const AppModal = ({ open, onClose, app, globalUrl }) => { const [frameworkData, setFrameworkData] = useState({}) const [userdata, setUserdata] = useState({}) const [usecases, setUsecases] = useState([]) const [workflows, setWorkflows] = useState([]) const [prevSubcase, setPrevSubcase] = useState({}) const [inputUsecase, setInputUsecase] = useState({}) const [latestUsecase, setLatestUsecase] = useState([]) const [foundAppUsecase, setFoundAppUsecase] = useState({}) const navigate = useNavigate(); const parseUsecase = (subcase) => { const srcdata = findSpecificApp(frameworkData, subcase.type) const dstdata = findSpecificApp(frameworkData, subcase.last) if (srcdata !== undefined && srcdata !== null) { subcase.srcimg = srcdata.large_image subcase.srcapp = srcdata.name } if (dstdata !== undefined && dstdata !== null) { subcase.dstimg = dstdata.large_image subcase.dstapp = dstdata.name } return subcase } useEffect(() => { var baseurl = globalUrl; fetch(baseurl + "/api/v1/me", { credentials: "include", headers: { 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(responseJson => { if (responseJson.success) { setUserdata(responseJson) } }) .catch(error => { console.log("Failed login check: ", error); }); }, [app]); const getFramework = () => { fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { if (responseJson.success === false) { const preparedData = { "siem": findSpecificApp({}, "SIEM"), "communication": findSpecificApp({}, "COMMUNICATION"), "assets": findSpecificApp({}, "ASSETS"), "cases": findSpecificApp({}, "CASES"), "network": findSpecificApp({}, "NETWORK"), "intel": findSpecificApp({}, "INTEL"), "edr": findSpecificApp({}, "EDR"), "iam": findSpecificApp({}, "IAM"), "email": findSpecificApp({}, "EMAIL"), } setFrameworkData(preparedData) } else { setFrameworkData(responseJson) } }) .catch((error) => { console.log("Error getting framework: ", error) }) } const fetchUsecases = (workflows) => { fetch(globalUrl + "/api/v1/workflows/usecases", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for usecases"); } return response.json(); }) .then((responseJson) => { const newUsecases = [...usecases] newUsecases.forEach((category, index) => { category.list.forEach((subcase, subindex) => { getUsecase(subcase, index, subindex) }) }) setLatestUsecase(newUsecases) // Matching workflows with usecases if (responseJson.success !== false) { if (workflows !== undefined && workflows !== null && workflows.length > 0) { var categorydata = responseJson var newcategories = [] for (var key in categorydata) { var category = categorydata[key] category.matches = [] for (var subcategorykey in category.list) { var subcategory = category.list[subcategorykey] subcategory.matches = [] for (var workflowkey in workflows) { const workflow = workflows[workflowkey] if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) { for (var usecasekey in workflow.usecase_ids) { if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory?.name?.toLowerCase()) { category.matches.push({ "workflow": workflow.id, "category": subcategory?.name, }) subcategory.matches.push(workflow) break } } } if (subcategory.matches.length > 0) { break } } } newcategories.push(category) } if (newcategories !== undefined && newcategories !== null && newcategories.length > 0) { setUsecases(newcategories) } else { setUsecases(responseJson) } } else { setUsecases(responseJson) } } }) .catch((error) => { //toast("ERROR: " + error.toString()); console.log("ERROR: " + error.toString()); }); }; const getAvailableWorkflows = () => { fetch(globalUrl + "/api/v1/workflows", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { fetchUsecases() console.log("Status not 200 for workflows :O!: ", response.status); return; } return response.json(); }) .then((responseJson) => { fetchUsecases(responseJson) if (responseJson !== undefined) { setWorkflows(responseJson); } }) .catch((error) => { fetchUsecases() //toast(error.toString()); }); } const getUsecase = (subcase, index, subindex) => { subcase = parseUsecase(subcase) setPrevSubcase(subcase) fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase?.name?.replaceAll(" ", "_"))}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for framework!"); } return response.json(); }) .then((responseJson) => { var parsedUsecase = responseJson if (responseJson.success === false) { parsedUsecase = subcase } else { parsedUsecase = responseJson parsedUsecase.srcimg = subcase.srcimg parsedUsecase.srcapp = subcase.srcapp parsedUsecase.dstimg = subcase.dstimg parsedUsecase.dstapp = subcase.dstapp } // Look for the type of app and fill in img1, srcapp... setInputUsecase(parsedUsecase) }) .catch((error) => { //toast(error.toString()); setInputUsecase(subcase) console.log("Error getting usecase: ", error) }) } useEffect(() => { getAvailableWorkflows() getFramework() }, [app]) useEffect(() => { const foundCategory = latestUsecase?.find((category) => category?.list?.some((subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name) ); const foundSubcase = foundCategory?.list?.find( (subcase) => subcase?.srcapp === app?.name || subcase?.dstapp === app?.name ); setFoundAppUsecase(foundSubcase); }, [latestUsecase]) const downloadApp = (inputdata) => { const id = inputdata.id; toast("Downloading.."); fetch(globalUrl + "/api/v1/apps/" + id + "/config", { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", }, credentials: "include", }) .then((response) => { if (response.status !== 200) { window.location.pathname = "/apps"; } return response.json(); }) .then((responseJson) => { if (!responseJson.success) { toast("Failed to download file"); } else { console.log(responseJson); const basedata = atob(responseJson.openapi); console.log("BASE: ", basedata); var inputdata = JSON.parse(basedata); console.log("POST INPUT: ", inputdata); inputdata = JSON.parse(inputdata.body); const newpaths = {}; if (inputdata["paths"] !== undefined) { Object.keys(inputdata["paths"]).forEach(function (key) { newpaths[key.split("?")[0]] = inputdata.paths[key]; }); } inputdata.paths = newpaths; console.log("INPUT: ", inputdata); var name = inputdata.info.title; name = name.replace(/ /g, "_", -1); name = name.toLowerCase(); delete inputdata.id; delete inputdata.editing; const data = YAML.stringify(inputdata); var blob = new Blob([data], { type: "application/octet-stream", }); var url = URL.createObjectURL(blob); var link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", `${name}.yaml`); var event = document.createEvent("MouseEvents"); event.initMouseEvent( "click", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null ); link.dispatchEvent(event); //link.parentNode.removeChild(link) } }) .catch((error) => { console.log(error); toast(error.toString()); }); }; const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "localhost:3000" ? true : false; var newAppname = app?.name; if (newAppname === undefined) { newAppname = "Undefined"; } else { newAppname = newAppname.charAt(0).toUpperCase() + newAppname.substring(1); newAppname = newAppname?.replaceAll("_", " "); } var canEditApp = userdata.admin === "true" || userdata?.id === app?.owner || app?.owner === "" || (userdata.admin === "true" && userdata.active_org.id === app?.reference_org) || !app?.generated return ( About {app?.name.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())}
{app?.name}
{newAppname} { isCloud && ( ) }
{app?.categories ? app.categories.join(", ") : "Communication"}
{app?.activated && app?.private_id !== undefined && app?.private_id?.length > 0 && app?.generated ? ( ) : null}
20 Public Workflow
{Array.isArray(app?.actions) ? app.actions.length : app?.actions} Actions
{ app?.collection ? ( <> app.collection ) : ( No collection yet ) }
Part of a collection
{ (foundAppUsecase?.srcapp !== undefined && foundAppUsecase?.dstapp !== undefined) ? ( "Connect " + foundAppUsecase?.srcapp?.replaceAll("_", " ") + " to " + foundAppUsecase?.dstapp?.replaceAll("_", " ") ) : ( "Connect " + app?.name + " to any tool" ) }
{ foundAppUsecase === undefined ? ( ) : ( ) } { foundAppUsecase === undefined ? ( ) : ( ) } {foundAppUsecase?.name || "Search for a Usecase"}
); }; export default AppModal;