diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts index d6d7f9ff7..21e62b6d6 100644 --- a/portal-ui/src/common/SecureComponent/permissions.ts +++ b/portal-ui/src/common/SecureComponent/permissions.ts @@ -121,6 +121,7 @@ export const IAM_SCOPES = { ADMIN_CREATE_POLICY: "admin:CreatePolicy", ADMIN_DELETE_POLICY: "admin:DeletePolicy", ADMIN_ATTACH_USER_OR_GROUP_POLICY: "admin:AttachUserOrGroupPolicy", + ADMIN_HEAL_ACTION: "admin:Heal", S3_ALL_ACTIONS: "s3:*", ADMIN_ALL_ACTIONS: "admin:*", }; @@ -179,6 +180,7 @@ export const IAM_PERMISSIONS = { IAM_SCOPES.ADMIN_GET_POLICY, IAM_SCOPES.ADMIN_LIST_USER_POLICIES, IAM_SCOPES.ADMIN_LIST_USERS, + IAM_SCOPES.ADMIN_HEAL_ACTION, ], }; diff --git a/portal-ui/src/screens/Console/Common/SettingsCard/SettingsCard.tsx b/portal-ui/src/screens/Console/Common/SettingsCard/SettingsCard.tsx index 465aa5244..cbfe3b302 100644 --- a/portal-ui/src/screens/Console/Common/SettingsCard/SettingsCard.tsx +++ b/portal-ui/src/screens/Console/Common/SettingsCard/SettingsCard.tsx @@ -25,6 +25,7 @@ interface ISettingsCard { classes: any; configuration: IElement; prefix?: string; + disabled?: boolean; } const styles = (theme: Theme) => @@ -53,6 +54,11 @@ const styles = (theme: Theme) => "&:hover": { backgroundColor: "#FBFAFA", }, + "&.disabled": { + backgroundColor: "#F9F9F9", + color: "#ababab", + cursor: "not-allowed" + }, }, }); @@ -60,11 +66,14 @@ const SettingsCard = ({ classes, configuration, prefix = "settings", + disabled = false, }: ISettingsCard) => { return ( {configuration.icon} {configuration.configuration_label} diff --git a/portal-ui/src/screens/Console/Heal/Heal.tsx b/portal-ui/src/screens/Console/Heal/Heal.tsx index 4251e8e1c..84043772c 100644 --- a/portal-ui/src/screens/Console/Heal/Heal.tsx +++ b/portal-ui/src/screens/Console/Heal/Heal.tsx @@ -17,6 +17,7 @@ import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import { HorizontalBar } from "react-chartjs-2"; +import { Redirect } from "react-router-dom"; import { Button, FormControl, @@ -40,6 +41,10 @@ import { inlineCheckboxes, searchField, } from "../Common/FormComponents/common/styleLibrary"; +import { + CONSOLE_UI_RESOURCE, + IAM_SCOPES, +} from "../../../common/SecureComponent/permissions"; import { AppState } from "../../../store"; import { ErrorResponseHandler } from "../../../common/types"; import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper"; @@ -47,6 +52,7 @@ import PageHeader from "../Common/PageHeader/PageHeader"; import api from "../../../common/api"; import BackLink from "../../../common/BackLink"; import PageLayout from "../Common/Layout/PageLayout"; +import SecureComponent from "../../../common/SecureComponent/SecureComponent"; const styles = (theme: Theme) => createStyles({ @@ -273,128 +279,134 @@ const Heal = ({ classes, distributedSetup }: IHeal) => { - - - - { - setBucketName(e.target.value as string); - }} - className={classes.searchField} - input={} - displayEmpty - > - - Select Bucket - - {bucketNames.map((option) => ( - - {option.label} + } + > + + + + { + setBucketName(e.target.value as string); + }} + className={classes.searchField} + input={} + displayEmpty + > + + Select Bucket - ))} - - - { - setPrefix(e.target.value); - }} - variant="standard" - /> + {bucketNames.map((option) => ( + + {option.label} + + ))} + + + { + setPrefix(e.target.value); + }} + variant="standard" + /> + + + { + setRecursive(e.target.checked); + }} + disabled={false} + label="Recursive" + /> + { + setForceStart(e.target.checked); + }} + disabled={false} + label="Force Start" + /> + { + setForceStop(e.target.checked); + }} + disabled={false} + label="Force Stop" + /> + + + setStart(true)} + > + Start + + - - { - setRecursive(e.target.checked); + + - { - setForceStart(e.target.checked); - }} - disabled={false} - label="Force Start" - /> - { - setForceStop(e.target.checked); - }} - disabled={false} - label="Force Stop" /> + + + Size scanned: {hStatus.sizeScanned} + + + Objects healed: {hStatus.objectsHealed} /{" "} + {hStatus.objectsScanned} + + + Healing time: {hStatus.healDuration}s + + - - setStart(true)} - > - Start - - - - - - - - Size scanned: {hStatus.sizeScanned} - - - Objects healed: {hStatus.objectsHealed} /{" "} - {hStatus.objectsScanned} - - - Healing time: {hStatus.healDuration}s - - - + ); diff --git a/portal-ui/src/screens/Console/Speedtest/Speedtest.tsx b/portal-ui/src/screens/Console/Speedtest/Speedtest.tsx index 25860fa0c..4670f10c2 100644 --- a/portal-ui/src/screens/Console/Speedtest/Speedtest.tsx +++ b/portal-ui/src/screens/Console/Speedtest/Speedtest.tsx @@ -15,11 +15,13 @@ // along with this program. If not, see . import React, { Fragment, useEffect, useState } from "react"; +import { connect } from "react-redux"; import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket"; +import { Button, CircularProgress, Grid } from "@mui/material"; import { Theme } from "@mui/material/styles"; +import { Redirect } from "react-router-dom"; import createStyles from "@mui/styles/createStyles"; import withStyles from "@mui/styles/withStyles"; -import { Button, CircularProgress, Grid } from "@mui/material"; import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos"; import moment from "moment/moment"; import PageHeader from "../Common/PageHeader/PageHeader"; @@ -31,6 +33,12 @@ import { } from "../Common/FormComponents/common/styleLibrary"; import { wsProtocol } from "../../../utils/wsUtils"; import { SpeedTestResponse } from "./types"; +import { AppState } from "../../../store"; +import { SpeedtestIcon } from "../../../icons"; +import { + CONSOLE_UI_RESOURCE, + IAM_SCOPES, +} from "../../../common/SecureComponent/permissions"; import STResults from "./STResults"; import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; import BackLink from "../../../common/BackLink"; @@ -38,9 +46,12 @@ import ProgressBarWrapper from "../Common/ProgressBarWrapper/ProgressBarWrapper" import InputUnitMenu from "../Common/FormComponents/InputUnitMenu/InputUnitMenu"; import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper"; import PageLayout from "../Common/Layout/PageLayout"; +import HelpBox from "../../../common/HelpBox"; +import SecureComponent from "../../../common/SecureComponent/SecureComponent"; interface ISpeedtest { classes: any; + distributedSetup: boolean; } const styles = (theme: Theme) => @@ -74,7 +85,7 @@ const styles = (theme: Theme) => ...containerForHeader(theme.spacing(4)), }); -const Speedtest = ({ classes }: ISpeedtest) => { +const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => { const [start, setStart] = useState(false); const [currStatus, setCurrStatus] = useState( @@ -182,179 +193,235 @@ const Speedtest = ({ classes }: ISpeedtest) => { setSpeedometerValue(percToDisplay); }, [start, currentValue, topDate, totalSeconds]); + if (!distributedSetup) { + return ( + + + + + + + } + help={ + + This feature is not available for a single-disk setup. + + Please deploy a server in{" "} + + Distributed Mode + {" "} + to use this feature. + + } + /> + + + + + ); + } + return ( - - - - - { - setCurrStatus(null); - setStart(true); - }} - color="primary" - type="button" - variant={ - currStatus !== null && !start ? "contained" : "outlined" - } - className={`${classes.buttonBackground} ${classes.speedStart}`} - disabled={duration.trim() === "" || size.trim() === "" || start} - > - {!start && ( - - {currStatus !== null ? "Retest" : "Start"} - - )} - {start ? "Start" : ""} - - - - - {start ? ( - "Speedtest in progress..." - ) : ( - - {currStatus && !start ? "Done!" : "Start a new test"} - - )} - {start && } - - - - - - - { - setAdvancedOpen(!advancedOpen); - }} - className={classes.advancedConfiguration} - > - {advancedOpen ? "Hide" : "Show"} advanced options{" "} - + } + > + + + + { + setCurrStatus(null); + setStart(true); + }} + color="primary" + type="button" + variant={ + currStatus !== null && !start ? "contained" : "outlined" + } + className={`${classes.buttonBackground} ${classes.speedStart}`} + disabled={ + duration.trim() === "" || size.trim() === "" || start } > - - - - - - - - setAutotune(e.target.checked)} - id={"autotune"} - name={"autotune"} - label={"Enable Autotune"} - tooltip={ - "Autotune gets the maximum stats for the system by running with multiple configurations at once. \ - This configuration is enabled by default and disables the rest of available options" - } - value="true" - disabled={start} - /> - - - { - setDuration(e.target.value); - }} - value={duration} - disabled={start || autotune} - overlayObject={ - - } - /> - - - { - setSize(e.target.value); - }} - value={size} - disabled={start || autotune} - overlayObject={ - - } - /> - - - { - setConcurrent(e.target.value); - }} - value={concurrent} - disabled={start || autotune} - /> - - - - - - - {currStatus !== null && ( + {!start && ( - + {currStatus !== null ? "Retest" : "Start"} )} - - + {start ? "Start" : ""} + + + + + {start ? ( + "Speedtest in progress..." + ) : ( + + {currStatus && !start ? "Done!" : "Start a new test"} + + )} + {start && } + + + + + + + { + setAdvancedOpen(!advancedOpen); + }} + className={classes.advancedConfiguration} + > + {advancedOpen ? "Hide" : "Show"} advanced options{" "} + + + + + + + + + setAutotune(e.target.checked)} + id={"autotune"} + name={"autotune"} + label={"Enable Autotune"} + tooltip={ + "Autotune gets the maximum stats for the system by running with multiple configurations at once. \ + This configuration is enabled by default and disables the rest of available options" + } + value="true" + disabled={start} + /> + + + { + setDuration(e.target.value); + }} + value={duration} + disabled={start || autotune} + overlayObject={ + + } + /> + + + { + setSize(e.target.value); + }} + value={size} + disabled={start || autotune} + overlayObject={ + + } + /> + + + { + setConcurrent(e.target.value); + }} + value={concurrent} + disabled={start || autotune} + /> + + + + + + + {currStatus !== null && ( + + + + )} + + + - + ); }; -export default withStyles(styles)(Speedtest); +const mapState = (state: AppState) => ({ + distributedSetup: state.system.distributedSetup, +}); + +const connector = connect(mapState, null); + +export default connector(withStyles(styles)(Speedtest)); diff --git a/portal-ui/src/screens/Console/Tools/ToolsPanel/ToolsList.tsx b/portal-ui/src/screens/Console/Tools/ToolsPanel/ToolsList.tsx index e59e543b3..b4d8a3069 100644 --- a/portal-ui/src/screens/Console/Tools/ToolsPanel/ToolsList.tsx +++ b/portal-ui/src/screens/Console/Tools/ToolsPanel/ToolsList.tsx @@ -88,6 +88,7 @@ const ToolsList = ({ classes }: IConfigurationOptions) => { prefix={"tools"} configuration={element} key={`configItem-${element.configuration_label}`} + disabled={element.disabled || false} /> ))} diff --git a/portal-ui/src/screens/Console/Tools/types.ts b/portal-ui/src/screens/Console/Tools/types.ts index b035fef61..2214a37d0 100644 --- a/portal-ui/src/screens/Console/Tools/types.ts +++ b/portal-ui/src/screens/Console/Tools/types.ts @@ -58,4 +58,5 @@ export interface IElement { configuration_id: string; configuration_label: string; icon?: any; + disabled?: boolean; } diff --git a/portal-ui/src/screens/Console/Tools/utils.tsx b/portal-ui/src/screens/Console/Tools/utils.tsx index 81477965b..550a57879 100644 --- a/portal-ui/src/screens/Console/Tools/utils.tsx +++ b/portal-ui/src/screens/Console/Tools/utils.tsx @@ -24,6 +24,13 @@ import { WatchIcon, } from "../../../icons"; import SpeedtestIcon from "../../../icons/SpeedtestIcon"; +import { + CONSOLE_UI_RESOURCE, + IAM_SCOPES, +} from "../../../common/SecureComponent/permissions"; +import { + hasPermission, +} from "../../../common/SecureComponent/SecureComponent"; export const configurationElements: IElement[] = [ { @@ -50,6 +57,9 @@ export const configurationElements: IElement[] = [ icon: , configuration_id: "heal", configuration_label: "heal", + disabled: !hasPermission(CONSOLE_UI_RESOURCE, [ + IAM_SCOPES.ADMIN_HEAL_ACTION, + ]), }, { icon: , @@ -60,5 +70,8 @@ export const configurationElements: IElement[] = [ icon: , configuration_id: "speedtest", configuration_label: "Speedtest", + disabled: !hasPermission(CONSOLE_UI_RESOURCE, [ + IAM_SCOPES.ADMIN_HEAL_ACTION, + ]), }, ]; diff --git a/restapi/ws_handle.go b/restapi/ws_handle.go index 336f9e967..d777a457a 100644 --- a/restapi/ws_handle.go +++ b/restapi/ws_handle.go @@ -116,10 +116,6 @@ func (c wsConn) readMessage() (messageType int, p []byte, err error) { // on the path. // Request should come like ws://:/ws/ func serveWS(w http.ResponseWriter, req *http.Request) { - upgrader.CheckOrigin = func(r *http.Request) bool { - return true - } - // Perform authentication before upgrading to a Websocket Connection // authenticate WS connection with Console session, err := auth.GetClaimsFromTokenInRequest(req)