Logs, Watch Slices to replace old reducers, Split Tenant Add slice (#2035)

Logs, Watch Slices to replace old reducers

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2022-05-25 19:12:07 -05:00
committed by GitHub
parent e235863b94
commit b420ef3c1f
71 changed files with 3813 additions and 3427 deletions

View File

@@ -26,6 +26,7 @@ import { useDispatch, useSelector } from "react-redux";
import {
globalSetDistributedSetup,
operatorMode,
selOpMode,
setSiteReplicationInfo,
userLogged,
} from "./systemSlice";
@@ -40,9 +41,7 @@ interface ProtectedRouteProps {
const ProtectedRoute = ({ Component }: ProtectedRouteProps) => {
const dispatch = useDispatch();
const isOperatorMode = useSelector(
(state: AppState) => state.system.operatorMode
);
const isOperatorMode = useSelector(selOpMode);
const [sessionLoading, setSessionLoading] = useState<boolean>(true);
const userLoggedIn = useSelector((state: AppState) => state.system.loggedIn);

View File

@@ -15,10 +15,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect, useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import api from "../../../common/api";
import { Box } from "@mui/material";
@@ -55,14 +54,15 @@ import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton";
import { selectSAs } from "../Configurations/utils";
import DeleteMultipleServiceAccounts from "../Users/DeleteMultipleServiceAccounts";
import ServiceAccountPolicy from "./ServiceAccountPolicy";
import { AppState } from "../../../store";
import { setErrorSnackMessage, setSnackBarMessage } from "../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";
import { selFeatures } from "../consoleSlice";
const DeleteServiceAccount = withSuspense(
React.lazy(() => import("./DeleteServiceAccount"))
);
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...actionsTray,
...searchField,
@@ -73,16 +73,18 @@ const styles = (theme: Theme) =>
},
...tableStyles,
...containerForHeader(theme.spacing(4)),
});
})
);
interface IServiceAccountsProps {
classes: any;
history: any;
features: any;
}
const Account = ({ classes, history, features }: IServiceAccountsProps) => {
const Account = ({ history }: IServiceAccountsProps) => {
const dispatch = useDispatch();
const classes = useStyles();
const features = useSelector(selFeatures);
const [records, setRecords] = useState<string[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [filter, setFilter] = useState<string>("");
@@ -302,10 +304,4 @@ const Account = ({ classes, history, features }: IServiceAccountsProps) => {
);
};
const mapState = (state: AppState) => ({
features: state.console.session.features,
});
const connector = connect(mapState, null);
export default withStyles(styles)(connector(Account));
export default Account;

View File

@@ -15,21 +15,18 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect, useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { Paper } from "@mui/material";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { AppState } from "../../../../store";
import { TabPanel } from "../../../shared/tabs";
import { Policy } from "../../Policies/types";
import { ISessionResponse } from "../../types";
import { User } from "../../Users/types";
import { ErrorResponseHandler } from "../../../../common/types";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import api from "../../../../common/api";
import history from "../../../../history";
import { BucketInfo } from "../types";
import {
CONSOLE_UI_RESOURCE,
IAM_PAGES,
@@ -43,17 +40,10 @@ import {
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import { tableStyles } from "../../Common/FormComponents/common/styleLibrary";
import withStyles from "@mui/styles/withStyles";
import { encodeURLString } from "../../../../common/utils";
import { setErrorSnackMessage } from "../../../../systemSlice";
const mapState = (state: AppState) => ({
session: state.console.session,
loadingBucket: state.buckets.bucketDetails.loadingBucket,
bucketInfo: state.buckets.bucketDetails.bucketInfo,
});
const connector = connect(mapState, null);
import makeStyles from "@mui/styles/makeStyles";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
function a11yProps(index: any) {
return {
@@ -63,25 +53,20 @@ function a11yProps(index: any) {
}
interface IAccessDetailsProps {
session: ISessionResponse;
classes: any;
match: any;
loadingBucket: boolean;
bucketInfo: BucketInfo | null;
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...tableStyles,
});
const AccessDetails = ({
match,
loadingBucket,
classes,
}: IAccessDetailsProps) => {
})
);
const AccessDetails = ({ match }: IAccessDetailsProps) => {
const dispatch = useDispatch();
const classes = useStyles();
const loadingBucket = useSelector(selBucketDetailsLoading);
const [curTab, setCurTab] = useState<number>(0);
const [loadingPolicies, setLoadingPolicies] = useState<boolean>(true);
const [bucketPolicy, setBucketPolicy] = useState<Policy[]>([]);
@@ -241,4 +226,4 @@ const AccessDetails = ({
);
};
export default withStyles(styles)(connector(AccessDetails));
export default AccessDetails;

View File

@@ -15,13 +15,10 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect, useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Paper } from "@mui/material";
import { AppState } from "../../../../store";
import { ISessionResponse } from "../../types";
import { ErrorResponseHandler } from "../../../../common/types";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import api from "../../../../common/api";
@@ -35,7 +32,6 @@ import {
searchField,
tableStyles,
} from "../../Common/FormComponents/common/styleLibrary";
import { BucketInfo } from "../types";
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
import {
@@ -46,6 +42,8 @@ import {
import withSuspense from "../../Common/Components/withSuspense";
import RBIconButton from "./SummaryItems/RBIconButton";
import { setErrorSnackMessage } from "../../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
const AddAccessRuleModal = withSuspense(
React.lazy(() => import("./AddAccessRule"))
@@ -57,7 +55,7 @@ const EditAccessRuleModal = withSuspense(
React.lazy(() => import("./EditAccessRule"))
);
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
"@global": {
".rowLine:hover .iconFileElm": {
@@ -72,31 +70,19 @@ const styles = (theme: Theme) =>
...searchField,
...objectBrowserCommon,
...containerForHeader(theme.spacing(4)),
});
const mapState = (state: AppState) => ({
session: state.console.session,
loadingBucket: state.buckets.bucketDetails.loadingBucket,
bucketInfo: state.buckets.bucketDetails.bucketInfo,
});
const connector = connect(mapState, null);
})
);
interface IAccessRuleProps {
session: ISessionResponse;
classes: any;
match: any;
loadingBucket: boolean;
bucketInfo: BucketInfo | null;
}
const AccessRule = ({
classes,
match,
loadingBucket,
bucketInfo,
}: IAccessRuleProps) => {
const AccessRule = ({ match }: IAccessRuleProps) => {
const dispatch = useDispatch();
const classes = useStyles();
const loadingBucket = useSelector(selBucketDetailsLoading);
const [loadingAccessRules, setLoadingAccessRules] = useState<boolean>(true);
const [accessRules, setAccessRules] = useState([]);
const [addAccessRuleOpen, setAddAccessRuleOpen] = useState<boolean>(false);
@@ -254,4 +240,4 @@ const AccessRule = ({
);
};
export default withStyles(styles)(connector(AccessRule));
export default AccessRule;

View File

@@ -51,9 +51,8 @@ import {
import { LifecycleConfigIcon } from "../../../../icons";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { BucketVersioning } from "../types";
import { AppState } from "../../../../store";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import { setModalErrorSnackMessage } from "../../../../systemSlice";
import { selDistSet, setModalErrorSnackMessage } from "../../../../systemSlice";
interface IReplicationModal {
open: boolean;
@@ -100,9 +99,7 @@ const AddLifecycleModal = ({
bucketName,
}: IReplicationModal) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
const [addLoading, setAddLoading] = useState(false);

View File

@@ -30,8 +30,6 @@ import {
pageContentStyles,
searchField,
} from "../../Common/FormComponents/common/styleLibrary";
import { AppState } from "../../../../store";
import { ErrorResponseHandler } from "../../../../common/types";
import PageHeader from "../../Common/PageHeader/PageHeader";
@@ -51,8 +49,17 @@ import {
import withSuspense from "../../Common/Components/withSuspense";
import RBIconButton from "./SummaryItems/RBIconButton";
import { TrashIcon } from "../../../../icons";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { setBucketDetailsLoad, setBucketInfo } from "../bucketsSlice";
import {
selDistSet,
selSiteRep,
setErrorSnackMessage,
} from "../../../../systemSlice";
import {
selBucketDetailsInfo,
selBucketDetailsLoading,
setBucketDetailsLoad,
setBucketInfo,
} from "./bucketDetailsSlice";
const BucketsIcon = React.lazy(() => import("../../../../icons/BucketsIcon"));
const FolderIcon = React.lazy(() => import("../../../../icons/FolderIcon"));
@@ -107,18 +114,10 @@ interface IBucketDetailsProps {
const BucketDetails = ({ classes, match, history }: IBucketDetailsProps) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const bucketInfo = useSelector(
(state: AppState) => state.buckets.bucketDetails.bucketInfo
);
const siteReplicationInfo = useSelector(
(state: AppState) => state.system.siteReplicationInfo
);
const distributedSetup = useSelector(selDistSet);
const loadingBucket = useSelector(selBucketDetailsLoading);
const bucketInfo = useSelector(selBucketDetailsInfo);
const siteReplicationInfo = useSelector(selSiteRep);
const [iniLoad, setIniLoad] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);

View File

@@ -24,8 +24,6 @@ import Grid from "@mui/material/Grid";
import AddIcon from "../../../../icons/AddIcon";
import LambdaIcon from "../../../../icons/LambdaIcon";
import { BucketEvent, BucketEventList } from "../types";
import { AppState } from "../../../../store";
import {
actionsTray,
searchField,
@@ -45,6 +43,7 @@ import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
import withSuspense from "../../Common/Components/withSuspense";
import RBIconButton from "./SummaryItems/RBIconButton";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
const DeleteEvent = withSuspense(React.lazy(() => import("./DeleteEvent")));
const AddEvent = withSuspense(React.lazy(() => import("./AddEvent")));
@@ -66,9 +65,7 @@ interface IBucketEventsProps {
const BucketEventsPanel = ({ classes, match }: IBucketEventsProps) => {
const dispatch = useDispatch();
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const loadingBucket = useSelector(selBucketDetailsLoading);
const [addEventScreenOpen, setAddEventScreenOpen] = useState<boolean>(false);
const [loadingEvents, setLoadingEvents] = useState<boolean>(true);

View File

@@ -27,8 +27,6 @@ import {
actionsTray,
searchField,
} from "../../Common/FormComponents/common/styleLibrary";
import { AppState } from "../../../../store";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import EditLifecycleConfiguration from "./EditLifecycleConfiguration";
@@ -43,6 +41,7 @@ import {
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
import RBIconButton from "./SummaryItems/RBIconButton";
import DeleteBucketLifecycleRule from "./DeleteBucketLifecycleRule";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
const styles = (theme: Theme) =>
createStyles({
@@ -62,9 +61,7 @@ const BucketLifecyclePanel = ({
classes,
match,
}: IBucketLifecyclePanelProps) => {
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const loadingBucket = useSelector(selBucketDetailsLoading);
const [loadingLifecycle, setLoadingLifecycle] = useState<boolean>(true);
const [lifecycleRecords, setLifecycleRecords] = useState<LifeCycleItem[]>([]);

View File

@@ -31,7 +31,6 @@ import {
BucketReplicationRule,
} from "../types";
import { ErrorResponseHandler } from "../../../../common/types";
import { AppState } from "../../../../store";
import {
hasPermission,
SecureComponent,
@@ -46,6 +45,7 @@ import withSuspense from "../../Common/Components/withSuspense";
import RBIconButton from "./SummaryItems/RBIconButton";
import EditReplicationModal from "./EditReplicationModal";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
const AddReplicationModal = withSuspense(
React.lazy(() => import("./AddReplicationModal"))
@@ -74,9 +74,7 @@ const BucketReplicationPanel = ({
}: IBucketReplicationProps) => {
const dispatch = useDispatch();
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const loadingBucket = useSelector(selBucketDetailsLoading);
const [loadingReplication, setLoadingReplication] = useState<boolean>(true);
const [replicationRules, setReplicationRules] = useState<

View File

@@ -21,7 +21,6 @@ import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box, Grid } from "@mui/material";
import get from "lodash/get";
import { AppState } from "../../../../store";
import {
BucketEncryptionInfo,
BucketObjectLocking,
@@ -54,8 +53,12 @@ import EditablePropertyItem from "./SummaryItems/EditablePropertyItem";
import ReportedUsage from "./SummaryItems/ReportedUsage";
import BucketQuotaSize from "./SummaryItems/BucketQuotaSize";
import SectionTitle from "../../Common/SectionTitle";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { setBucketDetailsLoad } from "../bucketsSlice";
import { selDistSet, setErrorSnackMessage } from "../../../../systemSlice";
import {
selBucketDetailsInfo,
selBucketDetailsLoading,
setBucketDetailsLoad,
} from "./bucketDetailsSlice";
const SetAccessPolicy = withSuspense(
React.lazy(() => import("./SetAccessPolicy"))
@@ -96,16 +99,10 @@ interface IBucketSummaryProps {
const BucketSummary = ({ classes, match }: IBucketSummaryProps) => {
const dispatch = useDispatch();
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const bucketInfo = useSelector(
(state: AppState) => state.buckets.bucketDetails.bucketInfo
);
const loadingBucket = useSelector(selBucketDetailsLoading);
const bucketInfo = useSelector(selBucketDetailsInfo);
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [encryptionCfg, setEncryptionCfg] =
useState<BucketEncryptionInfo | null>(null);

View File

@@ -24,21 +24,13 @@ import {
modalStyleUtils,
spacingUtils,
} from "../../Common/FormComponents/common/styleLibrary";
import { connect, useDispatch } from "react-redux";
import { useDispatch } from "react-redux";
import api from "../../../../common/api";
import { ErrorResponseHandler } from "../../../../common/types";
import { AppState } from "../../../../store";
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import { AddAccessRuleIcon } from "../../../../icons";
import { setErrorSnackMessage } from "../../../../systemSlice";
const mapState = (state: AppState) => ({
session: state.console.session,
});
const connector = connect(mapState, null);
interface IEditAccessRule {
classes: any;
modalOpen: boolean;
@@ -136,4 +128,4 @@ const EditAccessRule = ({
);
};
export default withStyles(styles)(connector(EditAccessRule));
export default withStyles(styles)(EditAccessRule);

View File

@@ -0,0 +1,57 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { BucketInfo } from "../types";
import { AppState } from "../../../../store";
export interface BucketDetailsState {
selectedTab: string;
loadingBucket: boolean;
bucketInfo: BucketInfo | null;
}
const initialState: BucketDetailsState = {
selectedTab: "summary",
loadingBucket: false,
bucketInfo: null,
};
export const bucketDetailsSlice = createSlice({
name: "trace",
initialState,
reducers: {
setBucketDetailsTab: (state, action: PayloadAction<string>) => {
state.selectedTab = action.payload;
},
setBucketDetailsLoad: (state, action: PayloadAction<boolean>) => {
state.loadingBucket = action.payload;
},
setBucketInfo: (state, action: PayloadAction<BucketInfo | null>) => {
state.bucketInfo = action.payload;
},
},
});
export const { setBucketDetailsTab, setBucketInfo, setBucketDetailsLoad } =
bucketDetailsSlice.actions;
export const selBucketDetailsLoading = (state: AppState) =>
state.bucketDetails.loadingBucket;
export const selBucketDetailsInfo = (state: AppState) =>
state.bucketDetails.bucketInfo;
export default bucketDetailsSlice.reducer;

View File

@@ -41,7 +41,11 @@ import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMe
import FormLayout from "../../Common/FormLayout";
import HelpBox from "../../../../common/HelpBox";
import SectionTitle from "../../Common/SectionTitle";
import { setErrorSnackMessage } from "../../../../systemSlice";
import {
selDistSet,
selSiteRep,
setErrorSnackMessage,
} from "../../../../systemSlice";
import {
addBucketEnableObjectLocking,
addBucketName,
@@ -141,12 +145,8 @@ const AddBucket = ({ classes }: IAddBucketProps) => {
const retentionValidity = useSelector(
(state: AppState) => state.buckets.addBucketRetentionValidity
);
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const siteReplicationInfo = useSelector(
(state: AppState) => state.system.siteReplicationInfo
);
const distributedSetup = useSelector(selDistSet);
const siteReplicationInfo = useSelector(selSiteRep);
const [addLoading, setAddLoading] = useState<boolean>(false);
const [sendEnabled, setSendEnabled] = useState<boolean>(false);

View File

@@ -95,7 +95,7 @@ import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../../../systemSlice";
import { setBucketDetailsLoad, setBucketInfo } from "../../../bucketsSlice";
import {
makeid,
storeCallForObjectWithID,
@@ -119,6 +119,12 @@ import {
updateProgress,
} from "../../../../ObjectBrowser/objectBrowserSlice";
import makeStyles from "@mui/styles/makeStyles";
import {
selBucketDetailsInfo,
selBucketDetailsLoading,
setBucketDetailsLoad,
setBucketInfo,
} from "../../../BucketDetails/bucketDetailsSlice";
const HistoryIcon = React.lazy(
() => import("../../../../../../icons/HistoryIcon")
@@ -303,12 +309,8 @@ const ListObjects = ({ match, history }: IListObjectsProps) => {
(state: AppState) => state.objectBrowser.simplePath
);
const loadingBucket = useSelector(
(state: AppState) => state.buckets.bucketDetails.loadingBucket
);
const bucketInfo = useSelector(
(state: AppState) => state.buckets.bucketDetails.bucketInfo
);
const loadingBucket = useSelector(selBucketDetailsLoading);
const bucketInfo = useSelector(selBucketDetailsInfo);
const allowResources = useSelector(
(state: AppState) => state.console.session.allowResources
);
@@ -921,14 +923,14 @@ const ListObjects = ({ match, history }: IListObjectsProps) => {
errorMessage = "something went wrong";
}
}
dispatch(dispatch(failObject(identity)));
dispatch(failObject(identity));
reject({ status: xhr.status, message: errorMessage });
}
};
xhr.upload.addEventListener("error", (event) => {
reject(errorMessage);
dispatch(dispatch(failObject(identity)));
dispatch(failObject(identity));
return;
});
@@ -1014,7 +1016,6 @@ const ListObjects = ({ match, history }: IListObjectsProps) => {
[bucketName, dispatch, simplePath]
);
const onDrop = useCallback(
(acceptedFiles: any[]) => {
if (acceptedFiles && acceptedFiles.length > 0) {

View File

@@ -71,6 +71,7 @@ import { displayFileIconName } from "./utils";
import TagsModal from "../ObjectDetails/TagsModal";
import InspectObject from "./InspectObject";
import Loader from "../../../../Common/Loader/Loader";
import { selDistSet } from "../../../../../../systemSlice";
import {
makeid,
storeCallForObjectWithID,
@@ -159,9 +160,7 @@ const ObjectDetailPanel = ({
}: IObjectDetailPanelProps) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const versionsMode = useSelector(
(state: AppState) => state.objectBrowser.versionsMode
);

View File

@@ -29,7 +29,6 @@ import {
} from "../../../../Common/FormComponents/common/styleLibrary";
import { IFileInfo } from "./types";
import { AppState } from "../../../../../../store";
import { ErrorResponseHandler } from "../../../../../../common/types";
import api from "../../../../../../common/api";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
@@ -39,6 +38,7 @@ import { encodeURLString } from "../../../../../../common/utils";
import { ShareIcon } from "../../../../../../icons";
import BoxIconButton from "../../../../Common/BoxIconButton/BoxIconButton";
import {
selDistSet,
setModalErrorSnackMessage,
setModalSnackMessage,
} from "../../../../../../systemSlice";
@@ -95,9 +95,7 @@ const ShareFile = ({
dataObject,
}: IShareFileProps) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [shareURL, setShareURL] = useState<string>("");
const [isLoadingVersion, setIsLoadingVersion] = useState<boolean>(true);
const [isLoadingFile, setIsLoadingFile] = useState<boolean>(false);

View File

@@ -21,8 +21,6 @@ import { Box, Button, Grid } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { AppState } from "../../../../../../store";
import { ErrorResponseHandler } from "../../../../../../common/types";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
@@ -43,7 +41,10 @@ import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions
import { SecureComponent } from "../../../../../../common/SecureComponent";
import Chip from "@mui/material/Chip";
import CloseIcon from "@mui/icons-material/Close";
import { setModalErrorSnackMessage } from "../../../../../../systemSlice";
import {
selDistSet,
setModalErrorSnackMessage,
} from "../../../../../../systemSlice";
interface ITagModal {
modalOpen: boolean;
@@ -99,9 +100,7 @@ const AddTagModal = ({
classes,
}: ITagModal) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [newKey, setNewKey] = useState<string>("");
const [newLabel, setNewLabel] = useState<string>("");
const [isSending, setIsSending] = useState<boolean>(false);

View File

@@ -61,7 +61,10 @@ import RBIconButton from "../../../BucketDetails/SummaryItems/RBIconButton";
import DeleteNonCurrent from "../ListObjects/DeleteNonCurrent";
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
import DeleteSelectedVersions from "./DeleteSelectedVersions";
import { setErrorSnackMessage } from "../../../../../../systemSlice";
import {
selDistSet,
setErrorSnackMessage,
} from "../../../../../../systemSlice";
import {
makeid,
storeCallForObjectWithID,
@@ -171,9 +174,7 @@ const VersionsNavigator = ({
(state: AppState) => state.objectBrowser.selectedVersion
);
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [shareFileModalOpen, setShareFileModalOpen] = useState<boolean>(false);
const [actualInfo, setActualInfo] = useState<IFileInfo | null>(null);
const [objectToShare, setObjectToShare] = useState<IFileInfo | null>(null);

View File

@@ -15,7 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { BucketInfo } from "./types";
export interface BucketsState {
open: boolean;
@@ -30,13 +29,6 @@ export interface BucketsState {
addBucketRetentionMode: string;
addBucketRetentionUnit: string;
addBucketRetentionValidity: number;
bucketDetails: BucketDetailsState;
}
export interface BucketDetailsState {
selectedTab: string;
loadingBucket: boolean;
bucketInfo: BucketInfo | null;
}
const initialState: BucketsState = {
@@ -52,11 +44,6 @@ const initialState: BucketsState = {
addBucketRetentionMode: "compliance",
addBucketRetentionUnit: "days",
addBucketRetentionValidity: 180,
bucketDetails: {
selectedTab: "summary",
loadingBucket: false,
bucketInfo: null,
},
};
export const bucketsSlice = createSlice({
@@ -99,9 +86,7 @@ export const bucketsSlice = createSlice({
addBucketRetentionValidity: (state, action: PayloadAction<number>) => {
state.addBucketRetentionValidity = action.payload;
},
setBucketDetailsTab: (state, action: PayloadAction<string>) => {
state.bucketDetails.selectedTab = action.payload;
},
addBucketReset: (state) => {
state.addBucketName = "";
state.addBucketVersioningEnabled = false;
@@ -115,12 +100,6 @@ export const bucketsSlice = createSlice({
state.addBucketRetentionUnit = "days";
state.addBucketRetentionValidity = 180;
},
setBucketDetailsLoad: (state, action: PayloadAction<boolean>) => {
state.bucketDetails.loadingBucket = action.payload;
},
setBucketInfo: (state, action: PayloadAction<BucketInfo | null>) => {
state.bucketDetails.bucketInfo = action.payload;
},
},
});
@@ -138,9 +117,6 @@ export const {
addBucketRetentionMode,
addBucketRetentionUnit,
addBucketRetentionValidity,
setBucketDetailsTab,
setBucketDetailsLoad,
setBucketInfo,
} = bucketsSlice.actions;
export default bucketsSlice.reducer;

View File

@@ -34,10 +34,11 @@ import makeStyles from "@mui/styles/makeStyles";
import { routesAsKbarActions } from "./kbar-actions";
import { Box } from "@mui/material";
import { MenuExpandedIcon } from "../../icons/SidebarMenus";
import { AppState } from "../../store";
import { connect } from "react-redux";
import { useSelector } from "react-redux";
import useApi from "./Common/Hooks/useApi";
import { Bucket, BucketList } from "./Buckets/types";
import { selFeatures } from "./consoleSlice";
import { selOpMode } from "../../systemSlice";
const useStyles = makeStyles((theme: Theme) => ({
resultItem: {
@@ -124,13 +125,10 @@ const KBarStateChangeMonitor = ({
return null;
};
const CommandBar = ({
features = [],
operatorMode = false,
}: {
operatorMode?: boolean;
features?: string[] | null;
}) => {
const CommandBar = () => {
const operatorMode = useSelector(selOpMode);
const features = useSelector(selFeatures);
const [buckets, setBuckets] = useState<Bucket[]>([]);
const [, invokeListBucketsApi] = useApi(
@@ -319,11 +317,4 @@ const ResultItem = React.forwardRef(
}
);
const mapState = (state: AppState) => ({
operatorMode: state.system.operatorMode,
features: state.console.session.features,
});
const connector = connect(mapState, null);
export default connector(CommandBar);
export default CommandBar;

View File

@@ -28,6 +28,8 @@ import ConsoleLogo from "../../../../icons/ConsoleLogo";
import { CircleIcon, ObjectManagerIcon } from "../../../../icons";
import { Box } from "@mui/material";
import { toggleList } from "../../ObjectBrowser/objectBrowserSlice";
import { selFeatures } from "../../consoleSlice";
import { selOpMode } from "../../../../systemSlice";
const styles = (theme: Theme) =>
createStyles({
@@ -104,15 +106,11 @@ const PageHeader = ({
const sidebarOpen = useSelector(
(state: AppState) => state.system.sidebarOpen
);
const operatorMode = useSelector(
(state: AppState) => state.system.operatorMode
);
const operatorMode = useSelector(selOpMode);
const managerObjects = useSelector(
(state: AppState) => state.objectBrowser.objectManager.objectsToManage
);
const features = useSelector(
(state: AppState) => state.console.session.features
);
const features = useSelector(selFeatures);
const managerOpen = useSelector(
(state: AppState) => state.objectBrowser.objectManager.managerOpen
);

View File

@@ -45,7 +45,6 @@ import PageLayout from "../../Common/Layout/PageLayout";
import SearchBox from "../../Common/SearchBox";
import withSuspense from "../../Common/Components/withSuspense";
import { AppState } from "../../../../store";
import DistributedOnly from "../../Common/DistributedOnly/DistributedOnly";
import {
CONSOLE_UI_RESOURCE,
@@ -55,7 +54,7 @@ import {
import { SecureComponent } from "../../../../common/SecureComponent";
import { tierTypes } from "./utils";
import RBIconButton from "../../Buckets/BucketDetails/SummaryItems/RBIconButton";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { selDistSet, setErrorSnackMessage } from "../../../../systemSlice";
const UpdateTierCredentialsModal = withSuspense(
React.lazy(() => import("./UpdateTierCredentialsModal"))
@@ -96,9 +95,7 @@ const styles = (theme: Theme) =>
const ListTiersConfiguration = ({ classes, history }: IListTiersConfig) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [records, setRecords] = useState<ITierElement[]>([]);
const [filter, setFilter] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(true);

View File

@@ -53,10 +53,13 @@ import EditPool from "./Tenants/TenantDetails/Pools/EditPool/EditPool";
import ComponentsScreen from "./Common/ComponentsScreen";
import {
menuOpen,
selDistSet,
selOpMode,
serverIsLoading,
setServerNeedsRestart,
setSnackBarMessage,
} from "../../systemSlice";
import { selFeatures, selSession } from "./consoleSlice";
const Trace = React.lazy(() => import("./Trace/Trace"));
const Heal = React.lazy(() => import("./Heal/Heal"));
@@ -181,16 +184,10 @@ interface IConsoleProps {
const Console = ({ classes }: IConsoleProps) => {
const dispatch = useDispatch();
const open = useSelector((state: AppState) => state.system.sidebarOpen);
const session = useSelector((state: AppState) => state.console.session);
const features = useSelector(
(state: AppState) => state.console.session.features
);
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const operatorMode = useSelector(
(state: AppState) => state.system.operatorMode
);
const session = useSelector(selSession);
const features = useSelector(selFeatures);
const distributedSetup = useSelector(selDistSet);
const operatorMode = useSelector(selOpMode);
const snackBarMessage = useSelector(
(state: AppState) => state.system.snackBar
);

View File

@@ -16,14 +16,12 @@
import * as React from "react";
import { KBarProvider } from "kbar";
import Console from "./Console";
import { AppState } from "../../store";
import { useSelector } from "react-redux";
import CommandBar from "./CommandBar";
import { selFeatures } from "./consoleSlice";
const ConsoleKBar = () => {
const features = useSelector(
(state: AppState) => state.console.session.features
);
const features = useSelector(selFeatures);
// if we are hiding the menu also disable the k-bar so just return console
if (features?.includes("hide-menu")) {
return <Console />;

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect } from "react-redux";
import { useSelector } from "react-redux";
import { HorizontalBar } from "react-chartjs-2";
import {
Button,
@@ -44,7 +44,6 @@ import {
CONSOLE_UI_RESOURCE,
IAM_SCOPES,
} from "../../../common/SecureComponent/permissions";
import { AppState } from "../../../store";
import { ErrorResponseHandler } from "../../../common/types";
import { HealIcon } from "../../../icons";
import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
@@ -53,8 +52,10 @@ import api from "../../../common/api";
import PageLayout from "../Common/Layout/PageLayout";
import { SecureComponent } from "../../../common/SecureComponent";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import { selDistSet } from "../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
graphContainer: {
backgroundColor: "#fff",
@@ -96,12 +97,8 @@ const styles = (theme: Theme) =>
...inlineCheckboxes,
...searchField,
...containerForHeader(theme.spacing(4)),
});
interface IHeal {
classes: any;
distributedSetup: boolean;
}
})
);
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
@@ -118,7 +115,10 @@ const SelectStyled = withStyles((theme: Theme) =>
})
)(InputBase);
const Heal = ({ classes, distributedSetup }: IHeal) => {
const Heal = () => {
const classes = useStyles();
const distributedSetup = useSelector(selDistSet);
const [start, setStart] = useState(false);
const [bucketName, setBucketName] = useState("");
const [bucketList, setBucketList] = useState<Bucket[]>([]);
@@ -389,10 +389,4 @@ const Heal = ({ classes, distributedSetup }: IHeal) => {
);
};
const mapState = (state: AppState) => ({
distributedSetup: state.system.distributedSetup,
});
const connector = connect(mapState, null);
export default connector(withStyles(styles)(Heal));
export default Heal;

View File

@@ -15,17 +15,15 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box, LinearProgress } from "@mui/material";
import clsx from "clsx";
import Grid from "@mui/material/Grid";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { SubnetInfo } from "./types";
import { AppState } from "../../../store";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import PageHeader from "../Common/PageHeader/PageHeader";
import LicenseModal from "./LicenseModal";
@@ -46,14 +44,10 @@ import LicensePlans from "./LicensePlans";
import { Link } from "react-router-dom";
import PageLayout from "../Common/Layout/PageLayout";
import RegistrationStatusBanner from "../Support/RegistrationStatusBanner";
import makeStyles from "@mui/styles/makeStyles";
import { selOpMode } from "../../../systemSlice";
const mapState = (state: AppState) => ({
operatorMode: state.system.operatorMode,
});
const connector = connect(mapState, null);
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
pageTitle: {
backgroundColor: "rgb(250,250,252)",
@@ -120,14 +114,12 @@ const styles = (theme: Theme) =>
marginRight: 15,
},
},
});
})
);
interface ILicenseProps {
classes: any;
operatorMode: boolean;
}
const License = ({ classes, operatorMode }: ILicenseProps) => {
const License = () => {
const classes = useStyles();
const operatorMode = useSelector(selOpMode);
const [activateProductModal, setActivateProductModal] =
useState<boolean>(false);
@@ -454,4 +446,4 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
);
};
export default connector(withStyles(styles)(License));
export default License;

View File

@@ -18,7 +18,7 @@ import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { connect } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import {
Button,
FormControl,
@@ -32,11 +32,7 @@ import moment from "moment/moment";
import { ErrorResponseHandler } from "../../../../../src/common/types";
import api from "../../../../../src/common/api";
import { AppState } from "../../../../store";
import {
logMessageReceived,
logResetMessages,
setLogsStarted,
} from "../actions";
import { LogMessage } from "../types";
import { wsProtocol } from "../../../../utils/wsUtils";
import {
@@ -54,8 +50,14 @@ import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableContainer from "@mui/material/TableContainer";
import LogLine from "./LogLine";
import {
logMessageReceived,
logResetMessages,
setLogsStarted,
} from "../logsSlice";
import makeStyles from "@mui/styles/makeStyles";
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
logList: {
background: "#fff",
@@ -86,7 +88,8 @@ const styles = (theme: Theme) =>
...logsCommon,
...inlineCheckboxes,
...containerForHeader(theme.spacing(4)),
});
})
);
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
@@ -102,25 +105,15 @@ const SelectStyled = withStyles((theme: Theme) =>
})
)(InputBase);
interface ILogs {
classes: any;
logMessageReceived: typeof logMessageReceived;
logResetMessages: typeof logResetMessages;
setLogsStarted: typeof setLogsStarted;
messages: LogMessage[];
logsStarted: boolean;
}
var c: any = null;
const ErrorLogs = ({
classes,
logMessageReceived,
logResetMessages,
setLogsStarted,
messages,
logsStarted,
}: ILogs) => {
const ErrorLogs = () => {
const dispatch = useDispatch();
const classes = useStyles();
const messages = useSelector((state: AppState) => state.logs.logMessages);
const logsStarted = useSelector((state: AppState) => state.logs.logsStarted);
const [filter, setFilter] = useState<string>("");
const [nodes, setNodes] = useState<string[]>([""]);
const [selectedNode, setSelectedNode] = useState<string>("all");
@@ -131,7 +124,7 @@ const ErrorLogs = ({
const [loadingNodes, setLoadingNodes] = useState<boolean>(false);
const startLogs = () => {
logResetMessages();
dispatch(logResetMessages());
const url = new URL(window.location.toString());
const isDev = process.env.NODE_ENV === "development";
const port = isDev ? "9090" : url.port;
@@ -152,7 +145,7 @@ const ErrorLogs = ({
if (c !== null) {
c.onopen = () => {
console.log("WebSocket Client Connected");
setLogsStarted(true);
dispatch(setLogsStarted(true));
c.send("ok");
interval = setInterval(() => {
c.send("ok");
@@ -169,18 +162,18 @@ const ErrorLogs = ({
userAgents.push(m.userAgent);
setUserAgents(userAgents);
}
logMessageReceived(m);
dispatch(logMessageReceived(m));
};
c.onclose = () => {
clearInterval(interval);
console.log("connection closed by server");
setLogsStarted(false);
dispatch(setLogsStarted(false));
};
return () => {
c.close(1000);
clearInterval(interval);
console.log("closing websockets");
setLogsStarted(false);
dispatch(setLogsStarted(false));
};
}
};
@@ -188,7 +181,7 @@ const ErrorLogs = ({
const stopLogs = () => {
if (c !== null && c !== undefined) {
c.close(1000);
setLogsStarted(false);
dispatch(setLogsStarted(false));
}
};
@@ -397,16 +390,5 @@ const ErrorLogs = ({
);
};
const mapState = (state: AppState) => ({
messages: state.logs.logMessages,
logsStarted: state.logs.logsStarted,
});
const connector = connect(mapState, {
logMessageReceived: logMessageReceived,
logResetMessages: logResetMessages,
setLogsStarted,
});
//export default withStyles(styles)(connector(ErrorLogs));
export default connector(withStyles(styles)(ErrorLogs));
export default ErrorLogs;

View File

@@ -29,8 +29,6 @@ import {
} from "../../Common/FormComponents/common/styleLibrary";
import { IReqInfoSearchResults, ISearchResponse } from "./types";
import { niceBytes, nsToSeconds } from "../../../../common/utils";
import { AppState } from "../../../../store";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
@@ -49,6 +47,7 @@ import { SecureComponent } from "../../../../common/SecureComponent";
import { SearchIcon } from "../../../../icons";
import MissingIntegration from "../../Common/MissingIntegration/MissingIntegration";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { selFeatures } from "../../consoleSlice";
interface ILogSearchProps {
classes: any;
@@ -122,9 +121,7 @@ const styles = (theme: Theme) =>
const LogsSearchMain = ({ classes }: ILogSearchProps) => {
const dispatch = useDispatch();
const features = useSelector(
(state: AppState) => state.console.session.features
);
const features = useSelector(selFeatures);
const [loading, setLoading] = useState<boolean>(true);
const [timeStart, setTimeStart] = useState<any>(null);

View File

@@ -1,60 +0,0 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { LogMessage } from "./types";
export const LOG_MESSAGE_RECEIVED = "LOG_MESSAGE_RECEIVED";
export const LOG_RESET_MESSAGES = "LOG_RESET_MESSAGES";
export const LOG_SET_STARTED = "LOG_SET_STARTED";
interface LogMessageReceivedAction {
type: typeof LOG_MESSAGE_RECEIVED;
message: LogMessage;
}
interface LogResetMessagesAction {
type: typeof LOG_RESET_MESSAGES;
}
interface LogSetStarted {
type: typeof LOG_SET_STARTED;
status: boolean;
}
export type LogActionTypes =
| LogMessageReceivedAction
| LogResetMessagesAction
| LogSetStarted;
export function logMessageReceived(message: LogMessage) {
return {
type: LOG_MESSAGE_RECEIVED,
message: message,
};
}
export function logResetMessages() {
return {
type: LOG_RESET_MESSAGES,
};
}
export function setLogsStarted(status: boolean) {
return {
type: LOG_SET_STARTED,
status,
};
}

View File

@@ -1,5 +1,5 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
@@ -14,12 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import {
LOG_MESSAGE_RECEIVED,
LOG_RESET_MESSAGES,
LOG_SET_STARTED,
LogActionTypes,
} from "./actions";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { LogMessage } from "./types";
export interface LogState {
@@ -32,47 +27,40 @@ const initialState: LogState = {
logsStarted: false,
};
export function logReducer(
state = initialState,
action: LogActionTypes
): LogState {
switch (action.type) {
case LOG_MESSAGE_RECEIVED:
// if it's a simple ConsoleMsg, append it to the current ConsoleMsg in the
// state if any
let msgs = [...state.logMessages];
export const logsSlice = createSlice({
name: "logs",
initialState,
reducers: {
logMessageReceived: (state, action: PayloadAction<LogMessage>) => {
let msgs = state.logMessages;
if (
msgs.length > 0 &&
action.message.time.getFullYear() === 1 &&
action.message.ConsoleMsg !== ""
action.payload.time.getFullYear() === 1 &&
action.payload.ConsoleMsg !== ""
) {
for (let m in msgs) {
if (msgs[m].time.getFullYear() === 1) {
msgs[
m
].ConsoleMsg = `${msgs[m].ConsoleMsg}\n${action.message.ConsoleMsg}`;
].ConsoleMsg = `${msgs[m].ConsoleMsg}\n${action.payload.ConsoleMsg}`;
}
}
} else {
msgs.push(action.message);
msgs.push(action.payload);
}
state.logMessages = msgs;
},
logResetMessages: (state) => {
state.logMessages = [];
},
setLogsStarted: (state, action: PayloadAction<boolean>) => {
state.logsStarted = action.payload;
},
},
});
return {
...state,
logMessages: msgs,
};
case LOG_RESET_MESSAGES:
return {
...state,
logMessages: [],
};
case LOG_SET_STARTED:
return {
...state,
logsStarted: action.status,
};
default:
return state;
}
}
export const { logMessageReceived, logResetMessages, setLogsStarted } =
logsSlice.actions;
export default logsSlice.reducer;

View File

@@ -32,8 +32,8 @@ import api from "../../../common/api";
import MenuToggle from "./MenuToggle";
import ConsoleMenuList from "./ConsoleMenuList";
import { validRoutes } from "../valid-routes";
import { menuOpen, userLogged } from "../../../systemSlice";
import { resetSession } from "../consoleSlice";
import { menuOpen, selOpMode, userLogged } from "../../../systemSlice";
import { resetSession, selFeatures } from "../consoleSlice";
const drawerWidth = 250;
@@ -91,16 +91,12 @@ interface IMenuProps {
const Menu = ({ classes }: IMenuProps) => {
const dispatch = useDispatch();
const features = useSelector(
(state: AppState) => state.console.session.features
);
const features = useSelector(selFeatures);
const sidebarOpen = useSelector(
(state: AppState) => state.system.sidebarOpen
);
const operatorMode = useSelector(
(state: AppState) => state.system.operatorMode
);
const operatorMode = useSelector(selOpMode);
const logout = () => {
const deleteSession = () => {

View File

@@ -56,11 +56,11 @@ import {
} from "../../../common/SecureComponent";
import withSuspense from "../Common/Components/withSuspense";
import { AppState } from "../../../store";
import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton";
import PolicyView from "./PolicyView";
import { decodeURLString, encodeURLString } from "../../../common/utils";
import { setErrorSnackMessage, setSnackBarMessage } from "../../../systemSlice";
import { selFeatures } from "../consoleSlice";
const DeletePolicy = withSuspense(React.lazy(() => import("./DeletePolicy")));
@@ -100,9 +100,7 @@ interface IPolicyDetailsProps {
const PolicyDetails = ({ classes, match }: IPolicyDetailsProps) => {
const dispatch = useDispatch();
const features = useSelector(
(state: AppState) => state.console.session.features
);
const features = useSelector(selFeatures);
const [policy, setPolicy] = useState<Policy | null>(null);
const [policyStatements, setPolicyStatements] = useState<IAMStatement[]>([]);

View File

@@ -15,12 +15,11 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect } from "react-redux";
import { useSelector } from "react-redux";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { Button, Grid } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import moment from "moment/moment";
import PageHeader from "../Common/PageHeader/PageHeader";
import {
@@ -32,7 +31,6 @@ 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,
@@ -48,13 +46,10 @@ import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import HelpBox from "../../../common/HelpBox";
import WarnIcon from "../../../icons/WarnIcon";
import Loader from "../Common/Loader/Loader";
import { selDistSet } from "../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";
interface ISpeedtest {
classes: any;
distributedSetup: boolean;
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
advancedContent: {
backgroundColor: "#FBFAFA",
@@ -79,9 +74,12 @@ const styles = (theme: Theme) =>
...searchField,
...formFieldStyles,
...containerForHeader(theme.spacing(4)),
});
})
);
const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => {
const Speedtest = () => {
const distributedSetup = useSelector(selDistSet);
const classes = useStyles();
const [start, setStart] = useState<boolean>(false);
const [currStatus, setCurrStatus] = useState<SpeedTestResponse[] | null>(
@@ -311,10 +309,4 @@ const Speedtest = ({ classes, distributedSetup }: ISpeedtest) => {
);
};
const mapState = (state: AppState) => ({
distributedSetup: state.system.distributedSetup,
});
const connector = connect(mapState, null);
export default connector(withStyles(styles)(Speedtest));
export default Speedtest;

View File

@@ -58,12 +58,11 @@ import { useDispatch, useSelector } from "react-redux";
import SettingsIcon from "../../../icons/SettingsIcon";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import { AppState } from "../../../store";
import RegisterHelpBox from "./RegisterHelpBox";
import RegistrationStatusBanner from "./RegistrationStatusBanner";
import BackLink from "../../../common/BackLink";
import { setErrorSnackMessage } from "../../../systemSlice";
import { selOpMode, setErrorSnackMessage } from "../../../systemSlice";
interface IRegister {
classes: any;
@@ -153,9 +152,7 @@ const FormTitle = ({ icon = null, title }: { icon?: any; title: any }) => {
const Register = ({ classes }: IRegister) => {
const dispatch = useDispatch();
const operatorMode = useSelector(
(state: AppState) => state.system.operatorMode
);
const operatorMode = useSelector(selOpMode);
const [license, setLicense] = useState<string>("");
const [subnetPassword, setSubnetPassword] = useState<string>("");
const [subnetEmail, setSubnetEmail] = useState<string>("");

View File

@@ -22,7 +22,6 @@ import { LinearProgress } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
modalBasic,
settingsCommon,
@@ -59,13 +58,11 @@ import {
import HelpBox from "../../../../common/HelpBox";
import { StorageIcon } from "../../../../icons";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { resetAddTenantForm } from "../tenantsSlice";
import { selFeatures } from "../../consoleSlice";
import makeStyles from "@mui/styles/makeStyles";
import { resetAddTenantForm } from "./createTenantSlice";
interface IAddTenantProps {
classes: any;
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
pageBox: {
border: "1px solid #EAEAEA",
@@ -73,32 +70,30 @@ const styles = (theme: Theme) =>
...modalBasic,
...wizardCommon,
...settingsCommon,
});
})
);
const AddTenant = ({ classes }: IAddTenantProps) => {
const AddTenant = () => {
const dispatch = useDispatch();
const classes = useStyles();
const namespace = useSelector(
(state: AppState) => state.tenants.createTenant.fields.nameTenant.namespace
(state: AppState) => state.createTenant.fields.nameTenant.namespace
);
const validPages = useSelector(
(state: AppState) => state.tenants.createTenant.validPages
);
const fields = useSelector(
(state: AppState) => state.tenants.createTenant.fields
(state: AppState) => state.createTenant.validPages
);
const fields = useSelector((state: AppState) => state.createTenant.fields);
const certificates = useSelector(
(state: AppState) => state.tenants.createTenant.certificates
(state: AppState) => state.createTenant.certificates
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
);
const features = useSelector(
(state: AppState) => state.console.session.features
state.createTenant.fields.nameTenant.selectedStorageClass
);
const features = useSelector(selFeatures);
const tolerations = useSelector(
(state: AppState) => state.tenants.createTenant.tolerations
(state: AppState) => state.createTenant.tolerations
);
// Modals
@@ -874,4 +869,4 @@ const AddTenant = ({ classes }: IAddTenantProps) => {
);
};
export default withStyles(styles)(AddTenant);
export default AddTenant;

View File

@@ -48,7 +48,7 @@ import {
setKeyValuePairs,
setTolerationInfo,
updateAddField,
} from "../../tenantsSlice";
} from "../createTenantSlice";
interface IAffinityProps {
classes: any;
@@ -119,21 +119,19 @@ const Affinity = ({ classes }: IAffinityProps) => {
const dispatch = useDispatch();
const podAffinity = useSelector(
(state: AppState) => state.tenants.createTenant.fields.affinity.podAffinity
(state: AppState) => state.createTenant.fields.affinity.podAffinity
);
const nodeSelectorLabels = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.affinity.nodeSelectorLabels
(state: AppState) => state.createTenant.fields.affinity.nodeSelectorLabels
);
const withPodAntiAffinity = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.affinity.withPodAntiAffinity
(state: AppState) => state.createTenant.fields.affinity.withPodAntiAffinity
);
const keyValuePairs = useSelector(
(state: AppState) => state.tenants.createTenant.nodeSelectorPairs
(state: AppState) => state.createTenant.nodeSelectorPairs
);
const tolerations = useSelector(
(state: AppState) => state.tenants.createTenant.tolerations
(state: AppState) => state.createTenant.tolerations
);
const [validationErrors, setValidationErrors] = useState<any>({});
@@ -325,13 +323,12 @@ const Affinity = ({ classes }: IAffinityProps) => {
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const newKey = e.target.value as string;
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value as string;
arrCp[i].value = keyValueMap[newKey][0];
const newLKP: LabelKeyPair = {
key: newKey,
value: keyValueMap[newKey][0],
};
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = newLKP;
dispatch(setKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -348,11 +345,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.key}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setKeyValuePairs(arrCp));
}}
index={i}
@@ -364,11 +361,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
{keyOptions.length > 0 && (
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value as string;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -391,11 +388,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.value}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setKeyValuePairs(arrCp));
}}
index={i}
@@ -408,7 +405,7 @@ const Affinity = ({ classes }: IAffinityProps) => {
<IconButton
size={"small"}
onClick={() => {
const arrCp = Object.assign([], keyValuePairs);
const arrCp = [...keyValuePairs];
if (keyOptions.length > 0) {
arrCp.push({
key: keyOptions[0].value,

View File

@@ -36,7 +36,7 @@ import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/Inpu
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
import InputUnitMenu from "../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import SectionH1 from "../../../Common/SectionH1";
import { isPageValid, updateAddField } from "../../tenantsSlice";
import { isPageValid, updateAddField } from "../createTenantSlice";
interface IConfigureProps {
classes: any;
@@ -85,48 +85,44 @@ const ConfigLogSearch = ({ classes }: IConfigureProps) => {
const dispatch = useDispatch();
const storageClasses = useSelector(
(state: AppState) => state.tenants.createTenant.storageClasses
(state: AppState) => state.createTenant.storageClasses
);
const logSearchEnabled = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchEnabled
(state: AppState) => state.createTenant.fields.configure.logSearchEnabled
);
const logSearchVolumeSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchVolumeSize
(state: AppState) => state.createTenant.fields.configure.logSearchVolumeSize
);
const logSearchSelectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchSelectedStorageClass
state.createTenant.fields.configure.logSearchSelectedStorageClass
);
const logSearchImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchImage
(state: AppState) => state.createTenant.fields.configure.logSearchImage
);
const logSearchPostgresImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchPostgresImage
state.createTenant.fields.configure.logSearchPostgresImage
);
const logSearchPostgresInitImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchPostgresInitImage
state.createTenant.fields.configure.logSearchPostgresInitImage
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
state.createTenant.fields.nameTenant.selectedStorageClass
);
const tenantSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.tenantSecurityContext
state.createTenant.fields.configure.tenantSecurityContext
);
const logSearchSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchSecurityContext
state.createTenant.fields.configure.logSearchSecurityContext
);
const logSearchPostgresSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure
.logSearchPostgresSecurityContext
state.createTenant.fields.configure.logSearchPostgresSecurityContext
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -37,7 +37,7 @@ import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/Inpu
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
import InputUnitMenu from "../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import SectionH1 from "../../../Common/SectionH1";
import { isPageValid, updateAddField } from "../../tenantsSlice";
import { isPageValid, updateAddField } from "../createTenantSlice";
interface IConfigureProps {
classes: any;
@@ -85,43 +85,40 @@ const ConfigPrometheus = ({ classes }: IConfigureProps) => {
const dispatch = useDispatch();
const storageClasses = useSelector(
(state: AppState) => state.tenants.createTenant.storageClasses
(state: AppState) => state.createTenant.storageClasses
);
const prometheusEnabled = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusEnabled
(state: AppState) => state.createTenant.fields.configure.prometheusEnabled
);
const prometheusVolumeSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusVolumeSize
state.createTenant.fields.configure.prometheusVolumeSize
);
const prometheusSelectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusSelectedStorageClass
state.createTenant.fields.configure.prometheusSelectedStorageClass
);
const prometheusImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusImage
(state: AppState) => state.createTenant.fields.configure.prometheusImage
);
const prometheusSidecarImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusSidecarImage
state.createTenant.fields.configure.prometheusSidecarImage
);
const prometheusInitImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusInitImage
(state: AppState) => state.createTenant.fields.configure.prometheusInitImage
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
state.createTenant.fields.nameTenant.selectedStorageClass
);
const tenantSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.tenantSecurityContext
state.createTenant.fields.configure.tenantSecurityContext
);
const prometheusSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusSecurityContext
state.createTenant.fields.configure.prometheusSecurityContext
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -41,7 +41,7 @@ import {
isPageValid,
removeMinIODomain,
updateAddField,
} from "../../tenantsSlice";
} from "../createTenantSlice";
interface IConfigureProps {
classes: any;
@@ -105,30 +105,26 @@ const Configure = ({ classes }: IConfigureProps) => {
const dispatch = useDispatch();
const exposeMinIO = useSelector(
(state: AppState) => state.tenants.createTenant.fields.configure.exposeMinIO
(state: AppState) => state.createTenant.fields.configure.exposeMinIO
);
const exposeConsole = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.exposeConsole
(state: AppState) => state.createTenant.fields.configure.exposeConsole
);
const setDomains = useSelector(
(state: AppState) => state.tenants.createTenant.fields.configure.setDomains
(state: AppState) => state.createTenant.fields.configure.setDomains
);
const consoleDomain = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.consoleDomain
(state: AppState) => state.createTenant.fields.configure.consoleDomain
);
const minioDomains = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.minioDomains
(state: AppState) => state.createTenant.fields.configure.minioDomains
);
const tenantCustom = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.tenantCustom
(state: AppState) => state.createTenant.fields.configure.tenantCustom
);
const tenantSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.tenantSecurityContext
state.createTenant.fields.configure.tenantSecurityContext
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -41,13 +41,15 @@ import {
import SectionH1 from "../../../Common/SectionH1";
import {
addFileClientCert,
addFileGemaltoCa,
addFileServerCert,
addFileVaultCa,
addFileVaultCert,
isPageValid,
updateAddField,
} from "../../tenantsSlice";
} from "../createTenantSlice";
import VaultKMSAdd from "./Encryption/VaultKMSAdd";
import AzureKMSAdd from "./Encryption/AzureKMSAdd";
import GCPKMSAdd from "./Encryption/GCPKMSAdd";
import GemaltoKMSAdd from "./Encryption/GemaltoKMSAdd";
import AWSKMSAdd from "./Encryption/AWSKMSAdd";
interface IEncryptionProps {
classes: any;
@@ -83,166 +85,58 @@ const Encryption = ({ classes }: IEncryptionProps) => {
const dispatch = useDispatch();
const replicas = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.replicas
(state: AppState) => state.createTenant.fields.encryption.replicas
);
const enableEncryption = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.enableEncryption
(state: AppState) => state.createTenant.fields.encryption.enableEncryption
);
const encryptionType = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.encryptionType
);
const gemaltoEndpoint = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gemaltoEndpoint
);
const gemaltoToken = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gemaltoToken
);
const gemaltoDomain = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gemaltoDomain
);
const gemaltoRetry = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gemaltoRetry
);
const awsEndpoint = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.awsEndpoint
);
const awsRegion = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.awsRegion
);
const awsKMSKey = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.awsKMSKey
);
const awsAccessKey = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.awsAccessKey
);
const awsSecretKey = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.awsSecretKey
);
const awsToken = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.awsToken
);
const vaultEndpoint = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultEndpoint
);
const vaultEngine = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultEngine
);
const vaultNamespace = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultNamespace
);
const vaultPrefix = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultPrefix
);
const vaultAppRoleEngine = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultAppRoleEngine
);
const vaultId = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.vaultId
);
const vaultSecret = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.vaultSecret
);
const vaultRetry = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.vaultRetry
);
const vaultPing = useSelector(
(state: AppState) => state.tenants.createTenant.fields.encryption.vaultPing
);
const azureEndpoint = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.azureEndpoint
);
const azureTenantID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.azureTenantID
);
const azureClientID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.azureClientID
);
const azureClientSecret = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.azureClientSecret
(state: AppState) => state.createTenant.fields.encryption.encryptionType
);
const gcpProjectID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpProjectID
(state: AppState) => state.createTenant.fields.encryption.gcpProjectID
);
const gcpEndpoint = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpEndpoint
(state: AppState) => state.createTenant.fields.encryption.gcpEndpoint
);
const gcpClientEmail = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpClientEmail
(state: AppState) => state.createTenant.fields.encryption.gcpClientEmail
);
const gcpClientID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpClientID
(state: AppState) => state.createTenant.fields.encryption.gcpClientID
);
const gcpPrivateKeyID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpPrivateKeyID
(state: AppState) => state.createTenant.fields.encryption.gcpPrivateKeyID
);
const gcpPrivateKey = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.gcpPrivateKey
(state: AppState) => state.createTenant.fields.encryption.gcpPrivateKey
);
const enableCustomCertsForKES = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.enableCustomCertsForKES
state.createTenant.fields.encryption.enableCustomCertsForKES
);
const enableAutoCert = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.security.enableAutoCert
(state: AppState) => state.createTenant.fields.security.enableAutoCert
);
const enableTLS = useSelector(
(state: AppState) => state.tenants.createTenant.fields.security.enableTLS
(state: AppState) => state.createTenant.fields.security.enableTLS
);
const minioCertificates = useSelector(
(state: AppState) =>
state.tenants.createTenant.certificates.minioCertificates
(state: AppState) => state.createTenant.certificates.minioCertificates
);
const serverCertificate = useSelector(
(state: AppState) =>
state.tenants.createTenant.certificates.serverCertificate
(state: AppState) => state.createTenant.certificates.serverCertificate
);
const clientCertificate = useSelector(
(state: AppState) =>
state.tenants.createTenant.certificates.clientCertificate
);
const vaultCertificate = useSelector(
(state: AppState) =>
state.tenants.createTenant.certificates.vaultCertificate
);
const vaultCA = useSelector(
(state: AppState) => state.tenants.createTenant.certificates.vaultCA
);
const gemaltoCA = useSelector(
(state: AppState) => state.tenants.createTenant.certificates.gemaltoCA
(state: AppState) => state.createTenant.certificates.clientCertificate
);
const enableCustomCerts = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.security.enableCustomCerts
(state: AppState) => state.createTenant.fields.security.enableCustomCerts
);
const kesSecurityContext = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.encryption.kesSecurityContext
(state: AppState) => state.createTenant.fields.encryption.kesSecurityContext
);
const [validationErrors, setValidationErrors] = useState<any>({});
@@ -341,121 +235,6 @@ const Encryption = ({ classes }: IEncryptionProps) => {
},
];
}
if (encryptionType === "vault") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "vault_endpoint",
required: true,
value: vaultEndpoint,
},
{
fieldKey: "vault_id",
required: true,
value: vaultId,
},
{
fieldKey: "vault_secret",
required: true,
value: vaultSecret,
},
{
fieldKey: "vault_ping",
required: false,
value: vaultPing,
customValidation: parseInt(vaultPing) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
{
fieldKey: "vault_retry",
required: false,
value: vaultRetry,
customValidation: parseInt(vaultRetry) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
];
}
if (encryptionType === "aws") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "aws_endpoint",
required: true,
value: awsEndpoint,
},
{
fieldKey: "aws_region",
required: true,
value: awsRegion,
},
{
fieldKey: "aws_accessKey",
required: true,
value: awsAccessKey,
},
{
fieldKey: "aws_secretKey",
required: true,
value: awsSecretKey,
},
];
}
if (encryptionType === "gemalto") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "gemalto_endpoint",
required: true,
value: gemaltoEndpoint,
},
{
fieldKey: "gemalto_token",
required: true,
value: gemaltoToken,
},
{
fieldKey: "gemalto_domain",
required: true,
value: gemaltoDomain,
},
{
fieldKey: "gemalto_retry",
required: false,
value: gemaltoRetry,
customValidation: parseInt(gemaltoRetry) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
];
}
if (encryptionType === "azure") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "azure_endpoint",
required: true,
value: azureEndpoint,
},
{
fieldKey: "azure_tenant_id",
required: true,
value: azureTenantID,
},
{
fieldKey: "azure_client_id",
required: true,
value: azureClientID,
},
{
fieldKey: "azure_client_secret",
required: true,
value: azureClientSecret,
},
];
}
}
const commonVal = commonFormValidation(encryptionValidation);
@@ -471,30 +250,12 @@ const Encryption = ({ classes }: IEncryptionProps) => {
}, [
enableEncryption,
encryptionType,
vaultEndpoint,
vaultEngine,
vaultId,
vaultSecret,
vaultPing,
vaultRetry,
awsEndpoint,
awsRegion,
awsSecretKey,
awsAccessKey,
gemaltoEndpoint,
gemaltoToken,
gemaltoDomain,
gemaltoRetry,
gcpProjectID,
gcpEndpoint,
gcpClientEmail,
gcpClientID,
gcpPrivateKeyID,
gcpPrivateKey,
azureEndpoint,
azureTenantID,
azureClientID,
azureClientSecret,
dispatch,
enableAutoCert,
enableCustomCerts,
@@ -565,534 +326,11 @@ const Encryption = ({ classes }: IEncryptionProps) => {
]}
/>
</Grid>
{encryptionType === "vault" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_endpoint"
name="vault_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultEndpoint", e.target.value);
cleanValidation("vault_endpoint");
}}
label="Endpoint"
value={vaultEndpoint}
error={validationErrors["vault_endpoint"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_engine"
name="vault_engine"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultEngine", e.target.value);
cleanValidation("vault_engine");
}}
label="Engine"
value={vaultEngine}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_namespace"
name="vault_namespace"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultNamespace", e.target.value);
}}
label="Namespace"
value={vaultNamespace}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_prefix"
name="vault_prefix"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultPrefix", e.target.value);
}}
label="Prefix"
value={vaultPrefix}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
App Role
</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_approle_engine"
name="vault_approle_engine"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultAppRoleEngine", e.target.value);
}}
label="Engine"
value={vaultAppRoleEngine}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_id"
name="vault_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultId", e.target.value);
cleanValidation("vault_id");
}}
label="AppRole ID"
value={vaultId}
error={validationErrors["vault_id"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_secret"
name="vault_secret"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultSecret", e.target.value);
cleanValidation("vault_secret");
}}
label="AppRole Secret"
value={vaultSecret}
error={validationErrors["vault_secret"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
type="number"
min="0"
id="vault_retry"
name="vault_retry"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultRetry", e.target.value);
cleanValidation("vault_retry");
}}
label="Retry (Seconds)"
value={vaultRetry}
error={validationErrors["vault_retry"] || ""}
/>
</Grid>
</fieldset>
</Grid>
<Grid container className={classes.mutualTlsConfig}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Mutual TLS authentication (optional)
</legend>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCert({
key: "key",
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_key");
}}
accept=".key,.pem"
id="vault_key"
name="vault_key"
label="Key"
value={vaultCertificate.key}
/>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCert({
key: "cert",
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_cert");
}}
accept=".cer,.crt,.cert,.pem"
id="vault_cert"
name="vault_cert"
label="Cert"
value={vaultCertificate.cert}
/>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCa({
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_ca");
}}
accept=".cer,.crt,.cert,.pem"
id="vault_ca"
name="vault_ca"
label="CA"
value={vaultCA.cert}
/>
</fieldset>
</Grid>
<Grid
item
xs={12}
className={classes.formFieldRow}
style={{ marginTop: 15 }}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Status</legend>
<InputBoxWrapper
type="number"
min="0"
id="vault_ping"
name="vault_ping"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultPing", e.target.value);
cleanValidation("vault_ping");
}}
label="Ping (Seconds)"
value={vaultPing}
error={validationErrors["vault_ping"] || ""}
/>
</fieldset>
</Grid>
</Fragment>
)}
{encryptionType === "azure" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_endpoint"
name="azure_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureEndpoint", e.target.value);
cleanValidation("azure_endpoint");
}}
label="Endpoint"
value={azureEndpoint}
error={validationErrors["azure_endpoint"] || ""}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Credentials
</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_tenant_id"
name="azure_tenant_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureTenantID", e.target.value);
cleanValidation("azure_tenant_id");
}}
label="Tenant ID"
value={azureTenantID}
error={validationErrors["azure_tenant_id"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_client_id"
name="azure_client_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureClientID", e.target.value);
cleanValidation("azure_client_id");
}}
label="Client ID"
value={azureClientID}
error={validationErrors["azure_client_id"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_client_secret"
name="azure_client_secret"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureClientSecret", e.target.value);
cleanValidation("azure_client_secret");
}}
label="Client Secret"
value={azureClientSecret}
error={validationErrors["azure_client_secret"] || ""}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
)}
{encryptionType === "gcp" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_project_id"
name="gcp_project_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpProjectID", e.target.value);
}}
label="Project ID"
value={gcpProjectID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_endpoint"
name="gcp_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpEndpoint", e.target.value);
}}
label="Endpoint"
value={gcpEndpoint}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Credentials
</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_client_email"
name="gcp_client_email"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpClientEmail", e.target.value);
}}
label="Client Email"
value={gcpClientEmail}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_client_id"
name="gcp_client_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpClientID", e.target.value);
}}
label="Client ID"
value={gcpClientID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_private_key_id"
name="gcp_private_key_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpPrivateKeyID", e.target.value);
}}
label="Private Key ID"
value={gcpPrivateKeyID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_private_key"
name="gcp_private_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpPrivateKey", e.target.value);
}}
label="Private Key"
value={gcpPrivateKey}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
)}
{encryptionType === "aws" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_endpoint"
name="aws_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsEndpoint", e.target.value);
cleanValidation("aws_endpoint");
}}
label="Endpoint"
value={awsEndpoint}
error={validationErrors["aws_endpoint"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_region"
name="aws_region"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsRegion", e.target.value);
cleanValidation("aws_region");
}}
label="Region"
value={awsRegion}
error={validationErrors["aws_region"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_kmsKey"
name="aws_kmsKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsKMSKey", e.target.value);
}}
label="KMS Key"
value={awsKMSKey}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Credentials
</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_accessKey"
name="aws_accessKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsAccessKey", e.target.value);
cleanValidation("aws_accessKey");
}}
label="Access Key"
value={awsAccessKey}
error={validationErrors["aws_accessKey"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_secretKey"
name="aws_secretKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsSecretKey", e.target.value);
cleanValidation("aws_secretKey");
}}
label="Secret Key"
value={awsSecretKey}
error={validationErrors["aws_secretKey"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_token"
name="aws_token"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsToken", e.target.value);
}}
label="Token"
value={awsToken}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
)}
{encryptionType === "gemalto" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_endpoint"
name="gemalto_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoEndpoint", e.target.value);
cleanValidation("gemalto_endpoint");
}}
label="Endpoint"
value={gemaltoEndpoint}
error={validationErrors["gemalto_endpoint"] || ""}
required
/>
</Grid>
<Grid
item
xs={12}
style={{
marginBottom: 15,
}}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Credentials
</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_token"
name="gemalto_token"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoToken", e.target.value);
cleanValidation("gemalto_token");
}}
label="Token"
value={gemaltoToken}
error={validationErrors["gemalto_token"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_domain"
name="gemalto_domain"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoDomain", e.target.value);
cleanValidation("gemalto_domain");
}}
label="Domain"
value={gemaltoDomain}
error={validationErrors["gemalto_domain"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
type="number"
min="0"
id="gemalto_retry"
name="gemalto_retry"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoRetry", e.target.value);
cleanValidation("gemalto_retry");
}}
label="Retry (seconds)"
value={gemaltoRetry}
error={validationErrors["gemalto_retry"] || ""}
/>
</Grid>
</fieldset>
</Grid>
<Grid
item
xs={12}
style={{
marginBottom: 15,
}}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Custom CA Root certificate verification
</legend>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileGemaltoCa({
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("gemalto_ca");
}}
accept=".cer,.crt,.cert,.pem"
id="gemalto_ca"
name="gemalto_ca"
label="CA"
value={gemaltoCA.cert}
/>
</fieldset>
</Grid>
</Fragment>
)}
{encryptionType === "vault" && <VaultKMSAdd />}
{encryptionType === "azure" && <AzureKMSAdd />}
{encryptionType === "gcp" && <GCPKMSAdd />}
{encryptionType === "aws" && <AWSKMSAdd />}
{encryptionType === "gemalto" && <GemaltoKMSAdd />}
<div className={classes.headerElement}>
<h4 className={classes.h3Section}>Additional Configurations</h4>
</div>

