2020-04-02 12:51:51 -07:00
|
|
|
|
// This file is part of MinIO Console Server
|
2021-01-19 17:04:13 -06:00
|
|
|
|
// Copyright (c) 2021 MinIO, Inc.
|
2020-04-01 18:18:57 -07:00
|
|
|
|
//
|
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
|
2021-11-02 16:41:52 -07:00
|
|
|
|
import React, { Fragment, useEffect, useState } from "react";
|
2023-02-01 14:33:03 -08:00
|
|
|
|
import {
|
|
|
|
|
|
AccountIcon,
|
|
|
|
|
|
AddIcon,
|
2023-05-26 11:24:34 -06:00
|
|
|
|
Box,
|
2023-02-01 14:33:03 -08:00
|
|
|
|
Button,
|
2023-05-26 11:24:34 -06:00
|
|
|
|
DataTable,
|
2023-02-01 14:33:03 -08:00
|
|
|
|
DeleteIcon,
|
2023-05-26 11:24:34 -06:00
|
|
|
|
Grid,
|
2023-02-01 14:33:03 -08:00
|
|
|
|
HelpBox,
|
2023-04-29 21:16:09 -06:00
|
|
|
|
PageLayout,
|
2023-02-01 14:33:03 -08:00
|
|
|
|
PasswordKeyIcon,
|
|
|
|
|
|
} from "mds";
|
2022-06-10 13:05:21 -05:00
|
|
|
|
import { useSelector } from "react-redux";
|
2022-06-09 17:37:21 -05:00
|
|
|
|
import { useNavigate } from "react-router-dom";
|
2023-05-28 19:47:10 -06:00
|
|
|
|
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
|
2022-04-22 17:49:24 -07:00
|
|
|
|
|
2020-12-07 17:11:08 -06:00
|
|
|
|
import ChangePasswordModal from "./ChangePasswordModal";
|
2021-11-19 02:43:07 +05:30
|
|
|
|
import SearchBox from "../Common/SearchBox";
|
2021-11-25 09:15:09 +05:30
|
|
|
|
import withSuspense from "../Common/Components/withSuspense";
|
2022-09-09 17:42:30 -05:00
|
|
|
|
|
2022-02-08 22:52:55 -08:00
|
|
|
|
import { selectSAs } from "../Configurations/utils";
|
2022-02-04 20:10:34 -08:00
|
|
|
|
import DeleteMultipleServiceAccounts from "../Users/DeleteMultipleServiceAccounts";
|
2023-11-22 09:38:23 +05:30
|
|
|
|
import EditServiceAccount from "./EditServiceAccount";
|
2023-08-21 11:00:45 -07:00
|
|
|
|
|
2022-05-25 19:12:07 -05:00
|
|
|
|
import { selFeatures } from "../consoleSlice";
|
2022-09-09 17:42:30 -05:00
|
|
|
|
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
|
2023-02-14 11:36:49 -06:00
|
|
|
|
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
|
2023-06-13 16:00:56 -06:00
|
|
|
|
import { api } from "api";
|
|
|
|
|
|
import { errorToHandler } from "api/errors";
|
2023-06-12 17:50:25 -04:00
|
|
|
|
import HelpMenu from "../HelpMenu";
|
2023-08-16 23:25:07 +05:30
|
|
|
|
import { ACCOUNT_TABLE_COLUMNS } from "./AccountUtils";
|
2023-08-21 11:00:45 -07:00
|
|
|
|
import { useAppDispatch } from "store";
|
2023-09-21 08:15:51 +05:30
|
|
|
|
import { ServiceAccounts } from "api/consoleApi";
|
2023-08-21 11:00:45 -07:00
|
|
|
|
import {
|
|
|
|
|
|
setErrorSnackMessage,
|
|
|
|
|
|
setHelpName,
|
|
|
|
|
|
setSnackBarMessage,
|
|
|
|
|
|
} from "systemSlice";
|
|
|
|
|
|
import { usersSort } from "utils/sortFunctions";
|
|
|
|
|
|
import { SecureComponent } from "common/SecureComponent";
|
|
|
|
|
|
import {
|
|
|
|
|
|
CONSOLE_UI_RESOURCE,
|
|
|
|
|
|
IAM_PAGES,
|
|
|
|
|
|
IAM_SCOPES,
|
|
|
|
|
|
} from "common/SecureComponent/permissions";
|
2021-11-25 09:15:09 +05:30
|
|
|
|
|
|
|
|
|
|
const DeleteServiceAccount = withSuspense(
|
2023-07-18 09:58:21 -06:00
|
|
|
|
React.lazy(() => import("./DeleteServiceAccount")),
|
2021-11-25 09:15:09 +05:30
|
|
|
|
);
|
2022-04-22 17:49:24 -07:00
|
|
|
|
|
2022-06-09 17:37:21 -05:00
|
|
|
|
const Account = () => {
|
2022-06-10 13:05:21 -05:00
|
|
|
|
const dispatch = useAppDispatch();
|
2022-06-09 17:37:21 -05:00
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
|
|
2022-05-25 19:12:07 -05:00
|
|
|
|
const features = useSelector(selFeatures);
|
|
|
|
|
|
|
2023-08-16 23:25:07 +05:30
|
|
|
|
const [records, setRecords] = useState<ServiceAccounts>([]);
|
2020-05-19 15:41:46 -05:00
|
|
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
|
|
|
|
const [filter, setFilter] = useState<string>("");
|
|
|
|
|
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
2022-04-25 15:41:06 -07:00
|
|
|
|
const [selectedServiceAccount, setSelectedServiceAccount] = useState<
|
|
|
|
|
|
string | null
|
|
|
|
|
|
>(null);
|
|
|
|
|
|
const [changePasswordModalOpen, setChangePasswordModalOpen] =
|
|
|
|
|
|
useState<boolean>(false);
|
2022-02-02 10:21:56 -08:00
|
|
|
|
const [selectedSAs, setSelectedSAs] = useState<string[]>([]);
|
2022-02-04 20:10:34 -08:00
|
|
|
|
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);
|
2023-11-22 09:38:23 +05:30
|
|
|
|
const [isEditOpen, setIsEditOpen] = useState<boolean>(false);
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2022-05-13 12:10:00 -07:00
|
|
|
|
const userIDP = (features && features.includes("external-idp")) || false;
|
|
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
fetchRecords();
|
|
|
|
|
|
}, []);
|
2022-05-18 17:02:26 -05:00
|
|
|
|
|
2023-06-12 17:50:25 -04:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
dispatch(setHelpName("accessKeys"));
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (loading) {
|
2023-06-13 16:00:56 -06:00
|
|
|
|
api.serviceAccounts
|
|
|
|
|
|
.listUserServiceAccounts()
|
|
|
|
|
|
.then((res) => {
|
2020-05-19 15:41:46 -05:00
|
|
|
|
setLoading(false);
|
2023-08-16 23:25:07 +05:30
|
|
|
|
const sortedRows = res.data.sort(usersSort);
|
|
|
|
|
|
setRecords(sortedRows);
|
2020-04-01 18:18:57 -07:00
|
|
|
|
})
|
2023-09-21 08:15:51 +05:30
|
|
|
|
.catch((res) => {
|
|
|
|
|
|
dispatch(
|
|
|
|
|
|
setErrorSnackMessage(
|
|
|
|
|
|
errorToHandler(res?.error || "Error retrieving access keys"),
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2020-05-19 15:41:46 -05:00
|
|
|
|
setLoading(false);
|
2020-04-01 18:18:57 -07:00
|
|
|
|
});
|
2020-05-19 15:41:46 -05:00
|
|
|
|
}
|
2022-05-18 17:02:26 -05:00
|
|
|
|
}, [loading, setLoading, setRecords, dispatch]);
|
2020-05-19 15:41:46 -05:00
|
|
|
|
|
|
|
|
|
|
const fetchRecords = () => {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
|
|
|
|
|
setDeleteOpen(false);
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
if (refresh) {
|
2023-05-26 16:58:01 -06:00
|
|
|
|
setSelectedSAs([]);
|
2020-05-19 15:41:46 -05:00
|
|
|
|
fetchRecords();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2022-02-02 10:21:56 -08:00
|
|
|
|
const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => {
|
|
|
|
|
|
setDeleteMultipleOpen(false);
|
|
|
|
|
|
if (refresh) {
|
2022-11-07 17:32:10 -06:00
|
|
|
|
dispatch(setSnackBarMessage(`Access keys deleted successfully.`));
|
2022-02-02 10:21:56 -08:00
|
|
|
|
setSelectedSAs([]);
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-22 09:38:23 +05:30
|
|
|
|
const editModalOpen = (selectedServiceAccount: string) => {
|
2022-02-08 22:52:55 -08:00
|
|
|
|
setSelectedServiceAccount(selectedServiceAccount);
|
2023-11-22 09:38:23 +05:30
|
|
|
|
setIsEditOpen(true);
|
2022-02-08 22:52:55 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const closePolicyModal = () => {
|
2023-11-22 09:38:23 +05:30
|
|
|
|
setIsEditOpen(false);
|
2022-02-10 17:18:57 -08:00
|
|
|
|
setLoading(true);
|
2022-02-08 22:52:55 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
const confirmDeleteServiceAccount = (selectedServiceAccount: string) => {
|
|
|
|
|
|
setSelectedServiceAccount(selectedServiceAccount);
|
|
|
|
|
|
setDeleteOpen(true);
|
|
|
|
|
|
};
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
const tableActions = [
|
2023-08-16 23:25:07 +05:30
|
|
|
|
{
|
|
|
|
|
|
type: "view",
|
|
|
|
|
|
onClick: (value: any) => {
|
|
|
|
|
|
if (value) {
|
2023-11-22 09:38:23 +05:30
|
|
|
|
editModalOpen(value.accessKey);
|
2023-08-16 23:25:07 +05:30
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
type: "delete",
|
|
|
|
|
|
onClick: (value: any) => {
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
confirmDeleteServiceAccount(value.accessKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2023-11-22 09:38:23 +05:30
|
|
|
|
{
|
|
|
|
|
|
type: "edit",
|
|
|
|
|
|
onClick: (value: any) => {
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
editModalOpen(value.accessKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
2020-05-19 15:41:46 -05:00
|
|
|
|
];
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2023-08-16 23:25:07 +05:30
|
|
|
|
const filteredRecords = records.filter(
|
|
|
|
|
|
(elementItem) =>
|
|
|
|
|
|
elementItem?.accessKey?.toLowerCase().includes(filter.toLowerCase()),
|
2020-05-19 15:41:46 -05:00
|
|
|
|
);
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2020-05-19 15:41:46 -05:00
|
|
|
|
return (
|
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
|
{deleteOpen && (
|
|
|
|
|
|
<DeleteServiceAccount
|
|
|
|
|
|
deleteOpen={deleteOpen}
|
|
|
|
|
|
selectedServiceAccount={selectedServiceAccount}
|
|
|
|
|
|
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
|
|
|
|
|
closeDeleteModalAndRefresh(refresh);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2022-02-02 10:21:56 -08:00
|
|
|
|
{deleteMultipleOpen && (
|
|
|
|
|
|
<DeleteMultipleServiceAccounts
|
|
|
|
|
|
deleteOpen={deleteMultipleOpen}
|
|
|
|
|
|
selectedSAs={selectedSAs}
|
|
|
|
|
|
closeDeleteModalAndRefresh={closeDeleteMultipleModalAndRefresh}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2022-04-25 15:41:06 -07:00
|
|
|
|
|
2023-11-22 09:38:23 +05:30
|
|
|
|
{isEditOpen && (
|
|
|
|
|
|
<EditServiceAccount
|
|
|
|
|
|
open={isEditOpen}
|
2022-02-08 22:52:55 -08:00
|
|
|
|
selectedAccessKey={selectedServiceAccount}
|
|
|
|
|
|
closeModalAndRefresh={closePolicyModal}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2023-06-12 17:50:25 -04:00
|
|
|
|
<ChangePasswordModal
|
|
|
|
|
|
open={changePasswordModalOpen}
|
|
|
|
|
|
closeModal={() => setChangePasswordModalOpen(false)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<PageHeaderWrapper label="Access Keys" actions={<HelpMenu />} />
|
|
|
|
|
|
|
2021-11-13 01:56:29 +05:30
|
|
|
|
<PageLayout>
|
2023-05-26 11:24:34 -06:00
|
|
|
|
<Grid container>
|
|
|
|
|
|
<Grid item xs={12} sx={{ ...actionsTray.actionsTray }}>
|
2023-04-29 21:16:09 -06:00
|
|
|
|
<SearchBox
|
|
|
|
|
|
placeholder={"Search Access Keys"}
|
|
|
|
|
|
onChange={setFilter}
|
2023-05-26 11:24:34 -06:00
|
|
|
|
sx={{ marginRight: "auto", maxWidth: 380 }}
|
2023-04-29 21:16:09 -06:00
|
|
|
|
value={filter}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Box
|
|
|
|
|
|
sx={{
|
|
|
|
|
|
display: "flex",
|
2023-05-26 11:24:34 -06:00
|
|
|
|
flexWrap: "nowrap",
|
|
|
|
|
|
gap: 5,
|
2023-04-29 21:16:09 -06:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<TooltipWrapper tooltip={"Delete Selected"}>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
id={"delete-selected-accounts"}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setDeleteMultipleOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
label={"Delete Selected"}
|
|
|
|
|
|
icon={<DeleteIcon />}
|
|
|
|
|
|
disabled={selectedSAs.length === 0}
|
|
|
|
|
|
variant={"secondary"}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</TooltipWrapper>
|
|
|
|
|
|
<SecureComponent
|
|
|
|
|
|
scopes={[IAM_SCOPES.ADMIN_CREATE_USER]}
|
|
|
|
|
|
resource={CONSOLE_UI_RESOURCE}
|
|
|
|
|
|
matchAll
|
|
|
|
|
|
errorProps={{ disabled: true }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
id={"change-password"}
|
|
|
|
|
|
onClick={() => setChangePasswordModalOpen(true)}
|
|
|
|
|
|
label={`Change Password`}
|
|
|
|
|
|
icon={<PasswordKeyIcon />}
|
|
|
|
|
|
variant={"regular"}
|
|
|
|
|
|
disabled={userIDP}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</SecureComponent>
|
2023-05-22 21:56:31 +05:30
|
|
|
|
<SecureComponent
|
|
|
|
|
|
scopes={[IAM_SCOPES.ADMIN_CREATE_SERVICEACCOUNT]}
|
|
|
|
|
|
resource={CONSOLE_UI_RESOURCE}
|
|
|
|
|
|
matchAll
|
|
|
|
|
|
errorProps={{ disabled: true }}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
id={"create-service-account"}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
navigate(`${IAM_PAGES.ACCOUNT_ADD}`);
|
|
|
|
|
|
}}
|
|
|
|
|
|
label={`Create access key`}
|
|
|
|
|
|
icon={<AddIcon />}
|
|
|
|
|
|
variant={"callAction"}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</SecureComponent>
|
2023-04-29 21:16:09 -06:00
|
|
|
|
</Box>
|
|
|
|
|
|
</Grid>
|
2022-01-12 17:53:47 +00:00
|
|
|
|
|
2023-05-28 19:47:10 -06:00
|
|
|
|
<Grid item xs={12}>
|
2023-05-26 11:24:34 -06:00
|
|
|
|
<DataTable
|
2023-04-29 21:16:09 -06:00
|
|
|
|
itemActions={tableActions}
|
2023-08-16 23:25:07 +05:30
|
|
|
|
entityName={"Access Keys"}
|
|
|
|
|
|
columns={ACCOUNT_TABLE_COLUMNS}
|
2023-04-29 21:16:09 -06:00
|
|
|
|
onSelect={(e) => selectSAs(e, setSelectedSAs, selectedSAs)}
|
2023-08-16 23:25:07 +05:30
|
|
|
|
selectedItems={selectedSAs}
|
|
|
|
|
|
isLoading={loading}
|
|
|
|
|
|
records={filteredRecords}
|
|
|
|
|
|
idField="accessKey"
|
2023-04-29 21:16:09 -06:00
|
|
|
|
/>
|
|
|
|
|
|
</Grid>
|
2023-05-26 11:24:34 -06:00
|
|
|
|
<Grid item xs={12} sx={{ marginTop: 15 }}>
|
2023-04-29 21:16:09 -06:00
|
|
|
|
<HelpBox
|
|
|
|
|
|
title={"Learn more about ACCESS KEYS"}
|
|
|
|
|
|
iconComponent={<AccountIcon />}
|
|
|
|
|
|
help={
|
|
|
|
|
|
<Fragment>
|
|
|
|
|
|
MinIO access keys are child identities of an authenticated
|
|
|
|
|
|
MinIO user, including externally managed identities. Each
|
|
|
|
|
|
access key inherits its privileges based on the policies
|
|
|
|
|
|
attached to it’s parent user or those groups in which the
|
|
|
|
|
|
parent user has membership. Access Keys also support an
|
|
|
|
|
|
optional inline policy which further restricts access to a
|
|
|
|
|
|
subset of actions and resources available to the parent user.
|
|
|
|
|
|
<br />
|
|
|
|
|
|
<br />
|
|
|
|
|
|
You can learn more at our{" "}
|
|
|
|
|
|
<a
|
2023-05-26 11:24:34 -06:00
|
|
|
|
href="https://min.io/docs/minio/linux/administration/identity-access-management/minio-user-management.html?ref=con#id3"
|
2023-04-29 21:16:09 -06:00
|
|
|
|
target="_blank"
|
|
|
|
|
|
rel="noopener"
|
|
|
|
|
|
>
|
|
|
|
|
|
documentation
|
|
|
|
|
|
</a>
|
|
|
|
|
|
.
|
|
|
|
|
|
</Fragment>
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Grid>
|
2020-04-01 18:18:57 -07:00
|
|
|
|
</Grid>
|
2021-11-13 01:56:29 +05:30
|
|
|
|
</PageLayout>
|
2020-05-19 15:41:46 -05:00
|
|
|
|
</React.Fragment>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
2020-04-01 18:18:57 -07:00
|
|
|
|
|
2022-05-25 19:12:07 -05:00
|
|
|
|
export default Account;
|