diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 04cdd17c..f1667aec 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -24,6 +24,7 @@ import DashboardView from "./views/DashboardViews.jsx"; import AdminSetup from "./views/AdminSetup"; import Admin from "./views/Admin"; import Docs from "./views/Docs.jsx"; +import Usecases2 from "./views/Usecases2.jsx"; //import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; @@ -383,6 +384,19 @@ const App = (message, props) => { {...props} /> } + /> + + } /> { + + const [frameworkData, setFrameworkData] = 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 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 + } + + + 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 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 Gmail + + + + + + + + + +
+ {app?.name} +
+
+ + {newAppname} + + { + isCloud && ( + + + + + + ) + } +
+ + {app?.categories ? app.categories.join(", ") : "Communication"} + +
+
+
+ + +
+
+ +
+
+ + 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; \ No newline at end of file diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index e12ba76d..ea8af7bd 100755 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -324,7 +324,7 @@ const ConfigureWorkflow = (props) => { } } - if (app.authentication.type === "oauth2" || app.authentication.type === "oauth2-app") { + if (app?.authentication?.type === "oauth2" || app?.authentication?.type === "oauth2-app") { filled = false action.auth_type = "oauth2" @@ -848,7 +848,7 @@ const ConfigureWorkflow = (props) => { {opened ?
- {action.app.authentication.type === "oauth2-app" || action.app.authentication.type === "oauth2" || action.auth_type === "oauth2" ? + {action.app?.authentication?.type === "oauth2-app" || action.app?.authentication?.type === "oauth2" || action.auth_type === "oauth2" ?
{ ) })} - {action.app.authentication.type !== "oauth2-app" && action.app.authentication.type !== "oauth2" ? + {action.app?.authentication?.type !== "oauth2-app" && action.app?.authentication?.type !== "oauth2" ?
+ ) : ( +
+ {data.tags && + data.tags.map((tag, tagIndex) => ( + + {normalizedString(tag)} + {tagIndex < data.tags.length - 1 ? ", " : ""} + + ))} +
+ )} +
+
+ {hoverEffect === index && isCloud && ( +
+ {allActivatedAppIds && allActivatedAppIds.includes(data.objectID) ? ( + + ) : ( + - ) : ( - - )} -
- )} -
+ onClick={(event) => { + handleActivateButton(event, data, "activate"); + }} + > + Activate + + )} + + )} - - -
- + + + + ); })} @@ -569,7 +566,7 @@ const SearchBox = ({ refine, searchQuery, setSearchQuery }) => { setSearchQuery(value); removeQuery("q"); refine(value); - }, 300) // Adjust the delay as needed + }, 300) ).current; useEffect(() => { @@ -702,8 +699,8 @@ const Apps2 = (props) => { const [mouseHoverIndex, setMouseHoverIndex] = useState(-1); var counted = 0; const [showNoAppFound, setShowNoAppFound] = useState(false); - - + const [openModal, setOpenModal] = useState(false); + const [selectedApp, setSelectedApp] = useState(null); const [appFramework, setAppFramework] = useState(undefined); const [defaultSearch, setDefaultSearch] = useState(""); // Set the current tab based on the query parameter @@ -799,6 +796,7 @@ const Apps2 = (props) => { } }; + // Only fetch if we have required data if (globalUrl && (currTab === 0 || (currTab === 1 && userdata?.id))) { fetchApps(); @@ -883,7 +881,7 @@ const Apps2 = (props) => { const handleCreateApp = (e) => { e.preventDefault(); - console.log("Create app clicked") + setOpenModal(true); }; @@ -941,7 +939,6 @@ const Apps2 = (props) => { queryParams.set('tab', newQueryParam); queryParams.delete('q'); window.history.replaceState({}, '', `${location.pathname}?${queryParams.toString()}`); - console.log("Apps to show", appsToShow) }; @@ -968,9 +965,24 @@ const Apps2 = (props) => { setSelectedLabel(value); }; + const handleAppClick = (app) => { + setSelectedApp(app); + setOpenModal(true); + } + + const handleAppModalClose = () => { + setOpenModal(false) + } return ( +
Apps @@ -1103,7 +1115,7 @@ const Apps2 = (props) => { {appsToShow?.length > 0 && appsToShow !== undefined && !isLoading ? (
{appsToShow.map((data, index) => ( - + ))}
) : ( @@ -1137,7 +1149,9 @@ const Apps2 = (props) => { {appsToShow?.length > 0 && appsToShow !== undefined ? (
{appsToShow.map((data, index) => ( - + ))}
) : ( @@ -1156,6 +1170,7 @@ const Apps2 = (props) => { { if (responseJson.tutorials === undefined || responseJson.tutorials === null || !responseJson.tutorials.includes("welcome")) { console.log("RUN Welcome!!") - window.location.pathname = "/welcome?tab=2" + setTimeout(() => { + navigate("/welcome?tab=2") + },200) + // window.location.pathname = "" return } diff --git a/frontend/src/views/Usecases2.jsx b/frontend/src/views/Usecases2.jsx index e6f33980..696e7573 100644 --- a/frontend/src/views/Usecases2.jsx +++ b/frontend/src/views/Usecases2.jsx @@ -692,7 +692,7 @@ const UsecaseListComponent = (props) => { // This is the start of a dashboard that can be used. // What data do we fill in here? Idk -const Dashboard = (props) => { +const Usecases2 = (props) => { const { globalUrl, isLoggedIn, userdata } = props; //const alert = useAlert(); const { leftSideBarOpenByClick} = useContext(Context); @@ -1250,4 +1250,4 @@ const Dashboard = (props) => { return dataWrapper }; -export default Dashboard; +export default Usecases2;