View File

@@ -0,0 +1,232 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import makeStyles from "@mui/styles/makeStyles";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
import { isPageValid, updateAddField } from "../../createTenantSlice";
import { clearValidationError } from "../../../utils";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const AWSKMSAdd = () => {
const dispatch = useDispatch();
const classes = useStyles();
const enableEncryption = useSelector(
(state: AppState) => state.createTenant.fields.encryption.enableEncryption
);
const encryptionType = useSelector(
(state: AppState) => state.createTenant.fields.encryption.encryptionType
);
const awsEndpoint = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsEndpoint
);
const awsRegion = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsRegion
);
const awsKMSKey = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsKMSKey
);
const awsAccessKey = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsAccessKey
);
const awsSecretKey = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsSecretKey
);
const awsToken = useSelector(
(state: AppState) => state.createTenant.fields.encryption.awsToken
);
const [validationErrors, setValidationErrors] = useState<any>({});
// Validation
useEffect(() => {
let encryptionValidation: IValidation[] = [];
if (enableEncryption) {
if (encryptionType === "aws") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "aws_endpoint",
required: true,
value: awsEndpoint,
},
{
fieldKey: "aws_region",
required: true,
value: awsRegion,
},
{
fieldKey: "aws_accessKey",
required: true,
value: awsAccessKey,
},
{
fieldKey: "aws_secretKey",
required: true,
value: awsSecretKey,
},
];
}
}
const commonVal = commonFormValidation(encryptionValidation);
dispatch(
isPageValid({
pageName: "encryption",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
enableEncryption,
encryptionType,
awsEndpoint,
awsRegion,
awsSecretKey,
awsAccessKey,
dispatch,
]);
// Common
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({ pageName: "encryption", field: field, value: value })
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_endpoint"
name="aws_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsEndpoint", e.target.value);
cleanValidation("aws_endpoint");
}}
label="Endpoint"
value={awsEndpoint}
error={validationErrors["aws_endpoint"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_region"
name="aws_region"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsRegion", e.target.value);
cleanValidation("aws_region");
}}
label="Region"
value={awsRegion}
error={validationErrors["aws_region"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_kmsKey"
name="aws_kmsKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsKMSKey", e.target.value);
}}
label="KMS Key"
value={awsKMSKey}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Credentials</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_accessKey"
name="aws_accessKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsAccessKey", e.target.value);
cleanValidation("aws_accessKey");
}}
label="Access Key"
value={awsAccessKey}
error={validationErrors["aws_accessKey"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_secretKey"
name="aws_secretKey"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsSecretKey", e.target.value);
cleanValidation("aws_secretKey");
}}
label="Secret Key"
value={awsSecretKey}
error={validationErrors["aws_secretKey"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="aws_token"
name="aws_token"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("awsToken", e.target.value);
}}
label="Token"
value={awsToken}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
);
};
export default AWSKMSAdd;

