Service account policy UI (#1519)
* saving * service account policy UI * fixing warning * Update portal-ui/src/screens/Console/Account/ServiceAccountPolicy.tsx Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> * fixing comment Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Co-authored-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -51,8 +51,9 @@ 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 { selectSAs } from "../Configurations/utils";
|
||||
import DeleteMultipleServiceAccounts from "../Users/DeleteMultipleServiceAccounts";
|
||||
import ServiceAccountPolicy from "./ServiceAccountPolicy";
|
||||
|
||||
const AddServiceAccount = withSuspense(
|
||||
React.lazy(() => import("./AddServiceAccount"))
|
||||
@@ -98,6 +99,7 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
|
||||
useState<boolean>(false);
|
||||
const [selectedSAs, setSelectedSAs] = useState<string[]>([]);
|
||||
const [deleteMultipleOpen, setDeleteMultipleOpen] = useState<boolean>(false);
|
||||
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
@@ -157,6 +159,11 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const policyModalOpen = (selectedServiceAccount: string) => {
|
||||
setSelectedServiceAccount(selectedServiceAccount);
|
||||
setPolicyOpen(true);
|
||||
};
|
||||
|
||||
const selectAllItems = () => {
|
||||
if (selectedSAs.length === records.length) {
|
||||
setSelectedSAs([]);
|
||||
@@ -170,12 +177,17 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
|
||||
setNewServiceAccount(null);
|
||||
};
|
||||
|
||||
const closePolicyModal = () => {
|
||||
setPolicyOpen(false);
|
||||
};
|
||||
|
||||
const confirmDeleteServiceAccount = (selectedServiceAccount: string) => {
|
||||
setSelectedServiceAccount(selectedServiceAccount);
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: policyModalOpen },
|
||||
{ type: "delete", onClick: confirmDeleteServiceAccount },
|
||||
];
|
||||
|
||||
@@ -219,6 +231,13 @@ const Account = ({ classes, displayErrorMessage }: IServiceAccountsProps) => {
|
||||
entity="Service Account"
|
||||
/>
|
||||
)}
|
||||
{policyOpen && (
|
||||
<ServiceAccountPolicy
|
||||
open={policyOpen}
|
||||
selectedAccessKey={selectedServiceAccount}
|
||||
closeModalAndRefresh={closePolicyModal}
|
||||
/>
|
||||
)}
|
||||
<ChangePasswordModal
|
||||
open={changePasswordModalOpen}
|
||||
closeModal={() => setChangePasswordModalOpen(false)}
|
||||
|
||||
131
portal-ui/src/screens/Console/Account/ServiceAccountPolicy.tsx
Normal file
131
portal-ui/src/screens/Console/Account/ServiceAccountPolicy.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
// 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, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Button } from "@mui/material";
|
||||
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 {
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { setModalErrorSnackMessage } from "../../../actions";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import api from "../../../common/api";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import { ChangeAccessPolicyIcon } from "../../../icons";
|
||||
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
codeMirrorContainer: {
|
||||
marginBottom: 20,
|
||||
"& label": {
|
||||
marginBottom: ".5rem",
|
||||
},
|
||||
"& label + div": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
...formFieldStyles,
|
||||
...modalStyleUtils,
|
||||
...spacingUtils,
|
||||
});
|
||||
createStyles({
|
||||
...modalStyleUtils,
|
||||
...spacingUtils,
|
||||
});
|
||||
|
||||
interface IServiceAccountPolicyProps {
|
||||
classes: any;
|
||||
open: boolean;
|
||||
selectedAccessKey: string | null;
|
||||
closeModalAndRefresh: () => void;
|
||||
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
|
||||
}
|
||||
|
||||
const ServiceAccountPolicy = ({
|
||||
classes,
|
||||
open,
|
||||
selectedAccessKey,
|
||||
closeModalAndRefresh,
|
||||
setModalErrorSnackMessage,
|
||||
}: IServiceAccountPolicyProps) => {
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [policyDefinition, setPolicyDefinition] = useState<string>("");
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/service-accounts/${selectedAccessKey}/policy`)
|
||||
.then((res) => {
|
||||
setLoading(false);
|
||||
setPolicyDefinition(res);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loading, setLoading, setModalErrorSnackMessage, selectedAccessKey]);
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title="Service Account Policy"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
titleIcon={<ChangeAccessPolicyIcon />}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.codeMirrorContainer}>
|
||||
<CodeMirrorWrapper
|
||||
label={`Service Account Policy`}
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
editorHeight={"350px"}
|
||||
readOnly={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.modalButtonBar}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
setModalErrorSnackMessage,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(ServiceAccountPolicy));
|
||||
@@ -19,6 +19,7 @@ package restapi
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -366,6 +367,11 @@ func getServiceAccountPolicy(ctx context.Context, userClient MinioAdmin, accessK
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var policy iampolicy.Policy
|
||||
json.Unmarshal([]byte(serviceAccountInfo.Policy), &policy)
|
||||
if policy.Statements == nil {
|
||||
return "", nil
|
||||
}
|
||||
return serviceAccountInfo.Policy, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user