Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions portal-ui/src/screens/Console/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import Grid from "@mui/material/Grid";
import api from "../../../common/api";
import { Box } from "@mui/material";
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
import { setErrorSnackMessage } from "../../../actions";
import { AccountIcon, AddIcon, PasswordKeyIcon } from "../../../icons";
import { setErrorSnackMessage, setSnackBarMessage } from "../../../actions";
import { AccountIcon, AddIcon, PasswordKeyIcon, DeleteIcon } from "../../../icons";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { stringSort } from "../../../utils/sortFunctions";
import PageHeader from "../Common/PageHeader/PageHeader";
Expand All @@ -46,6 +46,8 @@ import {
} from "../../../common/SecureComponent/permissions";
import SecureComponent from "../../../common/SecureComponent/SecureComponent";
import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton";
import {selectSAs} from "../../Console/Configurations/utils"
import DeleteMultipleServiceAccounts from "../Users/DeleteMultipleServiceAccounts"

const AddServiceAccount = withSuspense(
React.lazy(() => import("./AddServiceAccount"))
Expand Down Expand Up @@ -89,6 +91,8 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
useState<NewServiceAccount | null>(null);
const [changePasswordModalOpen, setChangePasswordModalOpen] =
useState<boolean>(false);
const [selectedSAs, setSelectedSAs] = useState<string[]>([]);
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);

useEffect(() => {
fetchRecords();
Expand Down Expand Up @@ -139,6 +143,24 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
}
};

const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
setDeleteMultipleOpen(false);
if (refresh) {
setSnackBarMessage(`Service accounts deleted successfully.`);
setSelectedSAs([]);
setLoading(true);
}
};


const selectAllItems = () => {
if (selectedSAs.length === records.length) {
setSelectedSAs([]);
return;
}
setSelectedSAs(records);
};