View File

@@ -0,0 +1,202 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import makeStyles from "@mui/styles/makeStyles";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
import { isPageValid, updateAddField } from "../../createTenantSlice";
import { clearValidationError } from "../../../utils";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const AzureKMSAdd = () => {
const dispatch = useDispatch();
const classes = useStyles();
const enableEncryption = useSelector(
(state: AppState) => state.createTenant.fields.encryption.enableEncryption
);
const encryptionType = useSelector(
(state: AppState) => state.createTenant.fields.encryption.encryptionType
);
const azureEndpoint = useSelector(
(state: AppState) => state.createTenant.fields.encryption.azureEndpoint
);
const azureTenantID = useSelector(
(state: AppState) => state.createTenant.fields.encryption.azureTenantID
);
const azureClientID = useSelector(
(state: AppState) => state.createTenant.fields.encryption.azureClientID
);
const azureClientSecret = useSelector(
(state: AppState) => state.createTenant.fields.encryption.azureClientSecret
);
const [validationErrors, setValidationErrors] = useState<any>({});
// Validation
useEffect(() => {
let encryptionValidation: IValidation[] = [];
if (enableEncryption) {
if (encryptionType === "azure") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "azure_endpoint",
required: true,
value: azureEndpoint,
},
{
fieldKey: "azure_tenant_id",
required: true,
value: azureTenantID,
},
{
fieldKey: "azure_client_id",
required: true,
value: azureClientID,
},
{
fieldKey: "azure_client_secret",
required: true,
value: azureClientSecret,
},
];
}
}
const commonVal = commonFormValidation(encryptionValidation);
dispatch(
isPageValid({
pageName: "encryption",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
enableEncryption,
encryptionType,
azureEndpoint,
azureTenantID,
azureClientID,
azureClientSecret,
dispatch,
]);
// Common
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({ pageName: "encryption", field: field, value: value })
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_endpoint"
name="azure_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureEndpoint", e.target.value);
cleanValidation("azure_endpoint");
}}
label="Endpoint"
value={azureEndpoint}
error={validationErrors["azure_endpoint"] || ""}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Credentials</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_tenant_id"
name="azure_tenant_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureTenantID", e.target.value);
cleanValidation("azure_tenant_id");
}}
label="Tenant ID"
value={azureTenantID}
error={validationErrors["azure_tenant_id"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_client_id"
name="azure_client_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureClientID", e.target.value);
cleanValidation("azure_client_id");
}}
label="Client ID"
value={azureClientID}
error={validationErrors["azure_client_id"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="azure_client_secret"
name="azure_client_secret"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("azureClientSecret", e.target.value);
cleanValidation("azure_client_secret");
}}
label="Client Secret"
value={azureClientSecret}
error={validationErrors["azure_client_secret"] || ""}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
);
};
export default AzureKMSAdd;

