UI for Inspect (#1583)

This commit is contained in:
Prakash Senthil Vel
2022-02-22 18:05:31 +00:00
committed by GitHub
parent 1f97f39864
commit 006b3c7da8
14 changed files with 803 additions and 4 deletions

View File

@@ -132,6 +132,7 @@ export const IAM_PAGES = {
DASHBOARD: "/tools/metrics",
TOOLS_HEAL: "/tools/heal",
TOOLS_WATCH: "/tools/watch",
TOOLS_INSPECT: "/tools/inspect",
/* Health */
HEALTH: "/health",
@@ -366,6 +367,7 @@ export const IAM_PAGES_PERMISSIONS = {
[IAM_PAGES.CALL_HOME]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
[IAM_PAGES.PROFILE]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
[IAM_PAGES.HEALTH]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
[IAM_PAGES.TOOLS_INSPECT]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
[IAM_PAGES.LICENSE]: [
IAM_SCOPES.ADMIN_SERVER_INFO,
IAM_SCOPES.ADMIN_CONFIG_UPDATE,

View File

@@ -609,3 +609,20 @@ export const decodeFileName = (text: string) => {
return text;
}
};
export const performDownload = (blob: Blob, fileName: string) => {
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
export const getCookieValue = (cookieName: string) => {
return (
document.cookie
.match("(^|;)\\s*" + cookieName + "\\s*=\\s*([^;]+)")
?.pop() || ""
);
};

View File

@@ -23,6 +23,7 @@ import Grid from "@mui/material/Grid";
import { actionsTray, fieldBasic } from "../common/styleLibrary";
import HelpIcon from "../../../../../icons/HelpIcon";
import clsx from "clsx";
import { InputProps as StandardInputProps } from "@mui/material/Input/Input";
interface IFormSwitch {
label?: string;
@@ -38,6 +39,7 @@ interface IFormSwitch {
checked: boolean;
switchOnly?: boolean;
indicatorLabels?: string[];
extraInputProps?: StandardInputProps["inputProps"];
}
const styles = (theme: Theme) =>
@@ -125,6 +127,7 @@ const FormSwitchWrapper = ({
description = "",
classes,
indicatorLabels,
extraInputProps = {},
}: IFormSwitch) => {
const switchComponent = (
<React.Fragment>
@@ -144,7 +147,7 @@ const FormSwitchWrapper = ({
onChange={onChange}
color="primary"
name={name}
inputProps={{ "aria-label": "primary checkbox" }}
inputProps={{ "aria-label": "primary checkbox", ...extraInputProps }}
disabled={disabled}
disableRipple
disableFocusRipple

View File

@@ -414,7 +414,7 @@ export const selectorsCommon = {
},
};
export const settingsCommon = {
export const settingsCommon:any = {
customTitle: {
fontSize: 18,
color: "#000",
@@ -1152,7 +1152,7 @@ export const spacingUtils: any = {
},
};
export const formFieldStyles = {
export const formFieldStyles:any = {
formFieldRow: {
marginBottom: ".8rem",
"& .MuiInputLabel-root": {

View File

@@ -326,6 +326,10 @@ const Console = ({
component: Tools,
path: IAM_PAGES.PROFILE,
},
{
component: Tools,
path: IAM_PAGES.TOOLS_INSPECT,
},
{
component: ConfigurationOptions,
path: IAM_PAGES.SETTINGS,

View File

@@ -0,0 +1,501 @@
import React, { Fragment, useEffect, useState } from "react";
import { Box, Button, DialogContentText } from "@mui/material";
import PageHeader from "../Common/PageHeader/PageHeader";
import PageLayout from "../Common/Layout/PageLayout";
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import { CopyIcon, FileBookIcon, PasswordKeyIcon } from "../../../icons";
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import {
deleteDialogStyles,
modalStyleUtils,
} from "../Common/FormComponents/common/styleLibrary";
import withStyles from "@mui/styles/withStyles";
import { setErrorSnackMessage } from "../../../actions";
import { connect } from "react-redux";
import HelpBox from "../../../common/HelpBox";
import {
deleteCookie,
getCookieValue,
performDownload,
} from "../../../common/utils";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import { AppState } from "../../../store";
import { InspectMenuIcon } from "../../../icons/SidebarMenus";
const styles = (theme: Theme) =>
createStyles({
switchLabel: {
fontWeight: "normal",
},
...deleteDialogStyles,
...modalStyleUtils,
});
const KeyRevealer = ({ value }: { value: string }) => {
const [shown, setShown] = React.useState(false);
return (
<Box
sx={{
display: "flex",
alignItems: "center",
flexFlow: {
sm: "row",
xs: "column",
},
}}
>
<InputBoxWrapper
id="inspect-dec-key"
name="inspect-dec-key"
placeholder=""
label=""
type={shown ? "text" : "password"}
onChange={() => {}}
value={value}
overlayIcon={<CopyIcon />}
extraInputProps={{
readOnly: true,
}}
overlayAction={() => navigator.clipboard.writeText(value)}
/>
<Button
sx={{
marginLeft: "10px",
}}
variant="contained"
onClick={() => setShown(!shown)}
>
Show/Hide
</Button>
</Box>
);
};
const mapState = (state: AppState) => ({
distributedSetup: state.system.distributedSetup,
});
const mapDispatchToProps = {
setErrorSnackMessage,
};
const connector = connect(mapState, mapDispatchToProps);
const Inspect = ({
classes,
setErrorSnackMessage,
distributedSetup,
}: {
classes: any;
setErrorSnackMessage: any;
distributedSetup: boolean;
}) => {
const [volumeName, setVolumeName] = useState<string>("");
const [inspectPath, setInspectPath] = useState<string>("");
const [isEncrypt, setIsEncrypt] = useState<boolean>(true);
const [decryptionKey, setDecryptionKey] = useState<string>("");
const [insFileName, setInsFileName] = useState<string>("");
const [isFormValid, setIsFormValid] = useState<boolean>(false);
const [volumeError, setVolumeError] = useState<string>("");
const [pathError, setPathError] = useState<string>("");
/**
* Validation Effect
*/
useEffect(() => {
let isVolValid;
let isPathValid;
isVolValid = volumeName.trim().length > 0;
if (!isVolValid) {
setVolumeError("This field is required");
} else if (volumeName.slice(0, 1) === "/") {
isVolValid = false;
setVolumeError("Volume/Bucket name cannot start with /");
}
isPathValid = inspectPath.trim().length > 0;
if (!inspectPath) {
setPathError("This field is required");
} else if (inspectPath.slice(0, 1) === "/") {
isPathValid = false;
setPathError("Path cannot start with /");
}
const isValid = isVolValid && isPathValid;
if (isVolValid) {
setVolumeError("");
}
if (isPathValid) {
setPathError("");
}
setIsFormValid(isValid);
}, [volumeName, inspectPath]);
const makeRequest = async (url: string) => {
return await fetch(url, { method: "GET" });
};
const performInspect = async () => {
const file = encodeURIComponent(inspectPath);
const volume = encodeURIComponent(volumeName);
const urlOfInspectApi = `/api/v1/admin/inspect?volume=${volume}&file=${file}&encrypt=${isEncrypt}`;
makeRequest(urlOfInspectApi)
.then(async (res) => {
if (!res.ok) {
const resErr: any = await res.json();
setErrorSnackMessage({
errorMessage: resErr.message,
detailedError: resErr.code,
});
}
const blob: Blob = await res.blob();
//@ts-ignore
const filename = res.headers.get("content-disposition").split('"')[1];
const decryptKey = getCookieValue(filename) || "";
performDownload(blob, filename);
setInsFileName(filename);
setDecryptionKey(decryptKey);
})
.catch((err) => {
setErrorSnackMessage(err);
});
};
const resetForm = () => {
setVolumeName("");
setInspectPath("");
setIsEncrypt(true);
};
const onCloseDecKeyModal = () => {
deleteCookie(insFileName);
setDecryptionKey("");
resetForm();
};
return (
<Fragment>
<PageHeader label={"Inspect"} />
<PageLayout>
{!distributedSetup ? (
<DistributedOnly
iconComponent={<InspectMenuIcon />}
entity={"Inspect"}
/>
) : (
<Fragment>
<Box
sx={{
border: "1px solid #eaeaea",
padding: "25px",
}}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
performInspect();
}}
>
<Box>
<InputBoxWrapper
id="inspect_volume"
name="inspect_volume"
extraInputProps={{
"data-test-id": "inspect_volume",
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumeName(e.target.value);
}}
label="Volume or Bucket Name"
value={volumeName}
error={volumeError}
required
placeholder={"test-bucket"}
/>
</Box>
<Box
sx={{
marginTop: "15px",
}}
>
<InputBoxWrapper
id="inspect_path"
name="inspect_path"
extraInputProps={{
"data-test-id": "inspect_path",
}}
error={pathError}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setInspectPath(e.target.value);
}}
label="File or Path to inspect"
value={inspectPath}
required
placeholder={"test*/xl.meta"}
/>
</Box>
<Box
sx={{
marginTop: "25px",
}}
>
<FormSwitchWrapper
classes={{
inputLabel: classes.switchLabel,
}}
extraInputProps={{
"data-test-id": "inspect_encrypt",
}}
label="Encrypt"
indicatorLabels={["True", "False"]}
checked={isEncrypt}
value={"true"}
id="inspect_encrypt"
name="inspect_encrypt"
onChange={(e) => {
setIsEncrypt(!isEncrypt);
}}
/>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
marginTop: "55px",
}}
>
<Button
sx={{
marginRight: "15px",
}}
type="button"
color="primary"
variant="outlined"
data-test-id="inspect-clear-button"
onClick={resetForm}
>
Clear
</Button>
<Button
type="submit"
variant="contained"
color="primary"
data-test-id="inspect-submit-button"
disabled={!isFormValid}
>
Inspect
</Button>
</Box>
</form>
</Box>
<Box
sx={{
marginTop: "55px",
}}
>
<HelpBox
title={"Inspect"}
iconComponent={<FileBookIcon />}
help={
<Fragment>
<Box
sx={{
marginTop: "15px",
}}
>
Inspect files on MinIO server
</Box>
<Box
sx={{
marginTop: "15px",
fontWeight: 500,
}}
>
Examples
</Box>
<Box
sx={{
display: "flex",
flexFlow: "column",
flex: "2",
marginTop: "15px",
"& .step-number": {
color: "#ffffff",
height: "25px",
width: "25px",
background: "#081C42",
marginRight: "10px",
textAlign: "center",
fontWeight: 600,
borderRadius: "50%",
},
"& .step-row": {
fontSize: "16px",
display: "flex",
marginTop: "15px",
marginBottom: "15px",
},
"& code": {
paddingLeft: "10px",
paddingRight: "10px",
paddingTop: "4px",
paddingBottom: "3px",
borderRadius: "2px",
backgroundColor: "#eaeaea",
color: "#082146",
},
"& .spacer": {
marginBottom: "5px",
},
}}
>
<Box>
<Box className="step-row">
<div className="step-text">
To Download 'xl.meta' for a specific object from all
the drives in a zip file:
</div>
</Box>
<Box
sx={{
flex: "1",
marginTop: "15px",
marginLeft: "35px",
"& input": {
color: "#737373",
},
}}
>
<Box>
<label>Volume/bucket Name :</label>{" "}
<code>test-bucket</code>
<div className="spacer" />
<label>Path : </label>
<code>test*/xl.meta</code>
</Box>
</Box>
</Box>
<Box>
<Box className="step-row">
<div className="step-text">
To Download all constituent parts for a specific
object, and optionally encrypt the downloaded zip:
</div>
</Box>
<Box
sx={{
flex: "1",
marginTop: "15px",
marginLeft: "35px",
"& input": {
color: "#737373",
},
}}
>
<Box>
<label>Volume/bucket Name : </label>
<code>test-bucket</code>
<div className="spacer" />
<label>Path :</label> <code>test*/*/part.*</code>
</Box>
</Box>
</Box>
<Box>
<Box className="step-row">
<div className="step-text">
To Download recursively all objects at a prefix.
<br />
NOTE: This can be an expensive operation use it with
caution.
</div>
</Box>
<Box
sx={{
flex: "1",
marginTop: "15px",
marginLeft: "35px",
"& input": {
color: "#737373",
},
}}
>
<Box>
<label>Volume/bucket Name : </label>
<code>test-bucket</code>
<div className="spacer" />
<label>Path :</label> <code>test/**</code>
</Box>
</Box>
</Box>
</Box>
<Box
sx={{
marginTop: "30px",
}}
>
You can learn more at our{" "}
<a
href="https://github.com/minio/minio/tree/master/docs/debugging?ref=con"
target="_blank"
rel="noreferrer"
>
documentation
</a>
.
</Box>
</Fragment>
}
/>
</Box>
</Fragment>
)}
{decryptionKey ? (
<ModalWrapper
modalOpen={true}
title="Inspect Decryption Key"
onClose={onCloseDecKeyModal}
titleIcon={<PasswordKeyIcon />}
>
<DialogContentText>
<Box>
This will be displayed only once. It cannot be recovered.
<br />
Use secure medium to share this key.
</Box>
<Box>
<KeyRevealer value={decryptionKey} />
</Box>
</DialogContentText>
</ModalWrapper>
) : null}
</PageLayout>
</Fragment>
);
};
export default withStyles(styles)(connector(Inspect));

View File

@@ -19,11 +19,15 @@ import { Route, Router, Switch } from "react-router-dom";
import history from "../../../history";
import NotFoundPage from "../../NotFoundPage";
import ToolsList from "./ToolsPanel/ToolsList";
import Register from "../Support/Register";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import FeatureNotAvailablePage from "../Common/Components/FeatureNotAvailablePage";
import { SupportMenuIcon } from "../../../icons/SidebarMenus";
import withSuspense from "../Common/Components/withSuspense";
const Inspect = withSuspense(React.lazy(() => import("./Inspect")));
const Register = withSuspense(React.lazy(() => import("../Support/Register")));
const Tools = () => {
return (
<Router history={history}>
@@ -72,6 +76,7 @@ const Tools = () => {
);
}}
/>
<Route path={IAM_PAGES.TOOLS_INSPECT} exact component={Inspect} />
<Route component={NotFoundPage} />
</Switch>
</Router>

View File

@@ -39,6 +39,7 @@ import {
SupportMenuIcon,
TraceMenuIcon,
UsersMenuIcon,
InspectMenuIcon,
} from "../../icons/SidebarMenus";
import { hasPermission } from "../../common/SecureComponent";
import WatchIcon from "../../icons/WatchIcon";
@@ -159,6 +160,13 @@ export const validRoutes = (
icon: DrivesMenuIcon,
component: NavLink,
},
{
name: "Inspect",
id: "inspectObjects",
to: IAM_PAGES.TOOLS_INSPECT,
icon: InspectMenuIcon,
component: NavLink,
},
],
},
{

View File

@@ -0,0 +1,201 @@
// 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 { Role, Selector } from "testcafe";
import { readFileSync } from "fs";
const data = readFileSync(__dirname + "/../constants/timestamp.txt", "utf-8");
const $TIMESTAMP = data.trim();
let testDomainUrl = "http://localhost:9090";
let insAllowedAccKey = `inspect-allowed-${$TIMESTAMP}`;
let insAllowedSeckey = "insallowed1234";
let insNotAllowedAccKey = `inspect-not-allowed-${$TIMESTAMP}`;
let insNotAllowedSeckey = "insnotallowed1234";
/* Begin Local Testing config block */
// For local Testing Create users and assign policies then update here.
// Command to invoke the test locally: testcafe chrome tests/permissions/inspect.ts
/*
testDomainUrl = "http://localhost:5005";
insAllowedAccKey = `all-actions`;
insAllowedSeckey = "minio123";
insNotAllowedAccKey = `deny-admin`;
insNotAllowedSeckey = "minio123";
*/
/* End Local Testing config block */
const loginUrl = `${testDomainUrl}/login`;
const inspectScreenUrl = `${testDomainUrl}/tools/inspect`;
const loginSubmitBtn = Selector("form button");
export const supportSidebarEl = Selector(".MuiPaper-root")
.find("ul")
.child("#tools");
export const supportChildren = Selector("#tools-children");
export const inspectEl = supportChildren
.find("a")
.withAttribute("href", "/tools/inspect");
export const inspect_volume_input = Selector('[data-test-id="inspect_volume"]');
export const inspect_path_input = Selector('[data-test-id="inspect_path"]');
export const inspect_volume_input_err = Selector("#inspect_volume-helper-text");
export const inspect_path_input_err = Selector("#inspect_path-helper-text");
export const inspect_encrypt_input = Selector(
'[data-test-id="inspect_encrypt"]'
);
export const inspect_form_clear_btn = Selector(
'[data-test-id="inspect-clear-button"]'
);
export const inspect_form_submit_btn = Selector(
'[data-test-id="inspect-submit-button"]'
);
/** Begin Allowed Policy Test **/
export const inspectAllowedRole = Role(
loginUrl,
async (t) => {
await t
.typeText("#accessKey", insAllowedAccKey)
.typeText("#secretKey", insAllowedSeckey)
.click(loginSubmitBtn);
},
{ preserveUrl: true }
);
fixture("For user with Inspect permissions")
.page(testDomainUrl)
.beforeEach(async (t) => {
await t.useRole(inspectAllowedRole);
});
test("Inspect page can be opened", async (t) => {
await t.navigateTo(inspectScreenUrl);
});
test("Inspect sidebar item exists", async (t) => {
await t.expect(supportSidebarEl.exists).ok();
});
test("Inspect link exists in Menu list", async (t) => {
await t
.expect(supportSidebarEl.exists)
.ok()
.click(supportSidebarEl)
.expect(inspectEl.exists)
.ok();
});
test("Form Input states verification", async (t) => {
const volumeValue = "test";
const pathValue = "test.txt/xl.meta";
await t.navigateTo(inspectScreenUrl);
//Initial state verification
await t.expect(inspect_volume_input.value).eql("");
await t.expect(inspect_path_input.value).eql("");
await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).ok();
await t.expect(inspect_encrypt_input.hasAttribute("checked")).ok();
await t
.expect(inspect_volume_input_err.innerText)
.eql("This field is required");
await t
.expect(inspect_path_input_err.innerText)
.eql("This field is required");
//Enter form values
await t.typeText(inspect_volume_input, "/").typeText(inspect_path_input, "/");
//verify post state of Invalid values
await t.expect(inspect_volume_input.value).eql("/");
await t.expect(inspect_path_input.value).eql("/");
await t
.expect(inspect_volume_input_err.innerText)
.eql("Volume/Bucket name cannot start with /");
await t
.expect(inspect_path_input_err.innerText)
.eql("Path cannot start with /");
await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).eql(true);
await t.expect(inspect_form_clear_btn.hasAttribute("disabled")).eql(false);
//Important. Testcafe's way to clear input values.
await t.selectText(inspect_volume_input).pressKey("delete");
await t.selectText(inspect_path_input).pressKey("delete");
//Enter Valid form values
await t
.typeText(inspect_volume_input, volumeValue)
.typeText(inspect_path_input, pathValue);
//verify post state of valid values
await t.expect(inspect_volume_input.value).eql(volumeValue);
await t.expect(inspect_path_input.value).eql(pathValue);
await t.expect(inspect_volume_input_err.exists).notOk();
await t.expect(inspect_path_input_err.exists).notOk();
await t.click(inspect_form_clear_btn);
//reset state verification
await t.expect(inspect_volume_input.value).eql("");
await t.expect(inspect_path_input.value).eql("");
await t.expect(inspect_form_submit_btn.hasAttribute("disabled")).eql(true);
});
/** End Allowed Policy Test **/
/** Begin Not Allowed Policy Test **/
export const inspectNotAllowedRole = Role(
loginUrl,
async (t) => {
await t
.typeText("#accessKey", insNotAllowedAccKey)
.typeText("#secretKey", insNotAllowedSeckey)
.click(loginSubmitBtn);
},
{ preserveUrl: true }
);
fixture("For user with Denied Inspect permissions")
.page(testDomainUrl)
.beforeEach(async (t) => {
await t.useRole(inspectNotAllowedRole);
});
test("Inspect page can NOT be opened", async (t) => {
try {
await t.navigateTo(inspectScreenUrl);
} catch (e) {
await t.expect(e).ok();
}
});
test("Inspect link should NOT exists in Menu list", async (t) => {
await t
.expect(supportSidebarEl.exists)
.ok()
.click(supportSidebarEl)
.expect(inspectEl.exists)
.notOk(
"Inspect Link should not exist in the menu list as per inspect not allowed policy"
);
});
/** End Not Allowed Policy Test **/

View File

@@ -0,0 +1,22 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"admin:*"
],
"Effect": "Allow",
"Sid": "Allow_Admin_Actions"
},
{
"Action": [
"s3:*"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::*"
],
"Sid": "Allow_S3_Actions"
}
]
}

View File

@@ -0,0 +1,22 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"admin:*"
],
"Effect": "Deny",
"Sid": "Deny_Admin_Actions"
},
{
"Action": [
"s3:*"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::*"
],
"Sid": "Allow_S3_Actions"
}
]
}

View File

@@ -23,6 +23,8 @@ remove_users() {
mc admin user remove minio trace-$TIMESTAMP
mc admin user remove minio users-$TIMESTAMP
mc admin user remove minio watch-$TIMESTAMP
mc admin user remove minio inspect-allowed-$TIMESTAMP
mc admin user remove minio inspect-not-allowed-$TIMESTAMP
}
remove_policies() {
@@ -41,6 +43,8 @@ remove_policies() {
mc admin policy remove minio trace-$TIMESTAMP
mc admin policy remove minio users-$TIMESTAMP
mc admin policy remove minio watch-$TIMESTAMP
mc admin policy remove minio inspect-allowed-$TIMESTAMP
mc admin policy remove minio inspect-not-allowed-$TIMESTAMP
}
__init__() {

View File

@@ -40,6 +40,8 @@ create_policies() {
mc admin policy add minio users-$TIMESTAMP portal-ui/tests/policies/users.json
mc admin policy add minio watch-$TIMESTAMP portal-ui/tests/policies/watch.json
mc admin policy add minio bucketwriteprefixonlypolicy-$TIMESTAMP portal-ui/tests/policies/bucketWritePrefixOnlyPolicy.json
mc admin policy add minio inspect-allowed-$TIMESTAMP portal-ui/tests/policies/inspect-allowed.json
mc admin policy add minio inspect-not-allowed-$TIMESTAMP portal-ui/tests/policies/inspect-not-allowed.json
}
create_users() {
@@ -60,6 +62,8 @@ create_users() {
mc admin user add minio users-$TIMESTAMP users1234
mc admin user add minio watch-$TIMESTAMP watch1234
mc admin user add minio bucketwriteprefixonlypolicy-$TIMESTAMP bucketwriteprefixonlypolicy
mc admin user add minio inspect-allowed-$TIMESTAMP insallowed1234
mc admin user add minio inspect-not-allowed-$TIMESTAMP insnotallowed1234
}
create_buckets() {
@@ -84,4 +88,6 @@ assign_policies() {
mc admin policy set minio users-$TIMESTAMP user=users-$TIMESTAMP
mc admin policy set minio watch-$TIMESTAMP user=watch-$TIMESTAMP
mc admin policy set minio bucketwriteprefixonlypolicy-$TIMESTAMP user=bucketwriteprefixonlypolicy-$TIMESTAMP
mc admin policy set minio inspect-allowed-$TIMESTAMP user=inspect-allowed-$TIMESTAMP
mc admin policy set minio inspect-not-allowed-$TIMESTAMP user=inspect-not-allowed-$TIMESTAMP
}

View File

@@ -32,6 +32,8 @@ remove_users() {
mc admin user remove minio users-$TIMESTAMP
mc admin user remove minio watch-$TIMESTAMP
mc admin user remove minio bucketwriteprefixonlypolicy-$TIMESTAMP
mc admin user remove minio inspect-allowed-$TIMESTAMP
mc admin user remove minio inspect-not-allowed-$TIMESTAMP
}
remove_policies() {
@@ -51,6 +53,8 @@ remove_policies() {
mc admin policy remove minio users-$TIMESTAMP
mc admin policy remove minio watch-$TIMESTAMP
mc admin policy remove minio bucketwriteprefixonlypolicy-$TIMESTAMP
mc admin policy remove minio inspect-allowed-$TIMESTAMP
mc admin policy remove minio inspect-not-allowed-$TIMESTAMP
}
remove_buckets() {