diff --git a/frontend/src/components/NewHeader.jsx b/frontend/src/components/NewHeader.jsx new file mode 100644 index 00000000..7b72f0bd --- /dev/null +++ b/frontend/src/components/NewHeader.jsx @@ -0,0 +1,1377 @@ +import React, { useState } from "react"; +import { toast } from "react-toastify"; +import theme from "../theme.jsx"; +import { BrowserView, MobileView } from "react-device-detect"; + +import { useNavigate, Link } from "react-router-dom"; +import ReactGA from "react-ga4"; +import SearchField from "../components/Searchfield.jsx"; +import { + Paper, + Typography, + Badge, + Tooltip, + List, + ListItem, + Avatar, + Menu, + MenuItem, + Select, + Button, + Grid, + IconButton, + Divider, + LinearProgress, + + AppBar, +} from "@mui/material"; + +import { + MeetingRoom as MeetingRoomIcon, + HelpOutline as HelpOutlineIcon, + Settings as SettingsIcon, + Notifications as NotificationsIcon, + Home as HomeIcon, + Apps as AppsIcon, + Description as DescriptionIcon, + EmojiObjects as EmojiObjectsIcon, + Business as BusinessIcon, + Polyline as PolylineIcon, +} from "@mui/icons-material"; + +import { + Analytics as AnalyticsIcon, + Lightbulb as LightbulbIcon, +} from "@mui/icons-material"; + +const hoverColor = "#f85a3e"; +const hoverOutColor = "#e8eaf6"; + +const Header = (props) => { + const { + globalUrl, + setNotifications, + notifications, + isLoaded, + isLoggedIn, + removeCookie, + homePage, + userdata, + isMobile, + serverside, + } = props; + + const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); + const [SoarHoverColor, setSoarHoverColor] = useState(hoverOutColor); + const [LoginHoverColor, setLoginHoverColor] = useState(hoverOutColor); + const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor); + const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor); + const [isHeader, setIsHeader] = React.useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const [anchorElAvatar, setAnchorElAvatar] = React.useState(null); + const [subAnchorEl, setSubAnchorEl] = React.useState(null); + let navigate = useNavigate(); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + setAnchorElAvatar(null); + }; + // Should be based on some path + const logoCheck = !homePage ? null : null + + const hrefStyle = { + color: hoverOutColor, + textDecoration: "none", + }; + + const menuText = { + textTransform: "none", + color: "#FFF", + textAlign: "center", + fontSize: 16, + fontStyle: "normal", + fontWeight: 400, + lineHeight: "normal", + } + + const isCloud = + serverside === true || typeof window === "undefined" + ? true + : window.location.host === "localhost:3002" || + window.location.host === "shuffler.io"; + + const clearNotifications = () => { + // Don't really care about the logout + fetch(`${globalUrl}/api/v1/notifications/clear`, { + credentials: "include", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + setNotifications([]); + handleClose(); + } else { + toast("Failed dismissing notifications. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification dismissal: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; + + const dismissNotification = (alert_id) => { + // Don't really care about the logout + fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { + credentials: "include", + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + const newNotifications = notifications.filter( + (data) => data.id !== alert_id + ); + console.log("NEW NOTIFICATIONS: ", newNotifications); + setNotifications(newNotifications); + } else { + toast("Failed dismissing notification. Please try again later."); + } + }) + .catch((error) => { + console.log("error in notification dismissal: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; + + // DEBUG HERE + const handleClickLogout = () => { + console.log("SHOULD LOG OUT"); + + // Don't really care about the logout + fetch(globalUrl + "/api/v1/logout", { + credentials: "include", + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }) + .then(() => { + // Log out anyway + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + removeCookie("session_token", { path: "/" }); + window.location.pathname = "/"; + }) + .catch((error) => { + console.log(error); + }); + }; + + // Rofl this is weird + const handleDocsHover = () => { + setDocsHoverColor(hoverColor); + }; + + const handleDocsHoverOut = () => { + setDocsHoverColor(hoverOutColor); + }; + + const handleHomeHover = () => { + setHomeHoverColor(hoverColor); + }; + + const handleHelpHover = () => { + setHelpHoverColor(hoverColor); + }; + + const handleHelpHoverOut = () => { + setHelpHoverColor(hoverOutColor); + }; + + const handleSoarHover = () => { + setSoarHoverColor(hoverColor); + }; + + const handleSoarHoverOut = () => { + setSoarHoverColor(hoverOutColor); + }; + + const handleHomeHoverOut = () => { + setHomeHoverColor(hoverOutColor); + }; + + const handleLoginHover = () => { + setLoginHoverColor(hoverColor); + }; + + const handleLoginHoverOut = () => { + setLoginHoverColor(hoverOutColor); + }; + + const notificationWidth = 300 + const imagesize = 22; + const boxColor = "#86c142"; + + const NotificationItem = (props) => { + const {data} = props + + var image = ""; + var orgName = ""; + var orgId = ""; + if (userdata.orgs !== undefined) { + const foundOrg = userdata.orgs.find((org) => org.id === data["org_id"]); + if (foundOrg !== undefined && foundOrg !== null) { + //position: "absolute", bottom: 5, right: -5, + const imageStyle = { + width: imagesize, + height: imagesize, + pointerEvents: "none", + marginLeft: + data.creator_org !== undefined && data.creator_org.length > 0 + ? 20 + : 0, + borderRadius: 10, + border: + foundOrg.id === userdata.active_org.id + ? `3px solid ${boxColor}` + : null, + cursor: "pointer", + marginRight: 10, + }; + + image = + foundOrg.image === "" ? ( + {foundOrg.name} + ) : ( + {foundOrg.name} {}} + /> + ); + + orgName = foundOrg.name; + orgId = foundOrg.id; + } + } + + return ( + + {/* + {new Date(data.updated_at).toISOString()} + */} + {data.reference_url !== undefined && + data.reference_url !== null && + data.reference_url.length > 0 ? ( + + {data.title} + + ) : ( + + {data.title} + + )} + + {data.image !== undefined && + data.image !== null && + data.image.length > 0 ? ( + {data.title} + ) : null} + {data.description} + {/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ? + data.tags.map((tag, index) => { + return ( + { + }} + variant="outlined" + color="primary" + /> + ) + }) + : null */} +
+ {data.read === false ? ( + + ) : null} + +
{}} + > + {image} +
+
+
+
+ ); + }; + + const notificationMenu = ( + + { + setAnchorEl(event.currentTarget); + }} + > + + + + + { + handleClose(); + }} + > + +
+ + Your Notifications ({notifications.length}) + + {notifications.length > 1 ? ( + + ) : null} +
+ + Notifications are made by Shuffle to help you discover issues or + improvements. + +
+ {notifications.map((data, index) => { + return ; + })} +
+
+ ); + + const handleClickChangeOrg = (orgId) => { + // Don't really care about the logout + //name: org.name, + //orgId = "asd" + const data = { + org_id: orgId, + }; + + localStorage.setItem("globalUrl", ""); + localStorage.setItem("getting_started_sidebar", "open"); + + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: "cors", + credentials: "include", + crossDomain: true, + method: "POST", + body: JSON.stringify(data), + withCredentials: true, + headers: { + "Content-Type": "application/json; charset=utf-8", + }, + }) + .then(function (response) { + if (response.status !== 200) { + console.log("Error in response"); + } + + return response.json(); + }) + .then(function (responseJson) { + if (responseJson.success === true) { + if ( + responseJson.region_url !== undefined && + responseJson.region_url !== null && + responseJson.region_url.length > 0 + ) { + console.log("Region Change: ", responseJson.region_url); + localStorage.setItem("globalUrl", responseJson.region_url); + //globalUrl = responseJson.region_url + } + + setTimeout(() => { + window.location.reload(); + }, 2000); + toast("Successfully changed active organization - refreshing!"); + } else { + toast("Failed changing org: ", responseJson.reason); + } + }) + .catch((error) => { + console.log("error changing: ", error); + //removeCookie("session_token", {path: "/"}) + }); + }; + + const supportMenu = ( + + + + {}} + > + Discord Community Join + + + + + ); + + // Should be based on some path + const parsedAvatar = + userdata.avatar !== undefined && + userdata.avatar !== null && + userdata.avatar.length > 0 + ? userdata.avatar + : ""; + + const avatarMenu = ( + + { + setAnchorElAvatar(event.currentTarget); + }} + > + + + { + handleClose(); + }} + > + + { + handleClose(); + }} + > + Admin + + + + + { + handleClose(); + }} + > + About + + + {/* + + { + handleClose(); + }} + > + Get Started + + + */} + + { + handleClose(); + }} + > + Use Cases + + + + + { + handleClose(); + }} + > + Creator page + + + + { + handleClose(); + }} + > + Settings + + + + { + handleClickLogout(); + event.preventDefault(); + handleClose(); + }} + > +  Logout + + + + ); + + const listItemStyle = { + textAlign: "center", + marginTop: "auto", + marginBottom: "auto", + marginRight: 10, + }; + + // Handle top bar or something + const defaultTop = isCloud ? 0 : 7; + const loginTextBrowser = !isLoggedIn ? ( +
+ + + + { + if (isCloud) { + ReactGA.event({ + category: "header", + action: "home_click", + label: "", + }); + } + }} + > + shuffle logo + + + + + + + + + + + {isCloud ? ( + + + + + + ) : null} + + + + + + + +
+ + + + + + + + + + + +
+ ) : ( +
+
+
+ + + +
+ + + logo + + +
+ + + +
+ {/* + + */} + + Workflows + +
+ +
+ + +
+ {/* + + */} + + Apps + +
+ +
+ {/* + + +
Dashboard
+ +
+ */} + + +
+ {/* + + */} + + Docs + +
+ +
+
+
+
+
+ +
+
+ + + {avatarMenu} + {notificationMenu} + {/*supportMenu*/} + {logoCheck} + + + {userdata === undefined || + userdata.orgs === undefined || + userdata.orgs === null || + userdata.orgs.length <= 1 ? null : ( + + + + )} + + {/* Show on cloud, if not suborg and if not customer/pov/internal */} + {isCloud && + (userdata.org_status === undefined || + userdata.org_status === null || + userdata.org_status.length === 0) ? ( + + + + + + ) : null} + + {userdata === undefined || + userdata.app_execution_limit === undefined || + userdata.app_execution_usage === undefined || + userdata.app_execution_usage < 1000 ? null : ( + +
= + 0.9 + ? "#f86a3e" + : null, + }} + onClick={() => { + console.log( + userdata.appe_execution_usage / + userdata.app_execution_limit + ); + if (window.drift !== undefined) { + window.drift.api.startInteraction({ + interactionId: 326905, + }); + navigate("/pricing"); + } else { + console.log( + "Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", + window.drift + ); + } + }} + > + + + {( + (userdata.app_execution_usage / + userdata.app_execution_limit) * + 100 + ).toFixed(0)} + % + + + +
+
+ )} +
+
+
+
+ ); + + const loginTextMobile = !isLoggedIn ? ( +
+ + + +
+ + + logo + + +
+ +
+ + +
+ About +
+ +
+ + + + + + + + + + +
+
+ ) : ( +
+
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + +
+
+
+ + +
+ Logout +
+
+ {logoCheck} + +
+
+
+ ); + + // + return !isMobile ? + isLoggedIn ? +
+ + {loginTextBrowser} + +
+ : + + + {loginTextBrowser} + + + : + {loginTextMobile} +}; + +export default Header; diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index b70e536a..ca51d31a 100755 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -125,7 +125,7 @@ const AuthenticationOauth2 = (props) => { const allscopes = authenticationType.scope !== undefined ? authenticationType.scope : []; - const [selectedScopes, setSelectedScopes] = React.useState(allscopes.length === 1 ? [allscopes[0]] : []) + const [selectedScopes, setSelectedScopes] = React.useState(allscopes.length <= 3 ? allscopes : []) const [manuallyConfigure, setManuallyConfigure] = React.useState( defaultConfigSet ? false : true ); @@ -883,7 +883,7 @@ const AuthenticationOauth2 = (props) => { //authenticationOption.label = event.target.value }} /> - {allscopes.length === 0 ? null : "Scopes"} + {allscopes.length === 0 ? null : "Scopes (access rights)"} {allscopes.length === 0 ? null : (
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 27ba8f18..bb019f14 100755 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -591,7 +591,7 @@ const AngularWorkflow = (defaultprops) => { } useEffect(() => { - console.log("In useeffect for loopRunning: ", loopRunning) + //console.log("In useeffect for loopRunning: ", loopRunning) if (loopRunning) { const intervalId = setInterval(() => { if (!loopRunning) { @@ -1244,7 +1244,7 @@ const AngularWorkflow = (defaultprops) => { // Doesn't work because this is some async garbage if (executionData.execution_id === undefined || (responseJson.execution_id === executionData.execution_id && responseJson.results !== undefined && responseJson.results !== null)) { if (executionData.status !== responseJson.status || executionData.result !== responseJson.result || (executionData.results !== undefined && responseJson.results !== null && executionData.results.length !== responseJson.results.length)) { - console.log("Updating data!") + //console.log("Updating data!") setExecutionData(responseJson) } else { if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE" || responseJson.status === "WAITING") { @@ -15381,7 +15381,7 @@ const AngularWorkflow = (defaultprops) => { }} onClick={() => { const oldstartnode = cy.getElementById(data.action.id); - console.log("FOUND NODe: ", oldstartnode) + //console.log("FOUND NODe: ", oldstartnode) if (oldstartnode !== undefined && oldstartnode !== null) { const foundname = oldstartnode.data("label") if (foundname !== undefined && foundname !== null) { @@ -15389,7 +15389,7 @@ const AngularWorkflow = (defaultprops) => { } } - console.log("Click data: ", data) + //console.log("Click data: ", data) //data.action.label = "" setSelectedResult(data); setCodeModalOpen(true); diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 4cd2dfc7..c2656a95 100755 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -242,7 +242,7 @@ export const appCategories = [ "name": "Eradication", "color": "#FFC107", "icon": "eradication", - "action_labels": ["List Alerts", "Close Alert", "Get Alert", "Create detection", "Block hash", "Search Hosts", "Isolate host", "Unisolate host"], + "action_labels": ["List Alerts", "Close Alert", "Get Alert", "Create detection", "Block hash", "Search Hosts", "Isolate host", "Unisolate host", "Trigger host scan",], }, { "name": "Cases", "color": "#FFC107", @@ -1757,8 +1757,8 @@ const AppCreator = (defaultprops) => { ) { if (value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0) { setParameterName(value[flowkey][basekey].authorizationUrl); - } else { - setOauth2Type("application") + // } else { + // setOauth2Type("application") } var tokenUrl = "";