View File

@@ -0,0 +1,152 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback } from "react";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import makeStyles from "@mui/styles/makeStyles";
import { updateAddField } from "../../createTenantSlice";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const GCPKMSAdd = () => {
const classes = useStyles();
const dispatch = useDispatch();
const gcpProjectID = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpProjectID
);
const gcpEndpoint = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpEndpoint
);
const gcpClientEmail = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpClientEmail
);
const gcpClientID = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpClientID
);
const gcpPrivateKeyID = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpPrivateKeyID
);
const gcpPrivateKey = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gcpPrivateKey
);
// Common
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({ pageName: "encryption", field: field, value: value })
);
},
[dispatch]
);
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_project_id"
name="gcp_project_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpProjectID", e.target.value);
}}
label="Project ID"
value={gcpProjectID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_endpoint"
name="gcp_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpEndpoint", e.target.value);
}}
label="Endpoint"
value={gcpEndpoint}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Credentials</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_client_email"
name="gcp_client_email"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpClientEmail", e.target.value);
}}
label="Client Email"
value={gcpClientEmail}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_client_id"
name="gcp_client_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpClientID", e.target.value);
}}
label="Client ID"
value={gcpClientID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_private_key_id"
name="gcp_private_key_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpPrivateKeyID", e.target.value);
}}
label="Private Key ID"
value={gcpPrivateKeyID}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gcp_private_key"
name="gcp_private_key"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gcpPrivateKey", e.target.value);
}}
label="Private Key"
value={gcpPrivateKey}
/>
</Grid>
</fieldset>
</Grid>
</Fragment>
);
};
export default GCPKMSAdd;