const closeCredentialsModal = () => {
setShowNewCredentials(false);
setNewServiceAccount(null);
Expand Down Expand Up @@ -176,6 +198,13 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
}}
/>
)}
{deleteMultipleOpen && (
<DeleteMultipleServiceAccounts
deleteOpen={deleteMultipleOpen}
selectedSAs={selectedSAs}
closeDeleteModalAndRefresh={closeDeleteMultipleModalAndRefresh}
/>
)}
{showNewCredentials && (
<CredentialsPrompt
newServiceAccount={newServiceAccount}
Expand Down Expand Up @@ -204,7 +233,15 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
sx={{
display: "flex",
}}
>
> <RBIconButton
tooltip={"Delete Selected"}
onClick={() => {setDeleteMultipleOpen(true);}}
text={"Delete Selected"}
icon={<DeleteIcon />}
color="secondary"
disabled={selectedSAs.length === 0}
variant={"outlined"}
/>
<SecureComponent
scopes={[IAM_SCOPES.ADMIN_CREATE_USER]}
resource={CONSOLE_UI_RESOURCE}
Expand All @@ -218,8 +255,9 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
color={"primary"}
variant={"outlined"}
/>
</SecureComponent>

</SecureComponent>


<RBIconButton
onClick={() => {
setAddScreenOpen(true);
Expand All @@ -241,6 +279,9 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
idField={""}
columns={[{ label: "Service Account", elementKey: "" }]}
itemActions={tableActions}
selectedItems={selectedSAs}
onSelect={e => selectSAs(e, setSelectedSAs, selectedSAs)}
onSelectAll={selectAllItems}
/>
</Grid>
<Grid item xs={12} marginTop={"15px"}>
Expand Down
17 changes: 17 additions & 0 deletions portal-ui/src/screens/Console/Configurations/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -522,3 +522,20 @@ export const removeEmptyFields = (formFields: IElementValue[]) => {

return nonEmptyFields;
};

export const selectSAs = (e: React.ChangeEvent<HTMLInputElement>, setSelectedSAs : Function, selectedSAs : string[]) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;

let elements: string[] = [...selectedSAs]; // We clone the selectedSAs array
if (checked) {
// If the user has checked this field we need to push this to selectedSAs
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}
setSelectedSAs(elements);
return elements;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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 from "react";
import { connect } from "react-redux";
import { DialogContentText } from "@mui/material";
import { setErrorSnackMessage } from "../../../actions";
import { ErrorResponseHandler } from "../../../common/types"
import useApi from "../../../screens/Console/Common/Hooks/useApi";
import ConfirmDialog from "../../../screens/Console/Common/ModalWrapper/ConfirmDialog";
import { ConfirmDeleteIcon } from "../../../icons";
interface IDeleteMultiSAsProps {
closeDeleteModalAndRefresh: (refresh: boolean) => void;
deleteOpen: boolean;
selectedSAs: string[];
setErrorSnackMessage: typeof setErrorSnackMessage;
}
const DeleteMultipleSAs = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedSAs,
setErrorSnackMessage,
}: IDeleteMultiSAsProps) => {
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) => setErrorSnackMessage(err);
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
if (!selectedSAs) {
return null;
}
const onConfirmDelete = () => {
invokeDeleteApi(
"POST",
`/api/v1/service-accounts/delete-multi`,
selectedSAs
);
};
return (
<ConfirmDialog
title={`Delete Service Accounts`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete the selected {selectedSAs.length}{" "}
service accounts?{" "}
</DialogContentText>
}
/>
);
};
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(null, mapDispatchToProps);

export default connector(DeleteMultipleSAs);
79 changes: 61 additions & 18 deletions portal-ui/src/screens/Console/Users/UserServiceAccountsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { Theme } from "@mui/material/styles";
import { Box } from "@mui/material";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
Expand All @@ -27,16 +28,18 @@ import {
import api from "../../../common/api";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { AppState } from "../../../store";
import { setErrorSnackMessage } from "../../../actions";
import { setErrorSnackMessage, setSnackBarMessage } from "../../../actions";
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
import { stringSort } from "../../../utils/sortFunctions";
import { ErrorResponseHandler } from "../../../common/types";
import AddUserServiceAccount from "./AddUserServiceAccount";
import DeleteServiceAccount from "../Account/DeleteServiceAccount";
import CredentialsPrompt from "../Common/CredentialsPrompt/CredentialsPrompt";
import { AddIcon } from "../../../icons";
import { AddIcon, DeleteIcon } from "../../../icons";
import PanelTitle from "../Common/PanelTitle/PanelTitle";
import RBIconButton from "../Buckets/BucketDetails/SummaryItems/RBIconButton";
import DeleteMultipleServiceAccounts from "./DeleteMultipleServiceAccounts"
import {selectSAs} from "../../Console/Configurations/utils"

interface IUserServiceAccountsProps {
classes: any;
Expand Down Expand Up @@ -71,6 +74,8 @@ const UserServiceAccountsPanel = ({
const [showNewCredentials, setShowNewCredentials] = useState<boolean>(false);
const [newServiceAccount, setNewServiceAccount] =
useState<NewServiceAccount | null>(null);
const [selectedSAs, setSelectedSAs] = useState<string[]>([]);
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);

useEffect(() => {
fetchRecords();
Expand Down Expand Up @@ -120,6 +125,24 @@ const UserServiceAccountsPanel = ({
}
};

const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
setDeleteMultipleOpen(false);
if (refresh) {
setSnackBarMessage(`Service accounts deleted successfully.`);
setSelectedSAs([]);
setLoading(true);
}
};


const selectAllItems = () => {
if (selectedSAs.length === records.length) {
setSelectedSAs([]);
return;
}
setSelectedSAs(records);
};

const closeCredentialsModal = () => {
setShowNewCredentials(false);
setNewServiceAccount(null);
Expand Down Expand Up @@ -154,6 +177,13 @@ const UserServiceAccountsPanel = ({
}}
/>
)}
{deleteMultipleOpen && (
<DeleteMultipleServiceAccounts
deleteOpen={deleteMultipleOpen}
selectedSAs={selectedSAs}
closeDeleteModalAndRefresh={closeDeleteMultipleModalAndRefresh}
/>
)}
{showNewCredentials && (
<CredentialsPrompt
newServiceAccount={newServiceAccount}
Expand All @@ -165,30 +195,43 @@ const UserServiceAccountsPanel = ({
/>
)}
<div className={classes.actionsTray}>
<PanelTitle>Service Accounts</PanelTitle>

<RBIconButton
tooltip={"Create service account"}
text={"Create service account"}
variant="contained"
color="primary"
icon={<AddIcon />}
onClick={() => {
setAddScreenOpen(true);
setAddScreenOpen(true);
setSelectedServiceAccount(null);
}}
disabled={!hasPolicy}
/>
<PanelTitle>Service Accounts</PanelTitle>
<Box >
<RBIconButton
tooltip={"Delete Selected"}
onClick={() => {setDeleteMultipleOpen(true);}}
text={"Delete Selected"}
icon={<DeleteIcon />}
color="secondary"
disabled={selectedSAs.length === 0}
variant={"outlined"}
/>
<RBIconButton
tooltip={"Create service account"}
text={"Create service account"}
variant="contained"
color="primary"
icon={<AddIcon />}
onClick={() => {
setAddScreenOpen(true);
setAddScreenOpen(true);
setSelectedServiceAccount(null);
}}
disabled={!hasPolicy}
/>
</Box>
</div>
<div className={classes.tableBlock}>
<div className={classes.tableBlock}>
<TableWrapper
isLoading={loading}
records={records}
entityName={"Service Accounts"}
idField={""}
columns={[{ label: "Service Account", elementKey: "" }]}
itemActions={tableActions}
selectedItems={selectedSAs}
onSelect={e => selectSAs(e, setSelectedSAs, selectedSAs)}
onSelectAll={selectAllItems}
/>
</div>
</React.Fragment>
Expand Down
Loading