View File

@@ -0,0 +1,252 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import makeStyles from "@mui/styles/makeStyles";
import FileSelector from "../../../../Common/FormComponents/FileSelector/FileSelector";
import {
addFileGemaltoCa,
isPageValid,
updateAddField,
} from "../../createTenantSlice";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
import { clearValidationError } from "../../../utils";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const GemaltoKMSAdd = () => {
const dispatch = useDispatch();
const classes = useStyles();
const enableEncryption = useSelector(
(state: AppState) => state.createTenant.fields.encryption.enableEncryption
);
const encryptionType = useSelector(
(state: AppState) => state.createTenant.fields.encryption.encryptionType
);
const gemaltoCA = useSelector(
(state: AppState) => state.createTenant.certificates.gemaltoCA
);
const gemaltoEndpoint = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gemaltoEndpoint
);
const gemaltoToken = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gemaltoToken
);
const gemaltoDomain = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gemaltoDomain
);
const gemaltoRetry = useSelector(
(state: AppState) => state.createTenant.fields.encryption.gemaltoRetry
);
const [validationErrors, setValidationErrors] = useState<any>({});
// Validation
useEffect(() => {
let encryptionValidation: IValidation[] = [];
if (enableEncryption) {
if (encryptionType === "gemalto") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "gemalto_endpoint",
required: true,
value: gemaltoEndpoint,
},
{
fieldKey: "gemalto_token",
required: true,
value: gemaltoToken,
},
{
fieldKey: "gemalto_domain",
required: true,
value: gemaltoDomain,
},
{
fieldKey: "gemalto_retry",
required: false,
value: gemaltoRetry,
customValidation: parseInt(gemaltoRetry) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
];
}
}
const commonVal = commonFormValidation(encryptionValidation);
dispatch(
isPageValid({
pageName: "encryption",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
enableEncryption,
encryptionType,
gemaltoEndpoint,
gemaltoToken,
gemaltoDomain,
gemaltoRetry,
dispatch,
]);
// Common
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({ pageName: "encryption", field: field, value: value })
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_endpoint"
name="gemalto_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoEndpoint", e.target.value);
cleanValidation("gemalto_endpoint");
}}
label="Endpoint"
value={gemaltoEndpoint}
error={validationErrors["gemalto_endpoint"] || ""}
required
/>
</Grid>
<Grid
item
xs={12}
style={{
marginBottom: 15,
}}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Credentials</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_token"
name="gemalto_token"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoToken", e.target.value);
cleanValidation("gemalto_token");
}}
label="Token"
value={gemaltoToken}
error={validationErrors["gemalto_token"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="gemalto_domain"
name="gemalto_domain"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoDomain", e.target.value);
cleanValidation("gemalto_domain");
}}
label="Domain"
value={gemaltoDomain}
error={validationErrors["gemalto_domain"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
type="number"
min="0"
id="gemalto_retry"
name="gemalto_retry"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("gemaltoRetry", e.target.value);
cleanValidation("gemalto_retry");
}}
label="Retry (seconds)"
value={gemaltoRetry}
error={validationErrors["gemalto_retry"] || ""}
/>
</Grid>
</fieldset>
</Grid>
<Grid
item
xs={12}
style={{
marginBottom: 15,
}}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Custom CA Root certificate verification
</legend>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileGemaltoCa({
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("gemalto_ca");
}}
accept=".cer,.crt,.cert,.pem"
id="gemalto_ca"
name="gemalto_ca"
label="CA"
value={gemaltoCA.cert}
/>
</fieldset>
</Grid>
</Fragment>
);
};
export default GemaltoKMSAdd;

View File

@@ -0,0 +1,373 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FileSelector from "../../../../Common/FormComponents/FileSelector/FileSelector";
import {
addFileVaultCa,
addFileVaultCert,
isPageValid,
updateAddField,
} from "../../createTenantSlice";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import makeStyles from "@mui/styles/makeStyles";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
import { clearValidationError } from "../../../utils";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const VaultKMSAdd = () => {
const dispatch = useDispatch();
const classes = useStyles();
const enableEncryption = useSelector(
(state: AppState) => state.createTenant.fields.encryption.enableEncryption
);
const encryptionType = useSelector(
(state: AppState) => state.createTenant.fields.encryption.encryptionType
);
const vaultEndpoint = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultEndpoint
);
const vaultEngine = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultEngine
);
const vaultNamespace = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultNamespace
);
const vaultPrefix = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultPrefix
);
const vaultAppRoleEngine = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultAppRoleEngine
);
const vaultId = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultId
);
const vaultSecret = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultSecret
);
const vaultRetry = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultRetry
);
const vaultPing = useSelector(
(state: AppState) => state.createTenant.fields.encryption.vaultPing
);
const vaultCertificate = useSelector(
(state: AppState) => state.createTenant.certificates.vaultCertificate
);
const vaultCA = useSelector(
(state: AppState) => state.createTenant.certificates.vaultCA
);
const [validationErrors, setValidationErrors] = useState<any>({});
// Validation
useEffect(() => {
let encryptionValidation: IValidation[] = [];
if (enableEncryption) {
if (encryptionType === "vault") {
encryptionValidation = [
...encryptionValidation,
{
fieldKey: "vault_endpoint",
required: true,
value: vaultEndpoint,
},
{
fieldKey: "vault_id",
required: true,
value: vaultId,
},
{
fieldKey: "vault_secret",
required: true,
value: vaultSecret,
},
{
fieldKey: "vault_ping",
required: false,
value: vaultPing,
customValidation: parseInt(vaultPing) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
{
fieldKey: "vault_retry",
required: false,
value: vaultRetry,
customValidation: parseInt(vaultRetry) < 0,
customValidationMessage: "Value needs to be 0 or greater",
},
];
}
}
const commonVal = commonFormValidation(encryptionValidation);
dispatch(
isPageValid({
pageName: "encryption",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
enableEncryption,
encryptionType,
vaultEndpoint,
vaultEngine,
vaultId,
vaultSecret,
vaultPing,
vaultRetry,
dispatch,
]);
// Common
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({ pageName: "encryption", field: field, value: value })
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_endpoint"
name="vault_endpoint"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultEndpoint", e.target.value);
cleanValidation("vault_endpoint");
}}
label="Endpoint"
value={vaultEndpoint}
error={validationErrors["vault_endpoint"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_engine"
name="vault_engine"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultEngine", e.target.value);
cleanValidation("vault_engine");
}}
label="Engine"
value={vaultEngine}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_namespace"
name="vault_namespace"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultNamespace", e.target.value);
}}
label="Namespace"
value={vaultNamespace}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_prefix"
name="vault_prefix"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultPrefix", e.target.value);
}}
label="Prefix"
value={vaultPrefix}
/>
</Grid>
<Grid item xs={12}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>App Role</legend>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_approle_engine"
name="vault_approle_engine"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultAppRoleEngine", e.target.value);
}}
label="Engine"
value={vaultAppRoleEngine}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_id"
name="vault_id"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultId", e.target.value);
cleanValidation("vault_id");
}}
label="AppRole ID"
value={vaultId}
error={validationErrors["vault_id"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="vault_secret"
name="vault_secret"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultSecret", e.target.value);
cleanValidation("vault_secret");
}}
label="AppRole Secret"
value={vaultSecret}
error={validationErrors["vault_secret"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
type="number"
min="0"
id="vault_retry"
name="vault_retry"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultRetry", e.target.value);
cleanValidation("vault_retry");
}}
label="Retry (Seconds)"
value={vaultRetry}
error={validationErrors["vault_retry"] || ""}
/>
</Grid>
</fieldset>
</Grid>
<Grid container className={classes.mutualTlsConfig}>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
Mutual TLS authentication (optional)
</legend>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCert({
key: "key",
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_key");
}}
accept=".key,.pem"
id="vault_key"
name="vault_key"
label="Key"
value={vaultCertificate.key}
/>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCert({
key: "cert",
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_cert");
}}
accept=".cer,.crt,.cert,.pem"
id="vault_cert"
name="vault_cert"
label="Cert"
value={vaultCertificate.cert}
/>
<FileSelector
onChange={(encodedValue, fileName) => {
dispatch(
addFileVaultCa({
fileName: fileName,
value: encodedValue,
})
);
cleanValidation("vault_ca");
}}
accept=".cer,.crt,.cert,.pem"
id="vault_ca"
name="vault_ca"
label="CA"
value={vaultCA.cert}
/>
</fieldset>
</Grid>
<Grid
item
xs={12}
className={classes.formFieldRow}
style={{ marginTop: 15 }}
>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>Status</legend>
<InputBoxWrapper
type="number"
min="0"
id="vault_ping"
name="vault_ping"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("vaultPing", e.target.value);
cleanValidation("vault_ping");
}}
label="Ping (Seconds)"
value={vaultPing}
error={validationErrors["vault_ping"] || ""}
/>
</fieldset>
</Grid>
</Fragment>
);
};
export default VaultKMSAdd;

View File

@@ -14,67 +14,26 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useCallback, useEffect, useState } from "react";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Grid, IconButton, Paper, Tooltip, Typography } from "@mui/material";
import CasinoIcon from "@mui/icons-material/Casino";
import DeleteIcon from "@mui/icons-material/Delete";
import { Grid, Paper } from "@mui/material";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import {
commonFormValidation,
IValidation,
} from "../../../../../utils/validationFunctions";
import { AppState } from "../../../../../store";
import { clearValidationError, getRandomString } from "../../utils";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import AddIcon from "@mui/icons-material/Add";
import RemoveIcon from "../../../../../icons/RemoveIcon";
import { isPageValid, updateAddField } from "../../tenantsSlice";
import { setIDP } from "../createTenantSlice";
import IDPActiveDirectory from "./IdentityProvider/IDPActiveDirectory";
import IDPOpenID from "./IdentityProvider/IDPOpenID";
import makeStyles from "@mui/styles/makeStyles";
import IDPBuiltIn from "./IdentityProvider/IDPBuiltIn";
interface IIdentityProviderProps {
classes: any;
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
shortened: {
gridTemplateColumns: "auto auto 50px 50px",
display: "grid",
gridGap: 15,
marginBottom: 10,
"& input": {
fontWeight: 400,
},
},
buttonTray: {
marginLeft: 10,
display: "flex",
height: 38,
"& button": {
background: "#EAEAEA",
},
},
overlayAction: {
marginLeft: 10,
"& svg": {
maxWidth: 15,
maxHeight: 15,
},
"& button": {
background: "#EAEAEA",
},
},
protocolRadioOptions: {
display: "flex",
flexFlow: "column",
@@ -89,374 +48,20 @@ const styles = (theme: Theme) =>
alignItems: "baseline",
},
},
adUserDnRows: {
display: "flex",
},
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
});
})
);
const IdentityProvider = ({ classes }: IIdentityProviderProps) => {
const IdentityProvider = () => {
const dispatch = useDispatch();
const classes = useStyles();
const idpSelection = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.idpSelection
);
const accessKeys = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.accessKeys
);
const secretKeys = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.secretKeys
);
const openIDConfigurationURL = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDConfigurationURL
);
const openIDClientID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDClientID
);
const openIDSecretID = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDSecretID
);
const openIDCallbackURL = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDCallbackURL
);
const openIDClaimName = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDClaimName
);
const openIDScopes = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.openIDScopes
);
const ADURL = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADURL
);
const ADSkipTLS = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADSkipTLS
);
const ADServerInsecure = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADServerInsecure
);
const ADGroupSearchBaseDN = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADGroupSearchBaseDN
);
const ADGroupSearchFilter = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADGroupSearchFilter
);
const ADUserDNs = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADUserDNs
);
const ADLookupBindDN = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADLookupBindDN
);
const ADLookupBindPassword = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADLookupBindPassword
);
const ADUserDNSearchBaseDN = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADUserDNSearchBaseDN
);
const ADUserDNSearchFilter = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADUserDNSearchFilter
);
const ADServerStartTLS = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.identityProvider.ADServerStartTLS
(state: AppState) => state.createTenant.fields.identityProvider.idpSelection
);
const [validationErrors, setValidationErrors] = useState<any>({});
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({
pageName: "identityProvider",
field: field,
value: value,
})
);
},
[dispatch]
);
const updateUserField = (index: number, value: string) => {
const newUserField = [...accessKeys];
newUserField[index] = value;
updateField("accessKeys", newUserField);
};
const updatePwordField = (index: number, value: string) => {
const newUserField = [...secretKeys];
newUserField[index] = value;
updateField("secretKeys", newUserField);
};
const updateADUserField = (index: number, value: string) => {
const newADUserDNsField = [...ADUserDNs];
newADUserDNsField[index] = value;
updateField("ADUserDNs", newADUserDNsField);
};
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
// Validation
useEffect(() => {
let customIDPValidation: IValidation[] = [];
if (idpSelection === "Built-in") {
customIDPValidation = [...customIDPValidation];
for (var i = 0; i < accessKeys.length; i++) {
customIDPValidation.push({
fieldKey: `accesskey-${i.toString()}`,
required: true,
value: accessKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
customIDPValidation.push({
fieldKey: `secretkey-${i.toString()}`,
required: true,
value: secretKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
}
}
if (idpSelection === "OpenID") {
customIDPValidation = [
...customIDPValidation,
{
fieldKey: "openID_CONFIGURATION_URL",
required: true,
value: openIDConfigurationURL,
},
{
fieldKey: "openID_clientID",
required: true,
value: openIDClientID,
},
{
fieldKey: "openID_secretID",
required: true,
value: openIDSecretID,
},
{
fieldKey: "openID_claimName",
required: true,
value: openIDClaimName,
},
];
}
if (idpSelection === "AD") {
customIDPValidation = [
...customIDPValidation,
{
fieldKey: "AD_URL",
required: true,
value: ADURL,
},
{
fieldKey: "ad_lookupBindDN",
required: true,
value: ADLookupBindDN,
},
];
// validate user DNs
for (let i = 0; i < ADUserDNs.length; i++) {
customIDPValidation.push({
fieldKey: `ad-userdn-${i.toString()}`,
required: true,
value: ADUserDNs[i],
});
}
}
const commonVal = commonFormValidation(customIDPValidation);
dispatch(
isPageValid({
pageName: "identityProvider",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
ADLookupBindDN,
idpSelection,
accessKeys,
secretKeys,
openIDClientID,
openIDSecretID,
ADURL,
ADGroupSearchBaseDN,
ADGroupSearchFilter,
ADUserDNs,
dispatch,
openIDConfigurationURL,
openIDClaimName,
]);
let inputs = null;
if (idpSelection === "Built-in") {
inputs = accessKeys.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.shortened}>
<InputBoxWrapper
id={`accesskey-${index.toString()}`}
label={""}
placeholder={"Access Key"}
name={`accesskey-${index.toString()}`}
value={accessKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateUserField(index, e.target.value);
cleanValidation(`accesskey-${index.toString()}`);
}}
index={index}
key={`csv-accesskey-${index.toString()}`}
error={validationErrors[`accesskey-${index.toString()}`] || ""}
/>
<InputBoxWrapper
id={`secretkey-${index.toString()}`}
label={""}
placeholder={"Secret Key"}
name={`secretkey-${index.toString()}`}
value={secretKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updatePwordField(index, e.target.value);
cleanValidation(`secretkey-${index.toString()}`);
}}
index={index}
key={`csv-secretkey-${index.toString()}`}
error={validationErrors[`secretkey-${index.toString()}`] || ""}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
accessKeys.push("");
secretKeys.push("");
updateUserField(accessKeys.length - 1, "");
updatePwordField(secretKeys.length - 1, "");
}}
>
<AddIcon />
</IconButton>
</div>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
if (accessKeys.length > 1) {
accessKeys.splice(index, 1);
secretKeys.splice(index, 1);
updateUserField(
accessKeys.length - 1,
accessKeys[accessKeys.length - 1]
);
}
}}
>
<RemoveIcon />
</IconButton>
</div>
</Tooltip>
<Tooltip title="Randomize Credentials" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
onClick={() => {
updateUserField(index, getRandomString(16));
updatePwordField(index, getRandomString(32));
}}
size={"small"}
>
<CasinoIcon />
</IconButton>
</div>
</Tooltip>
</div>
</div>
</Fragment>
);
});
}
if (idpSelection === "AD") {
inputs = ADUserDNs.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.adUserDnRows}>
<InputBoxWrapper
id={`ad-userdn-${index.toString()}`}
label={""}
placeholder=""
name={`ad-userdn-${index.toString()}`}
value={ADUserDNs[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateADUserField(index, e.target.value);
cleanValidation(`ad-userdn-${index.toString()}`);
}}
index={index}
key={`csv-ad-userdn-${index.toString()}`}
error={validationErrors[`ad-userdn-${index.toString()}`] || ""}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<IconButton
size={"small"}
onClick={() => {
ADUserDNs.push("");
updateADUserField(ADUserDNs.length - 1, "");
}}
>
<AddIcon />
</IconButton>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<IconButton
size={"small"}
style={{ marginLeft: 16 }}
onClick={() => {
if (ADUserDNs.length > 1) {
ADUserDNs.splice(index, 1);
updateUserField(
ADUserDNs.length - 1,
ADUserDNs[ADUserDNs.length - 1]
);
}
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</div>
</div>
</Fragment>
);
});
}
return (
<Paper className={classes.paperWrapper}>
<div className={classes.headerElement}>
@@ -474,7 +79,7 @@ const IdentityProvider = ({ classes }: IIdentityProviderProps) => {
name="idp-options"
label=" "
onChange={(e) => {
updateField("idpSelection", e.target.value);
dispatch(setIDP(e.target.value));
}}
selectorOptions={[
{ label: "Built-in", value: "Built-in" },
@@ -483,258 +88,11 @@ const IdentityProvider = ({ classes }: IIdentityProviderProps) => {
]}
/>
</Grid>
{idpSelection === "Built-in" && (
<Fragment>
Add additional users
{inputs}
</Fragment>
)}
{idpSelection === "OpenID" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_CONFIGURATION_URL"
name="openID_CONFIGURATION_URL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDConfigurationURL", e.target.value);
cleanValidation("openID_CONFIGURATION_URL");
}}
label="Configuration URL"
value={openIDConfigurationURL}
placeholder="https://your-identity-provider.com/.well-known/openid-configuration"
error={validationErrors["openID_CONFIGURATION_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_clientID"
name="openID_clientID"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDClientID", e.target.value);
cleanValidation("openID_clientID");
}}
label="Client ID"
value={openIDClientID}
error={validationErrors["openID_clientID"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_secretID"
name="openID_secretID"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDSecretID", e.target.value);
cleanValidation("openID_secretID");
}}
label="Secret ID"
value={openIDSecretID}
error={validationErrors["openID_secretID"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_callbackURL"
name="openID_callbackURL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDCallbackURL", e.target.value);
cleanValidation("openID_callbackURL");
}}
label="Callback URL"
value={openIDCallbackURL}
placeholder="https://your-console-endpoint:9443/oauth_callback"
error={validationErrors["openID_callbackURL"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_claimName"
name="openID_claimName"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDClaimName", e.target.value);
cleanValidation("openID_claimName");
}}
label="Claim Name"
value={openIDClaimName}
error={validationErrors["openID_claimName"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_scopes"
name="openID_scopes"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDScopes", e.target.value);
cleanValidation("openID_scopes");
}}
label="Scopes"
value={openIDScopes}
/>
</Grid>
</Fragment>
)}
{idpSelection === "AD" && (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="AD_URL"
name="AD_URL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADURL", e.target.value);
cleanValidation("AD_URL");
}}
label="LDAP Server Address"
value={ADURL}
placeholder="ldap-server:636"
error={validationErrors["AD_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_skipTLS"
id="ad_skipTLS"
name="ad_skipTLS"
checked={ADSkipTLS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADSkipTLS", checked);
}}
label={"Skip TLS Verification"}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_serverInsecure"
id="ad_serverInsecure"
name="ad_serverInsecure"
checked={ADServerInsecure}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerInsecure", checked);
}}
label={"Server Insecure"}
/>
</Grid>
{ADServerInsecure ? (
<Grid item xs={12}>
<Typography
className={classes.error}
variant="caption"
display="block"
gutterBottom
>
Warning: All traffic with Active Directory will be unencrypted
</Typography>
<br />
</Grid>
) : null}
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_serverStartTLS"
id="ad_serverStartTLS"
name="ad_serverStartTLS"
checked={ADServerStartTLS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerStartTLS", checked);
}}
label={"Start TLS connection to AD/LDAP server"}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_lookupBindDN"
name="ad_lookupBindDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindDN", e.target.value);
cleanValidation("ad_lookupBindDN");
}}
label="Lookup Bind DN"
value={ADLookupBindDN}
placeholder="cn=admin,dc=min,dc=io"
error={validationErrors["ad_lookupBindDN"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_lookupBindPassword"
name="ad_lookupBindPassword"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindPassword", e.target.value);
}}
label="Lookup Bind Password"
value={ADLookupBindPassword}
placeholder="admin"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_userDNSearchBaseDN"
name="ad_userDNSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchBaseDN", e.target.value);
}}
label="User DN Search Base DN"
value={ADUserDNSearchBaseDN}
placeholder="dc=min,dc=io"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_userDNSearchFilter"
name="ad_userDNSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchFilter", e.target.value);
}}
label="User DN Search Filter"
value={ADUserDNSearchFilter}
placeholder="(sAMAcountName=%s)"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_groupSearchBaseDN"
name="ad_groupSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchBaseDN", e.target.value);
}}
label="Group Search Base DN"
value={ADGroupSearchBaseDN}
placeholder="ou=hwengg,dc=min,dc=io;ou=swengg,dc=min,dc=io"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_groupSearchFilter"
name="ad_groupSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchFilter", e.target.value);
}}
label="Group Search Filter"
value={ADGroupSearchFilter}
placeholder="(&(objectclass=groupOfNames)(member=%s))"
/>
</Grid>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
List of user DNs (Distinguished Names) to be Tenant Administrators
</legend>
<Grid item xs={12}>
{inputs}
</Grid>
</fieldset>
</Fragment>
)}
{idpSelection === "Built-in" && <IDPBuiltIn />}
{idpSelection === "OpenID" && <IDPOpenID />}
{idpSelection === "AD" && <IDPActiveDirectory />}
</Paper>
);
};
export default withStyles(styles)(IdentityProvider);
export default IdentityProvider;

View File

@@ -0,0 +1,405 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Grid, IconButton, Tooltip, Typography } from "@mui/material";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import makeStyles from "@mui/styles/makeStyles";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import {
addIDPADUsrAtIndex,
isPageValid,
removeIDPADUsrAtIndex,
setIDPADUsrAtIndex,
updateAddField,
} from "../../createTenantSlice";
import { useDispatch, useSelector } from "react-redux";
import { clearValidationError } from "../../../utils";
import { AppState } from "../../../../../../store";
import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
adUserDnRows: {
display: "flex",
},
buttonTray: {
marginLeft: 10,
display: "flex",
height: 38,
"& button": {
background: "#EAEAEA",
},
},
overlayAction: {
marginLeft: 10,
"& svg": {
maxWidth: 15,
maxHeight: 15,
},
"& button": {
background: "#EAEAEA",
},
},
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const IDPActiveDirectory = () => {
const dispatch = useDispatch();
const classes = useStyles();
const idpSelection = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.idpSelection
);
const ADURL = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.ADURL
);
const ADSkipTLS = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.ADSkipTLS
);
const ADServerInsecure = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADServerInsecure
);
const ADGroupSearchBaseDN = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADGroupSearchBaseDN
);
const ADGroupSearchFilter = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADGroupSearchFilter
);
const ADUserDNs = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.ADUserDNs
);
const ADLookupBindDN = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADLookupBindDN
);
const ADLookupBindPassword = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADLookupBindPassword
);
const ADUserDNSearchBaseDN = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADUserDNSearchBaseDN
);
const ADUserDNSearchFilter = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADUserDNSearchFilter
);
const ADServerStartTLS = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.ADServerStartTLS
);
const [validationErrors, setValidationErrors] = useState<any>({});
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({
pageName: "identityProvider",
field: field,
value: value,
})
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
// Validation
useEffect(() => {
let customIDPValidation: IValidation[] = [];
if (idpSelection === "AD") {
customIDPValidation = [
...customIDPValidation,
{
fieldKey: "AD_URL",
required: true,
value: ADURL,
},
{
fieldKey: "ad_lookupBindDN",
required: true,
value: ADLookupBindDN,
},
];
// validate user DNs
for (let i = 0; i < ADUserDNs.length; i++) {
customIDPValidation.push({
fieldKey: `ad-userdn-${i.toString()}`,
required: true,
value: ADUserDNs[i],
});
}
}
const commonVal = commonFormValidation(customIDPValidation);
dispatch(
isPageValid({
pageName: "identityProvider",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
ADLookupBindDN,
idpSelection,
ADURL,
ADGroupSearchBaseDN,
ADGroupSearchFilter,
ADUserDNs,
dispatch,
]);
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="AD_URL"
name="AD_URL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADURL", e.target.value);
cleanValidation("AD_URL");
}}
label="LDAP Server Address"
value={ADURL}
placeholder="ldap-server:636"
error={validationErrors["AD_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_skipTLS"
id="ad_skipTLS"
name="ad_skipTLS"
checked={ADSkipTLS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADSkipTLS", checked);
}}
label={"Skip TLS Verification"}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_serverInsecure"
id="ad_serverInsecure"
name="ad_serverInsecure"
checked={ADServerInsecure}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerInsecure", checked);
}}
label={"Server Insecure"}
/>
</Grid>
{ADServerInsecure ? (
<Grid item xs={12}>
<Typography
className={classes.error}
variant="caption"
display="block"
gutterBottom
>
Warning: All traffic with Active Directory will be unencrypted
</Typography>
<br />
</Grid>
) : null}
<Grid item xs={12} className={classes.formFieldRow}>
<FormSwitchWrapper
value="ad_serverStartTLS"
id="ad_serverStartTLS"
name="ad_serverStartTLS"
checked={ADServerStartTLS}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("ADServerStartTLS", checked);
}}
label={"Start TLS connection to AD/LDAP server"}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_lookupBindDN"
name="ad_lookupBindDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindDN", e.target.value);
cleanValidation("ad_lookupBindDN");
}}
label="Lookup Bind DN"
value={ADLookupBindDN}
placeholder="cn=admin,dc=min,dc=io"
error={validationErrors["ad_lookupBindDN"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_lookupBindPassword"
name="ad_lookupBindPassword"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADLookupBindPassword", e.target.value);
}}
label="Lookup Bind Password"
value={ADLookupBindPassword}
placeholder="admin"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_userDNSearchBaseDN"
name="ad_userDNSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchBaseDN", e.target.value);
}}
label="User DN Search Base DN"
value={ADUserDNSearchBaseDN}
placeholder="dc=min,dc=io"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_userDNSearchFilter"
name="ad_userDNSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADUserDNSearchFilter", e.target.value);
}}
label="User DN Search Filter"
value={ADUserDNSearchFilter}
placeholder="(sAMAcountName=%s)"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_groupSearchBaseDN"
name="ad_groupSearchBaseDN"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchBaseDN", e.target.value);
}}
label="Group Search Base DN"
value={ADGroupSearchBaseDN}
placeholder="ou=hwengg,dc=min,dc=io;ou=swengg,dc=min,dc=io"
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="ad_groupSearchFilter"
name="ad_groupSearchFilter"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("ADGroupSearchFilter", e.target.value);
}}
label="Group Search Filter"
value={ADGroupSearchFilter}
placeholder="(&(objectclass=groupOfNames)(member=%s))"
/>
</Grid>
<fieldset className={classes.fieldGroup}>
<legend className={classes.descriptionText}>
List of user DNs (Distinguished Names) to be Tenant Administrators
</legend>
<Grid item xs={12}>
{ADUserDNs.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.adUserDnRows}>
<InputBoxWrapper
id={`ad-userdn-${index.toString()}`}
label={""}
placeholder=""
name={`ad-userdn-${index.toString()}`}
value={ADUserDNs[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
setIDPADUsrAtIndex({
index: index,
userDN: e.target.value,
})
);
cleanValidation(`ad-userdn-${index.toString()}`);
}}
index={index}
key={`csv-ad-userdn-${index.toString()}`}
error={
validationErrors[`ad-userdn-${index.toString()}`] || ""
}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<IconButton
size={"small"}
onClick={() => {
dispatch(addIDPADUsrAtIndex());
}}
>
<AddIcon />
</IconButton>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<IconButton
size={"small"}
style={{ marginLeft: 16 }}
onClick={() => {
if (ADUserDNs.length > 1) {
dispatch(removeIDPADUsrAtIndex(index));
}
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</div>
</div>
</Fragment>
);
})}
</Grid>
</fieldset>
</Fragment>
);
};
export default IDPActiveDirectory;

View File

@@ -0,0 +1,241 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import {
addIDPNewKeyPair,
isPageValid,
removeIDPKeyPairAtIndex,
setIDPPwdAtIndex,
setIDPUsrAtIndex,
} from "../../createTenantSlice";
import { IconButton, Tooltip } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import RemoveIcon from "../../../../../../icons/RemoveIcon";
import { clearValidationError, getRandomString } from "../../../utils";
import CasinoIcon from "@mui/icons-material/Casino";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import makeStyles from "@mui/styles/makeStyles";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
buttonTray: {
marginLeft: 10,
display: "flex",
height: 38,
"& button": {
background: "#EAEAEA",
},
},
overlayAction: {
marginLeft: 10,
"& svg": {
maxWidth: 15,
maxHeight: 15,
},
"& button": {
background: "#EAEAEA",
},
},
shortened: {
gridTemplateColumns: "auto auto 50px 50px",
display: "grid",
gridGap: 15,
marginBottom: 10,
"& input": {
fontWeight: 400,
},
},
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const IDPBuiltIn = () => {
const dispatch = useDispatch();
const classes = useStyles();
const idpSelection = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.idpSelection
);
const accessKeys = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.accessKeys
);
const secretKeys = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.secretKeys
);
const [validationErrors, setValidationErrors] = useState<any>({});
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
// Validation
useEffect(() => {
let customIDPValidation: IValidation[] = [];
if (idpSelection === "Built-in") {
customIDPValidation = [...customIDPValidation];
for (var i = 0; i < accessKeys.length; i++) {
customIDPValidation.push({
fieldKey: `accesskey-${i.toString()}`,
required: true,
value: accessKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
customIDPValidation.push({
fieldKey: `secretkey-${i.toString()}`,
required: true,
value: secretKeys[i],
pattern: /^[a-zA-Z0-9-]{8,63}$/,
customPatternMessage: "Keys must be at least length 8",
});
}
}
const commonVal = commonFormValidation(customIDPValidation);
dispatch(
isPageValid({
pageName: "identityProvider",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [idpSelection, accessKeys, secretKeys, dispatch]);
return (
<Fragment>
Add additional users
{accessKeys.map((_, index) => {
return (
<Fragment key={`identityField-${index.toString()}`}>
<div className={classes.shortened}>
<InputBoxWrapper
id={`accesskey-${index.toString()}`}
label={""}
placeholder={"Access Key"}
name={`accesskey-${index.toString()}`}
value={accessKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
setIDPUsrAtIndex({
index,
accessKey: e.target.value,
})
);
cleanValidation(`accesskey-${index.toString()}`);
}}
index={index}
key={`csv-accesskey-${index.toString()}`}
error={validationErrors[`accesskey-${index.toString()}`] || ""}
/>
<InputBoxWrapper
id={`secretkey-${index.toString()}`}
label={""}
placeholder={"Secret Key"}
name={`secretkey-${index.toString()}`}
value={secretKeys[index]}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
setIDPPwdAtIndex({
index,
secretKey: e.target.value,
})
);
cleanValidation(`secretkey-${index.toString()}`);
}}
index={index}
key={`csv-secretkey-${index.toString()}`}
error={validationErrors[`secretkey-${index.toString()}`] || ""}
/>
<div className={classes.buttonTray}>
<Tooltip title="Add User" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
dispatch(addIDPNewKeyPair());
}}
>
<AddIcon />
</IconButton>
</div>
</Tooltip>
<Tooltip title="Remove" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
size={"small"}
onClick={() => {
dispatch(removeIDPKeyPairAtIndex(index));
}}
>
<RemoveIcon />
</IconButton>
</div>
</Tooltip>
<Tooltip title="Randomize Credentials" aria-label="add">
<div className={classes.overlayAction}>
<IconButton
onClick={() => {
dispatch(
setIDPUsrAtIndex({
index,
accessKey: getRandomString(16),
})
);
dispatch(
setIDPPwdAtIndex({
index,
secretKey: getRandomString(16),
})
);
}}
size={"small"}
>
<CasinoIcon />
</IconButton>
</div>
</Tooltip>
</div>
</div>
</Fragment>
);
})}
</Fragment>
);
};
export default IDPBuiltIn;

View File

@@ -0,0 +1,253 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Grid } from "@mui/material";
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import makeStyles from "@mui/styles/makeStyles";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
createTenantCommon,
formFieldStyles,
modalBasic,
wizardCommon,
} from "../../../../Common/FormComponents/common/styleLibrary";
import { useDispatch, useSelector } from "react-redux";
import { AppState } from "../../../../../../store";
import { isPageValid, updateAddField } from "../../createTenantSlice";
import { clearValidationError } from "../../../utils";
import {
commonFormValidation,
IValidation,
} from "../../../../../../utils/validationFunctions";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
buttonTray: {
marginLeft: 10,
display: "flex",
height: 38,
"& button": {
background: "#EAEAEA",
},
},
overlayAction: {
marginLeft: 10,
"& svg": {
maxWidth: 15,
maxHeight: 15,
},
"& button": {
background: "#EAEAEA",
},
},
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
})
);
const IDPOpenID = () => {
const dispatch = useDispatch();
const classes = useStyles();
const idpSelection = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.idpSelection
);
const openIDConfigurationURL = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.openIDConfigurationURL
);
const openIDClientID = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.openIDClientID
);
const openIDSecretID = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.openIDSecretID
);
const openIDCallbackURL = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.openIDCallbackURL
);
const openIDClaimName = useSelector(
(state: AppState) =>
state.createTenant.fields.identityProvider.openIDClaimName
);
const openIDScopes = useSelector(
(state: AppState) => state.createTenant.fields.identityProvider.openIDScopes
);
const [validationErrors, setValidationErrors] = useState<any>({});
const updateField = useCallback(
(field: string, value: any) => {
dispatch(
updateAddField({
pageName: "identityProvider",
field: field,
value: value,
})
);
},
[dispatch]
);
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
// Validation
useEffect(() => {
let customIDPValidation: IValidation[] = [];
if (idpSelection === "OpenID") {
customIDPValidation = [
...customIDPValidation,
{
fieldKey: "openID_CONFIGURATION_URL",
required: true,
value: openIDConfigurationURL,
},
{
fieldKey: "openID_clientID",
required: true,
value: openIDClientID,
},
{
fieldKey: "openID_secretID",
required: true,
value: openIDSecretID,
},
{
fieldKey: "openID_claimName",
required: true,
value: openIDClaimName,
},
];
}
const commonVal = commonFormValidation(customIDPValidation);
dispatch(
isPageValid({
pageName: "identityProvider",
valid: Object.keys(commonVal).length === 0,
})
);
setValidationErrors(commonVal);
}, [
idpSelection,
openIDClientID,
openIDSecretID,
openIDConfigurationURL,
openIDClaimName,
dispatch,
]);
return (
<Fragment>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_CONFIGURATION_URL"
name="openID_CONFIGURATION_URL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDConfigurationURL", e.target.value);
cleanValidation("openID_CONFIGURATION_URL");
}}
label="Configuration URL"
value={openIDConfigurationURL}
placeholder="https://your-identity-provider.com/.well-known/openid-configuration"
error={validationErrors["openID_CONFIGURATION_URL"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_clientID"
name="openID_clientID"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDClientID", e.target.value);
cleanValidation("openID_clientID");
}}
label="Client ID"
value={openIDClientID}
error={validationErrors["openID_clientID"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_secretID"
name="openID_secretID"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDSecretID", e.target.value);
cleanValidation("openID_secretID");
}}
label="Secret ID"
value={openIDSecretID}
error={validationErrors["openID_secretID"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_callbackURL"
name="openID_callbackURL"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDCallbackURL", e.target.value);
cleanValidation("openID_callbackURL");
}}
label="Callback URL"
value={openIDCallbackURL}
placeholder="https://your-console-endpoint:9443/oauth_callback"
error={validationErrors["openID_callbackURL"] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_claimName"
name="openID_claimName"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDClaimName", e.target.value);
cleanValidation("openID_claimName");
}}
label="Claim Name"
value={openIDClaimName}
error={validationErrors["openID_claimName"] || ""}
required
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id="openID_scopes"
name="openID_scopes"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
updateField("openIDScopes", e.target.value);
cleanValidation("openID_scopes");
}}
label="Scopes"
value={openIDScopes}
/>
</Grid>
</Fragment>
);
};
export default IDPOpenID;

View File

@@ -33,7 +33,7 @@ import {
} from "../../../../../utils/validationFunctions";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { isPageValid, updateAddField } from "../../tenantsSlice";
import { isPageValid, updateAddField } from "../createTenantSlice";
interface IImagesProps {
classes: any;
@@ -50,84 +50,75 @@ const Images = ({ classes }: IImagesProps) => {
const dispatch = useDispatch();
const customImage = useSelector(
(state: AppState) => state.tenants.createTenant.fields.configure.customImage
(state: AppState) => state.createTenant.fields.configure.customImage
);
const imageName = useSelector(
(state: AppState) => state.tenants.createTenant.fields.configure.imageName
(state: AppState) => state.createTenant.fields.configure.imageName
);
const customDockerhub = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.customDockerhub
(state: AppState) => state.createTenant.fields.configure.customDockerhub
);
const imageRegistry = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.imageRegistry
(state: AppState) => state.createTenant.fields.configure.imageRegistry
);
const imageRegistryUsername = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.imageRegistryUsername
state.createTenant.fields.configure.imageRegistryUsername
);
const imageRegistryPassword = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.imageRegistryPassword
state.createTenant.fields.configure.imageRegistryPassword
);
const prometheusCustom = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusEnabled
(state: AppState) => state.createTenant.fields.configure.prometheusEnabled
);
const tenantCustom = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.tenantCustom
(state: AppState) => state.createTenant.fields.configure.tenantCustom
);
const logSearchCustom = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchEnabled
(state: AppState) => state.createTenant.fields.configure.logSearchEnabled
);
const logSearchVolumeSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchVolumeSize
(state: AppState) => state.createTenant.fields.configure.logSearchVolumeSize
);
const prometheusVolumeSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusVolumeSize
state.createTenant.fields.configure.prometheusVolumeSize
);
const logSearchSelectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchSelectedStorageClass
state.createTenant.fields.configure.logSearchSelectedStorageClass
);
const logSearchImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchImage
(state: AppState) => state.createTenant.fields.configure.logSearchImage
);
const kesImage = useSelector(
(state: AppState) => state.tenants.createTenant.fields.configure.kesImage
(state: AppState) => state.createTenant.fields.configure.kesImage
);
const logSearchPostgresImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchPostgresImage
state.createTenant.fields.configure.logSearchPostgresImage
);
const logSearchPostgresInitImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.logSearchPostgresInitImage
state.createTenant.fields.configure.logSearchPostgresInitImage
);
const prometheusSelectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusSelectedStorageClass
state.createTenant.fields.configure.prometheusSelectedStorageClass
);
const prometheusImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusImage
(state: AppState) => state.createTenant.fields.configure.prometheusImage
);
const prometheusSidecarImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusSidecarImage
state.createTenant.fields.configure.prometheusSidecarImage
);
const prometheusInitImage = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.configure.prometheusInitImage
(state: AppState) => state.createTenant.fields.configure.prometheusInitImage
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -42,7 +42,7 @@ import {
deleteKeyPair,
isPageValid,
updateAddField,
} from "../../tenantsSlice";
} from "../createTenantSlice";
interface ISecurityProps {
classes: any;
@@ -120,22 +120,19 @@ const Security = ({ classes }: ISecurityProps) => {
const dispatch = useDispatch();
const enableTLS = useSelector(
(state: AppState) => state.tenants.createTenant.fields.security.enableTLS
(state: AppState) => state.createTenant.fields.security.enableTLS
);
const enableAutoCert = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.security.enableAutoCert
(state: AppState) => state.createTenant.fields.security.enableAutoCert
);
const enableCustomCerts = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.security.enableCustomCerts
(state: AppState) => state.createTenant.fields.security.enableCustomCerts
);
const minioCertificates = useSelector(
(state: AppState) =>
state.tenants.createTenant.certificates.minioCertificates
(state: AppState) => state.createTenant.certificates.minioCertificates
);
const caCertificates = useSelector(
(state: AppState) => state.tenants.createTenant.certificates.caCertificates
(state: AppState) => state.createTenant.certificates.caCertificates
);
// Common

View File

@@ -52,32 +52,30 @@ const styles = (theme: Theme) =>
const SizePreview = ({ classes }: ISizePreviewProps) => {
const nodes = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.nodes
(state: AppState) => state.createTenant.fields.tenantSize.nodes
);
const memoryNode = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesMemoryRequest
state.createTenant.fields.tenantSize.resourcesMemoryRequest
);
const ecParity = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.ecParity
(state: AppState) => state.createTenant.fields.tenantSize.ecParity
);
const distribution = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.distribution
(state: AppState) => state.createTenant.fields.tenantSize.distribution
);
const ecParityCalc = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.ecParityCalc
(state: AppState) => state.createTenant.fields.tenantSize.ecParityCalc
);
const cpuToUse = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesCPURequest
state.createTenant.fields.tenantSize.resourcesCPURequest
);
const integrationSelection = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.integrationSelection
state.createTenant.fields.tenantSize.integrationSelection
);
const usableInformation = ecParityCalc.storageFactors.find(

View File

@@ -59,7 +59,8 @@ import {
setStorageClassesList,
setStorageType,
updateAddField,
} from "../../../tenantsSlice";
} from "../../createTenantSlice";
import { selFeatures } from "../../../../consoleSlice";
const styles = (theme: Theme) =>
createStyles({
@@ -84,25 +85,23 @@ const NameTenantMain = ({ classes, formToRender }: INameTenantMainScreen) => {
const dispatch = useDispatch();
const tenantName = useSelector(
(state: AppState) => state.tenants.createTenant.fields.nameTenant.tenantName
(state: AppState) => state.createTenant.fields.nameTenant.tenantName
);
const namespace = useSelector(
(state: AppState) => state.tenants.createTenant.fields.nameTenant.namespace
(state: AppState) => state.createTenant.fields.nameTenant.namespace
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
state.createTenant.fields.nameTenant.selectedStorageClass
);
const selectedStorageType = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageType
state.createTenant.fields.nameTenant.selectedStorageType
);
const storageClasses = useSelector(
(state: AppState) => state.tenants.createTenant.storageClasses
);
const features = useSelector(
(state: AppState) => state.console.session.features
(state: AppState) => state.createTenant.storageClasses
);
const features = useSelector(selFeatures);
const [validationErrors, setValidationErrors] = useState<any>({});
const [emptyNamespace, setEmptyNamespace] = useState<boolean>(true);

View File

@@ -15,17 +15,14 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { useSelector } from "react-redux";
import get from "lodash/get";
import NameTenantMain from "./NameTenantMain";
import { IMkEnvs, resourcesConfigurations } from "./utils";
import { AppState } from "../../../../../../store";
import { selFeatures } from "../../../../consoleSlice";
interface ITenantResources {
features?: string[];
}
const TenantResources = ({ features }: ITenantResources) => {
const TenantResources = () => {
const features = useSelector(selFeatures);
const [formRender, setFormRender] = useState<IMkEnvs | null>(null);
useEffect(() => {
@@ -55,10 +52,4 @@ const TenantResources = ({ features }: ITenantResources) => {
return <NameTenantMain formToRender={formRender} />;
};
const mapState = (state: AppState) => ({
features: state.console.session.features,
});
const connector = connect(mapState, null);
export default connector(TenantResources);
export default TenantResources;

View File

@@ -44,7 +44,7 @@ import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/Selec
import TenantSizeResources from "./TenantSizeResources";
import InputUnitMenu from "../../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { IMkEnvs } from "./utils";
import { isPageValid, updateAddField } from "../../../tenantsSlice";
import { isPageValid, updateAddField } from "../../createTenantSlice";
interface ITenantSizeProps {
classes: any;
@@ -73,58 +73,51 @@ const TenantSize = ({ classes, formToRender }: ITenantSizeProps) => {
const dispatch = useDispatch();
const volumeSize = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.volumeSize
(state: AppState) => state.createTenant.fields.tenantSize.volumeSize
);
const sizeFactor = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.sizeFactor
(state: AppState) => state.createTenant.fields.tenantSize.sizeFactor
);
const drivesPerServer = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.drivesPerServer
(state: AppState) => state.createTenant.fields.tenantSize.drivesPerServer
);
const nodes = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.nodes
(state: AppState) => state.createTenant.fields.tenantSize.nodes
);
const memoryNode = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.memoryNode
(state: AppState) => state.createTenant.fields.tenantSize.memoryNode
);
const ecParity = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.ecParity
(state: AppState) => state.createTenant.fields.tenantSize.ecParity
);
const ecParityChoices = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.ecParityChoices
(state: AppState) => state.createTenant.fields.tenantSize.ecParityChoices
);
const cleanECChoices = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.cleanECChoices
(state: AppState) => state.createTenant.fields.tenantSize.cleanECChoices
);
const resourcesSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesSize
(state: AppState) => state.createTenant.fields.tenantSize.resourcesSize
);
const distribution = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.distribution
(state: AppState) => state.createTenant.fields.tenantSize.distribution
);
const ecParityCalc = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.ecParityCalc
(state: AppState) => state.createTenant.fields.tenantSize.ecParityCalc
);
const untouchedECField = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.untouchedECField
(state: AppState) => state.createTenant.fields.tenantSize.untouchedECField
);
const limitSize = useSelector(
(state: AppState) => state.tenants.createTenant.limitSize
(state: AppState) => state.createTenant.limitSize
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
state.createTenant.fields.nameTenant.selectedStorageClass
);
const selectedStorageType = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageType
state.createTenant.fields.nameTenant.selectedStorageType
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -41,7 +41,7 @@ import {
IntegrationConfiguration,
mkPanelConfigurations,
} from "./utils";
import { isPageValid, updateAddField } from "../../../tenantsSlice";
import { isPageValid, updateAddField } from "../../createTenantSlice";
interface ITenantSizeAWSProps {
classes: any;
@@ -74,60 +74,54 @@ const TenantSizeMK = ({
const dispatch = useDispatch();
const volumeSize = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.volumeSize
(state: AppState) => state.createTenant.fields.tenantSize.volumeSize
);
const sizeFactor = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.sizeFactor
(state: AppState) => state.createTenant.fields.tenantSize.sizeFactor
);
const drivesPerServer = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.drivesPerServer
(state: AppState) => state.createTenant.fields.tenantSize.drivesPerServer
);
const nodes = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.nodes
(state: AppState) => state.createTenant.fields.tenantSize.nodes
);
const memoryNode = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.memoryNode
(state: AppState) => state.createTenant.fields.tenantSize.memoryNode
);
const ecParity = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.ecParity
(state: AppState) => state.createTenant.fields.tenantSize.ecParity
);
const ecParityChoices = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.ecParityChoices
(state: AppState) => state.createTenant.fields.tenantSize.ecParityChoices
);
const cleanECChoices = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.cleanECChoices
(state: AppState) => state.createTenant.fields.tenantSize.cleanECChoices
);
const resourcesSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesSize
(state: AppState) => state.createTenant.fields.tenantSize.resourcesSize
);
const distribution = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.distribution
(state: AppState) => state.createTenant.fields.tenantSize.distribution
);
const ecParityCalc = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.ecParityCalc
(state: AppState) => state.createTenant.fields.tenantSize.ecParityCalc
);
const cpuToUse = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.cpuToUse
(state: AppState) => state.createTenant.fields.tenantSize.cpuToUse
);
const maxCPUsUse = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.maxCPUsUse
(state: AppState) => state.createTenant.fields.tenantSize.maxCPUsUse
);
const integrationSelection = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.integrationSelection
state.createTenant.fields.tenantSize.integrationSelection
);
const limitSize = useSelector(
(state: AppState) => state.tenants.createTenant.limitSize
(state: AppState) => state.createTenant.limitSize
);
const selectedStorageType = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageType
state.createTenant.fields.nameTenant.selectedStorageType
);
const [validationErrors, setValidationErrors] = useState<any>({});

View File

@@ -33,7 +33,7 @@ import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/I
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import { floor } from "lodash";
import InputUnitMenu from "../../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { isPageValid, updateAddField } from "../../../tenantsSlice";
import { isPageValid, updateAddField } from "../../createTenantSlice";
interface ITenantSizeResourcesProps {
classes: any;
@@ -66,62 +66,59 @@ ITenantSizeResourcesProps) => {
const dispatch = useDispatch();
const nodes = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.nodes
(state: AppState) => state.createTenant.fields.tenantSize.nodes
);
const resourcesSize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesSize
(state: AppState) => state.createTenant.fields.tenantSize.resourcesSize
);
const selectedStorageClass = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.nameTenant.selectedStorageClass
state.createTenant.fields.nameTenant.selectedStorageClass
);
const maxCPUsUse = useSelector(
(state: AppState) => state.tenants.createTenant.fields.tenantSize.maxCPUsUse
(state: AppState) => state.createTenant.fields.tenantSize.maxCPUsUse
);
const maxMemorySize = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.maxMemorySize
(state: AppState) => state.createTenant.fields.tenantSize.maxMemorySize
);
const resourcesSpecifyLimit = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesSpecifyLimit
state.createTenant.fields.tenantSize.resourcesSpecifyLimit
);
const resourcesCPURequestError = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesCPURequestError
state.createTenant.fields.tenantSize.resourcesCPURequestError
);
const resourcesCPURequest = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesCPURequest
state.createTenant.fields.tenantSize.resourcesCPURequest
);
const resourcesCPULimitError = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesCPULimitError
state.createTenant.fields.tenantSize.resourcesCPULimitError
);
const resourcesCPULimit = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesCPULimit
(state: AppState) => state.createTenant.fields.tenantSize.resourcesCPULimit
);
const resourcesMemoryRequestError = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesMemoryRequestError
state.createTenant.fields.tenantSize.resourcesMemoryRequestError
);
const resourcesMemoryRequest = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesMemoryRequest
state.createTenant.fields.tenantSize.resourcesMemoryRequest
);
const resourcesMemoryLimitError = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesMemoryLimitError
state.createTenant.fields.tenantSize.resourcesMemoryLimitError
);
const resourcesMemoryLimit = useSelector(
(state: AppState) =>
state.tenants.createTenant.fields.tenantSize.resourcesMemoryLimit
state.createTenant.fields.tenantSize.resourcesMemoryLimit
);
// Common

File diff suppressed because it is too large Load Diff

View File

@@ -325,13 +325,12 @@ const Affinity = ({ classes }: IAffinityProps) => {
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const newKey = e.target.value as string;
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value as string;
arrCp[i].value = keyValueMap[newKey][0];
const newLKP: LabelKeyPair = {
key: newKey,
value: keyValueMap[newKey][0],
};
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = newLKP;
dispatch(setPoolKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -348,11 +347,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.key}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setPoolKeyValuePairs(arrCp));
}}
index={i}
@@ -364,11 +363,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
{keyOptions.length > 0 && (
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value as string;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setPoolKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -391,11 +390,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.value}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setPoolKeyValuePairs(arrCp));
}}
index={i}
@@ -408,7 +407,7 @@ const Affinity = ({ classes }: IAffinityProps) => {
<IconButton
size={"small"}
onClick={() => {
const arrCp = Object.assign([], keyValuePairs);
const arrCp = [...keyValuePairs];
if (keyOptions.length > 0) {
arrCp.push({
key: keyOptions[0].value,

View File

@@ -322,13 +322,12 @@ const Affinity = ({ classes }: IAffinityProps) => {
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const newKey = e.target.value as string;
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value as string;
arrCp[i].value = keyValueMap[newKey][0];
const newLKP: LabelKeyPair = {
key: newKey,
value: keyValueMap[newKey][0],
};
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = newLKP;
dispatch(setEditPoolKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -345,11 +344,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.key}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].key = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setEditPoolKeyValuePairs(arrCp));
}}
index={i}
@@ -361,11 +360,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
{keyOptions.length > 0 && (
<SelectWrapper
onChange={(e: SelectChangeEvent<string>) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value as string;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setEditPoolKeyValuePairs(arrCp));
}}
id="select-access-policy"
@@ -388,11 +387,11 @@ const Affinity = ({ classes }: IAffinityProps) => {
name={`nodeselector-${i.toString()}`}
value={kvp.value}
onChange={(e) => {
const arrCp: LabelKeyPair[] = Object.assign(
[],
keyValuePairs
);
arrCp[i].value = e.target.value;
const arrCp: LabelKeyPair[] = [...keyValuePairs];
arrCp[i] = {
key: arrCp[i].key,
value: e.target.value as string,
};
dispatch(setEditPoolKeyValuePairs(arrCp));
}}
index={i}
@@ -405,7 +404,7 @@ const Affinity = ({ classes }: IAffinityProps) => {
<IconButton
size={"small"}
onClick={() => {
const arrCp = Object.assign([], keyValuePairs);
const arrCp = [...keyValuePairs];
if (keyOptions.length > 0) {
arrCp.push({
key: keyOptions[0].value,

File diff suppressed because it is too large Load Diff

View File

@@ -26,100 +26,6 @@ import { IResourcesSize, ITenant } from "./ListTenants/types";
import { KeyPair, Opts } from "./ListTenants/utils";
import { IntegrationConfiguration } from "./AddTenant/Steps/TenantResources/utils";
export const ADD_TENANT_SET_CURRENT_PAGE = "ADD_TENANT/SET_CURRENT_PAGE";
export const ADD_TENANT_UPDATE_FIELD = "ADD_TENANT/UPDATE_FIELD";
export const ADD_TENANT_SET_PAGE_VALID = "ADD_TENANT/SET_PAGE_VALID";
export const ADD_TENANT_RESET_FORM = "ADD_TENANT/RESET_FORM";
// Name Tenant
export const ADD_TENANT_SET_STORAGE_CLASSES_LIST =
"ADD_TENANT/SET_STORAGE_CLASSES_LIST";
export const ADD_TENANT_SET_LIMIT_SIZE = "ADD_TENANT/SET_LIMIT_SIZE";
export const ADD_TENANT_SET_STORAGE_TYPE =
"ADD_TENANT/ADD_TENANT_SET_STORAGE_TYPE";
// Configuration
export const ADD_TENANT_ADD_MINIO_DOMAIN = "ADD_TENANT/ADD_MINIO_DOMAIN";
export const ADD_TENANT_DELETE_MINIO_DOMAIN = "ADD_TENANT/DELETE_MINIO_DOMAIN";
// Security
export const ADD_TENANT_ADD_MINIO_KEYPAIR = "ADD_TENANT/ADD_MINIO_KEYPAIR";
export const ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR =
"ADD_TENANT/ADD_FILE_MINIO_KEYPAIR";
export const ADD_TENANT_DELETE_MINIO_KEYPAIR =
"ADD_TENANT/DELETE_MINIO_KEYPAIR";
export const ADD_TENANT_ADD_CA_KEYPAIR = "ADD_TENANT/ADD_CA_KEYPAIR";
export const ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR =
"ADD_TENANT/ADD_FILE_TO_CA_KEYPAIR";
export const ADD_TENANT_DELETE_CA_KEYPAIR = "ADD_TENANT/DELETE_CA_KEYPAIR";
export const ADD_TENANT_ADD_CONSOLE_CERT = "ADD_TENANT/ADD_CONSOLE_CERT";
export const ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR =
"ADD_TENANT/ADD_CONSOLE_CA_KEYPAIR";
export const ADD_TENANT_ADD_FILE_TO_CONSOLE_CA_KEYPAIR =
"ADD_TENANT/ADD_FILE_TO_CONSOLE_CA_KEYPAIR";
export const ADD_TENANT_DELETE_CONSOLE_CA_KEYPAIR =
"ADD_TENANT/DELETE_CONSOLE_CA_KEYPAIR";
// Encryption
export const ADD_TENANT_ENCRYPTION_SERVER_CERT =
"ADD_TENANT/ENCRYPTION_SERVER_CERT";
export const ADD_TENANT_ENCRYPTION_CLIENT_CERT =
"ADD_TENANT/ENCRYPTION_CLIENT_CERT";
export const ADD_TENANT_ENCRYPTION_VAULT_CERT =
"ADD_TENANT/ENCRYPTION_VAULT_CERT";
export const ADD_TENANT_ENCRYPTION_VAULT_CA = "ADD_TENANT/ENCRYPTION_VAULT_CA";
export const ADD_TENANT_ENCRYPTION_GEMALTO_CA =
"ADD_TENANT/ENCRYPTION_GEMALTO_CA";
// Affinity Node Selector KeyPairs
export const ADD_TENANT_SET_KEY_PAIR_VALUE = "ADD_TENANT/SET_KEY_PAIR_VALUE";
// Affinity Tolerations
export const ADD_TENANT_SET_TOLERATION_VALUE =
"ADD_TENANT/SET_TOLERATION_VALUE";
export const ADD_TENANT_ADD_NEW_TOLERATION = "ADD_TENANT/ADD_NEW_TOLERATION";
export const ADD_TENANT_REMOVE_TOLERATION_ROW =
"ADD_TENANT/REMOVE_TOLERATION_ROW";
// Tenant Details
export const TENANT_DETAILS_SET_LOADING = "TENANT_DETAILS/SET_LOADING";
export const TENANT_DETAILS_SET_CURRENT_TENANT =
"TENANT_DETAILS/SET_CURRENT_TENANT";
export const TENANT_DETAILS_SET_TENANT = "TENANT_DETAILS/SET_TENANT";
export const TENANT_DETAILS_SET_TAB = "TENANT_DETAILS/SET_TAB";
// Add Pool
export const ADD_POOL_SET_POOL_STORAGE_CLASSES =
"ADD_POOL/SET_POOL_STORAGE_CLASSES";
export const ADD_POOL_SET_PAGE_VALID = "ADD_POOL/SET_PAGE_VALID";
export const ADD_POOL_SET_VALUE = "ADD_POOL/SET_VALUE";
export const ADD_POOL_SET_LOADING = "ADD_POOL/SET_LOADING";
export const ADD_POOL_RESET_FORM = "ADD_POOL/RESET_FORM";
export const ADD_POOL_SET_KEY_PAIR_VALUE = "ADD_POOL/SET_KEY_PAIR_VALUE";
// Pool Tolerations
export const ADD_POOL_SET_TOLERATION_VALUE = "ADD_POOL/SET_TOLERATION_VALUE";
export const ADD_POOL_ADD_NEW_TOLERATION = "ADD_POOL/ADD_NEW_TOLERATION";
export const ADD_POOL_REMOVE_TOLERATION_ROW = "ADD_POOL/REMOVE_TOLERATION_ROW";
// Pool Details
export const POOL_DETAILS_SET_OPEN_DETAILS = "POOL_DETAILS/SET_OPEN_DETAILS";
export const POOL_DETAILS_SET_SELECTED_POOL = "POOL_DETAILS/SET_SELECTED_POOL";
// Edit Pool
export const EDIT_POOL_SET_INITIAL_INFO = "EDIT_POOL/SET_INITIAL_INFO";
export const EDIT_POOL_SET_POOL_STORAGE_CLASSES =
"EDIT_POOL/SET_POOL_STORAGE_CLASSES";
export const EDIT_POOL_SET_PAGE_VALID = "EDIT_POOL/SET_PAGE_VALID";
export const EDIT_POOL_SET_VALUE = "EDIT_POOL/SET_VALUE";
export const EDIT_POOL_SET_LOADING = "EDIT_POOL/SET_LOADING";
export const EDIT_POOL_RESET_FORM = "EDIT_POOL/RESET_FORM";
export const EDIT_POOL_SET_KEY_PAIR_VALUE = "EDIT_POOL/SET_KEY_PAIR_VALUE";
export const EDIT_POOL_SET_TOLERATION_VALUE = "EDIT_POOL/SET_TOLERATION_VALUE";
export const EDIT_POOL_ADD_NEW_TOLERATION = "EDIT_POOL/ADD_NEW_TOLERATION";
export const EDIT_POOL_REMOVE_TOLERATION_ROW =
"EDIT_POOL/REMOVE_TOLERATION_ROW";
export interface ICertificateInfo {
name: string;
serialNumber: string;
@@ -192,17 +98,6 @@ export interface ITenantEncryptionResponse {
azure?: IAzureConfig;
}
export interface ICreateTenant {
page: number;
validPages: string[];
storageClasses: Opts[];
limitSize: any;
fields: IFieldStore;
certificates: ICertificatesItems;
nodeSelectorPairs: LabelKeyPair[];
tolerations: ITolerationModel[];
}
export interface ICertificatesItems {
minioCertificates: KeyPair[];
caCertificates: KeyPair[];
@@ -394,7 +289,6 @@ export interface ITenantDetails {
}
export interface ITenantState {
createTenant: ICreateTenant;
tenantDetails: ITenantDetails;
addPool: IAddPool;
editPool: IEditPool;

View File

@@ -38,10 +38,9 @@ import {
performDownload,
} from "../../../common/utils";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import { AppState } from "../../../store";
import { InspectMenuIcon } from "../../../icons/SidebarMenus";
import KeyRevealer from "./KeyRevealer";
import { setErrorSnackMessage } from "../../../systemSlice";
import { selDistSet, setErrorSnackMessage } from "../../../systemSlice";
const styles = (theme: Theme) =>
createStyles({
@@ -93,9 +92,7 @@ const ExampleBlock = ({
const Inspect = ({ classes }: { classes: any }) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(
(state: AppState) => state.system.distributedSetup
);
const distributedSetup = useSelector(selDistSet);
const [volumeName, setVolumeName] = useState<string>("");
const [inspectPath, setInspectPath] = useState<string>("");
const [isEncrypt, setIsEncrypt] = useState<boolean>(true);

View File

@@ -17,7 +17,6 @@
import React, { Fragment } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import {
@@ -44,15 +43,9 @@ import {
IAM_PAGES_PERMISSIONS,
} from "../../../../common/SecureComponent/permissions";
import { hasPermission } from "../../../../common/SecureComponent";
import { AppState } from "../../../../store";
import { connect } from "react-redux";
import makeStyles from "@mui/styles/makeStyles";
interface IConfigurationOptions {
classes: any;
features: string[];
}
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
settingsOptionsContainer: {
display: "flex" as const,
@@ -67,9 +60,11 @@ const styles = (theme: Theme) =>
...searchField,
...actionsTray,
...containerForHeader(theme.spacing(4)),
});
})
);
const ToolsList = ({ classes, features }: IConfigurationOptions) => {
const ToolsList = () => {
const classes = useStyles();
const configurationElements: IElement[] = [
{
icon: <LogsIcon />,
@@ -159,10 +154,4 @@ const ToolsList = ({ classes, features }: IConfigurationOptions) => {
);
};
const mapState = (state: AppState) => ({
features: state.console.session.features,
});
const connector = connect(mapState, null);
export default connector(withStyles(styles)(ToolsList));
export default ToolsList;

View File

@@ -24,12 +24,11 @@ import {
TextField,
} from "@mui/material";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { connect } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { AppState } from "../../../store";
import { watchMessageReceived, watchResetMessages } from "./actions";
import { Bucket, BucketList, EventInfo } from "./types";
import { niceBytes, timeFromDate } from "../../../common/utils";
import { wsProtocol } from "../../../utils/wsUtils";
@@ -44,8 +43,10 @@ import TableWrapper from "../Common/TableWrapper/TableWrapper";
import PageHeader from "../Common/PageHeader/PageHeader";
import api from "../../../common/api";
import PageLayout from "../Common/Layout/PageLayout";
import makeStyles from "@mui/styles/makeStyles";
import { watchMessageReceived, watchResetMessages } from "./watchSlice";
const styles = (theme: Theme) =>
const useStyles = makeStyles((theme: Theme) =>
createStyles({
searchPrefix: {
flexGrow: 1,
@@ -62,7 +63,8 @@ const styles = (theme: Theme) =>
...actionsTray,
...searchField,
...containerForHeader(theme.spacing(4)),
});
})
);
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
@@ -83,19 +85,12 @@ const SelectStyled = withStyles((theme: Theme) =>
})
)(InputBase);
interface IWatch {
classes: any;
watchMessageReceived: typeof watchMessageReceived;
watchResetMessages: typeof watchResetMessages;
messages: EventInfo[];
}
const Watch = () => {
const dispatch = useDispatch();
const classes = useStyles();
const messages = useSelector((state: AppState) => state.watch.messages);
const Watch = ({
classes,
watchMessageReceived,
watchResetMessages,
messages,
}: IWatch) => {
const [start, setStart] = useState(false);
const [bucketName, setBucketName] = useState("Select Bucket");
const [prefix, setPrefix] = useState("");
@@ -121,7 +116,7 @@ const Watch = ({
}, []);
useEffect(() => {
watchResetMessages();
dispatch(watchResetMessages());
// begin watch if bucketName in bucketList and start pressed
if (start && bucketList.some((bucket) => bucket.name === bucketName)) {
const url = new URL(window.location.toString());
@@ -150,7 +145,7 @@ const Watch = ({
let m: EventInfo = JSON.parse(message.data.toString());
m.Time = new Date(m.Time.toString());
m.key = Math.random();
watchMessageReceived(m);
dispatch(watchMessageReceived(m));
};
c.onclose = () => {
clearInterval(interval);
@@ -169,15 +164,7 @@ const Watch = ({
// reset start status
setStart(false);
}
}, [
watchMessageReceived,
start,
bucketList,
bucketName,
prefix,
suffix,
watchResetMessages,
]);
}, [dispatch, start, bucketList, bucketName, prefix, suffix]);
const bucketNames = bucketList.map((bucketName) => ({
label: bucketName.name,
@@ -296,13 +283,4 @@ const Watch = ({
);
};
const mapState = (state: AppState) => ({
messages: state.watch.messages,
});
const connector = connect(mapState, {
watchMessageReceived: watchMessageReceived,
watchResetMessages: watchResetMessages,
});
export default connector(withStyles(styles)(Watch));
export default Watch;

View File

@@ -1,46 +0,0 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { EventInfo } from "./types";
export const WATCH_MESSAGE_RECEIVED = "WATCH_MESSAGE_RECEIVED";
export const WATCH_RESET_MESSAGES = "WATCH_RESET_MESSAGES";
interface WatchMessageReceivedAction {
type: typeof WATCH_MESSAGE_RECEIVED;
message: EventInfo;
}
interface WatchResetMessagesAction {
type: typeof WATCH_RESET_MESSAGES;
}
export type WatchActionTypes =
| WatchMessageReceivedAction
| WatchResetMessagesAction;
export function watchMessageReceived(message: EventInfo) {
return {
type: WATCH_MESSAGE_RECEIVED,
message: message,
};
}
export function watchResetMessages() {
return {
type: WATCH_RESET_MESSAGES,
};
}

View File

@@ -1,5 +1,5 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
// Copyright (c) 2022 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
@@ -14,11 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import {
WATCH_MESSAGE_RECEIVED,
WATCH_RESET_MESSAGES,
WatchActionTypes,
} from "./actions";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { EventInfo } from "./types";
export interface WatchState {
@@ -29,22 +25,19 @@ const initialState: WatchState = {
messages: [],
};
export function watchReducer(
state = initialState,
action: WatchActionTypes
): WatchState {
switch (action.type) {
case WATCH_MESSAGE_RECEIVED:
return {
...state,
messages: [...state.messages, action.message],
};
case WATCH_RESET_MESSAGES:
return {
...state,
messages: [],
};
default:
return state;
}
}
export const watchSlice = createSlice({
name: "trace",
initialState,
reducers: {
watchMessageReceived: (state, action: PayloadAction<EventInfo>) => {
state.messages.push(action.payload);
},
watchResetMessages: (state) => {
state.messages = [];
},
},
});
export const { watchResetMessages, watchMessageReceived } = watchSlice.actions;
export default watchSlice.reducer;

View File

@@ -16,6 +16,7 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ISessionResponse } from "./types";
import { AppState } from "../../store";
export interface ConsoleState {
session: ISessionResponse;
@@ -46,5 +47,7 @@ export const consoleSlice = createSlice({
});
export const { saveSessionResponse, resetSession } = consoleSlice.actions;
export const selSession = (state: AppState) => state.console.session;
export const selFeatures = (state: AppState) => state.console.session.features;
export default consoleSlice.reducer;

View File

@@ -16,15 +16,17 @@
import systemReducer from "./systemSlice";
import traceReducer from "./screens/Console/Trace/traceSlice";
import { logReducer } from "./screens/Console/Logs/reducers";
import logReducer from "./screens/Console/Logs/logsSlice";
import healthInfoReducer from "./screens/Console/HealthInfo/healthInfoSlice";
import { watchReducer } from "./screens/Console/Watch/reducers";
import watchReducer from "./screens/Console/Watch/watchSlice";
import consoleReducer from "./screens/Console/consoleSlice";
import bucketsReducer from "./screens/Console/Buckets/bucketsSlice";
import bucketDetailsReducer from "./screens/Console/Buckets/BucketDetails/bucketDetailsSlice";
import objectBrowserReducer from "./screens/Console/ObjectBrowser/objectBrowserSlice";
import tenantsReducer from "./screens/Console/Tenants/tenantsSlice";
import dashboardReducer from "./screens/Console/Dashboard/dashboardSlice";
import { configureStore } from "@reduxjs/toolkit";
import createTenantReducer from "./screens/Console/Tenants/AddTenant/createTenantSlice";
export const store = configureStore({
reducer: {
@@ -34,10 +36,13 @@ export const store = configureStore({
watch: watchReducer,
console: consoleReducer,
buckets: bucketsReducer,
bucketDetails: bucketDetailsReducer,
objectBrowser: objectBrowserReducer,
healthInfo: healthInfoReducer,
tenants: tenantsReducer,
dashboard: dashboardReducer,
// Operator Reducers
tenants: tenantsReducer,
createTenant: createTenantReducer,
},
});

View File

@@ -16,6 +16,7 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { snackBarMessage, SRInfoStateType } from "./types";
import { ErrorResponseHandler } from "./common/types";
import { AppState } from "./store";
// determine whether we have the sidebar state stored on localstorage
const initSideBarOpen = localStorage.getItem("sidebarOpen")
@@ -154,4 +155,8 @@ export const {
setSiteReplicationInfo,
} = systemSlice.actions;
export const selDistSet = (state: AppState) => state.system.distributedSetup;
export const selSiteRep = (state: AppState) => state.system.siteReplicationInfo;
export const selOpMode = (state: AppState) => state.system.operatorMode;
export default systemSlice.reducer;

View File

@@ -134,10 +134,10 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
return
}
//// DELETE ME !!!
// Un-comment for development so websockets work on port 5005
// upgrader.CheckOrigin = func(r *http.Request) bool {
// return true
//}
// return true
// }
// upgrades the HTTP server connection to the WebSocket protocol.
conn, err := upgrader.Upgrade(w, req, nil)