diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts
index a5d302618..228f22ad9 100644
--- a/portal-ui/src/common/SecureComponent/permissions.ts
+++ b/portal-ui/src/common/SecureComponent/permissions.ts
@@ -435,13 +435,13 @@ export const CONSOLE_UI_RESOURCE = "console-ui";
export const permissionTooltipHelper = (scopes: string[], name: string) => {
return (
- "You require additional permissions in order to enable " +
+ "You require additional permissions in order to " +
name +
". Please ask your MinIO administrator to grant you " +
scopes +
" permission" +
(scopes.length > 1 ? "s" : "") +
- " in order to enable " +
+ " in order to " +
name +
"."
);
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ActionsListSection.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ActionsListSection.tsx
index 3d379119b..694608694 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ActionsListSection.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ActionsListSection.tsx
@@ -19,6 +19,7 @@ import ObjectActionButton from "./ObjectActionButton";
import { withStyles } from "@mui/styles";
import createStyles from "@mui/styles/createStyles";
import { detailsPanel } from "../../../../Common/FormComponents/common/styleLibrary";
+import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper";
const styles = () =>
createStyles({
@@ -52,12 +53,14 @@ const ActionsListSection = ({
{items.map((actionItem, index) => {
return (
-
+
+
+
);
})}
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
index f919ecb19..ea6b9a8cc 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
@@ -71,7 +71,10 @@ import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
import { AppState, useAppDispatch } from "../../../../../../store";
import PageLayout from "../../../../Common/Layout/PageLayout";
-import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
+import {
+ IAM_SCOPES,
+ permissionTooltipHelper,
+} from "../../../../../../common/SecureComponent/permissions";
import {
hasPermission,
SecureComponent,
@@ -1055,11 +1058,23 @@ const ListObjects = () => {
const onDrop = useCallback(
(acceptedFiles: any[]) => {
- if (acceptedFiles && acceptedFiles.length > 0) {
+ if (acceptedFiles && acceptedFiles.length > 0 && canUpload) {
let newFolderPath: string = acceptedFiles[0].path;
uploadObject(acceptedFiles, newFolderPath);
}
+ if (!canUpload) {
+ dispatch(
+ setErrorSnackMessage({
+ errorMessage: "Upload not allowed",
+ detailedError: permissionTooltipHelper(
+ [IAM_SCOPES.S3_PUT_OBJECT],
+ "upload objects to this location"
+ ),
+ })
+ );
+ }
},
+ // eslint-disable-next-line react-hooks/exhaustive-deps
[uploadObject]
);
@@ -1221,6 +1236,10 @@ const ListObjects = () => {
uploadPath = uploadPath.concat(currentPath);
}
+ const canDownload = hasPermission(bucketName, [IAM_SCOPES.S3_GET_OBJECT]);
+ const canDelete = hasPermission(bucketName, [IAM_SCOPES.S3_DELETE_OBJECT]);
+ const canUpload = hasPermission(uploadPath, [IAM_SCOPES.S3_PUT_OBJECT]);
+
const onClosePanel = (forceRefresh: boolean) => {
dispatch(setSelectedObjectView(null));
dispatch(setVersionsModeEnabled({ status: false }));
@@ -1272,23 +1291,28 @@ const ListObjects = () => {
{
action: downloadSelected,
label: "Download",
- disabled: selectedObjects.length === 0,
+ disabled: !canDownload || selectedObjects.length === 0,
icon: ,
- tooltip: "Download Selected",
+ tooltip: canDownload
+ ? "Download Selected"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_GET_OBJECT],
+ "download objects from this bucket"
+ ),
},
{
action: openShare,
label: "Share",
disabled: selectedObjects.length !== 1 || !canShareFile,
icon: ,
- tooltip: "Share Selected File",
+ tooltip: canShareFile ? "Share Selected File" : "Sharing unavailable",
},
{
action: openPreview,
label: "Preview",
disabled: selectedObjects.length !== 1 || !canPreviewFile,
icon: ,
- tooltip: "Preview Selected File",
+ tooltip: canPreviewFile ? "Preview Selected File" : "Preview unavailable",
},
{
action: () => {
@@ -1297,10 +1321,13 @@ const ListObjects = () => {
label: "Delete",
icon: ,
disabled:
- !hasPermission(bucketName, [IAM_SCOPES.S3_DELETE_OBJECT]) ||
- selectedObjects.length === 0 ||
- !displayDeleteObject,
- tooltip: "Delete Selected Files",
+ !canDelete || selectedObjects.length === 0 || !displayDeleteObject,
+ tooltip: canDelete
+ ? "Delete Selected Files"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_DELETE_OBJECT],
+ "delete objects in this bucket"
+ ),
},
];
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
index 5097f30f8..74ab25b39 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
@@ -41,7 +41,10 @@ import {
niceBytesInt,
niceDaysInt,
} from "../../../../../../common/utils";
-import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
+import {
+ IAM_SCOPES,
+ permissionTooltipHelper,
+} from "../../../../../../common/SecureComponent/permissions";
import { AppState, useAppDispatch } from "../../../../../../store";
import {
@@ -90,6 +93,7 @@ import {
updateProgress,
} from "../../../../ObjectBrowser/objectBrowserSlice";
import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
+import TooltipWrapper from "../../../../Common/TooltipWrapper/TooltipWrapper";
const styles = () =>
createStyles({
@@ -405,6 +409,33 @@ const ObjectDetailPanel = ({
currentItem,
[bucketName, actualInfo.name].join("/"),
];
+ const canSetLegalHold = hasPermission(bucketName, [
+ IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD,
+ ]);
+ const canSetTags = hasPermission(objectResources, [
+ IAM_SCOPES.S3_PUT_OBJECT_TAGGING,
+ ]);
+
+ const canChangeRetention = hasPermission(
+ objectResources,
+ [IAM_SCOPES.S3_GET_OBJECT_RETENTION, IAM_SCOPES.S3_PUT_OBJECT_RETENTION],
+ true
+ );
+ const canInspect = hasPermission(objectResources, [
+ IAM_SCOPES.ADMIN_INSPECT_DATA,
+ ]);
+ const canChangeVersioning = hasPermission(objectResources, [
+ IAM_SCOPES.S3_GET_BUCKET_VERSIONING,
+ IAM_SCOPES.S3_PUT_BUCKET_VERSIONING,
+ IAM_SCOPES.S3_GET_OBJECT_VERSION,
+ ]);
+ const canGetObject = hasPermission(objectResources, [
+ IAM_SCOPES.S3_GET_OBJECT,
+ ]);
+ const canDelete = hasPermission(
+ [bucketName, currentItem, [bucketName, actualInfo.name].join("/")],
+ [IAM_SCOPES.S3_DELETE_OBJECT]
+ );
const multiActionButtons = [
{
@@ -412,22 +443,28 @@ const ObjectDetailPanel = ({
downloadObject(actualInfo);
},
label: "Download",
- disabled:
- !!actualInfo.is_delete_marker ||
- !hasPermission(objectResources, [IAM_SCOPES.S3_GET_OBJECT]),
+ disabled: !!actualInfo.is_delete_marker || !canGetObject,
icon: ,
- tooltip: "Download this Object",
+ tooltip: canGetObject
+ ? "Download this Object"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_GET_OBJECT],
+ "download this object"
+ ),
},
{
action: () => {
shareObject();
},
label: "Share",
- disabled:
- !!actualInfo.is_delete_marker ||
- !hasPermission(objectResources, [IAM_SCOPES.S3_GET_OBJECT]),
+ disabled: !!actualInfo.is_delete_marker || !canGetObject,
icon: ,
- tooltip: "Share this File",
+ tooltip: canGetObject
+ ? "Share this File"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_GET_OBJECT],
+ "share this object"
+ ),
},
{
action: () => {
@@ -437,9 +474,14 @@ const ObjectDetailPanel = ({
disabled:
!!actualInfo.is_delete_marker ||
extensionPreview(currentItem) === "none" ||
- !hasPermission(objectResources, [IAM_SCOPES.S3_GET_OBJECT]),
+ !canGetObject,
icon: ,
- tooltip: "Preview this File",
+ tooltip: canGetObject
+ ? "Preview this File"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_GET_OBJECT],
+ "preview this object"
+ ),
},
{
action: () => {
@@ -450,10 +492,17 @@ const ObjectDetailPanel = ({
!locking ||
!distributedSetup ||
!!actualInfo.is_delete_marker ||
- !hasPermission(bucketName, [IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD]) ||
+ !canSetLegalHold ||
selectedVersion !== "",
icon: ,
- tooltip: "Change Legal Hold rules for this File",
+ tooltip: canSetLegalHold
+ ? locking
+ ? "Change Legal Hold rules for this File"
+ : "Object Locking must be enabled on this bucket in order to set Legal Hold"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD],
+ "change legal hold settings for this object"
+ ),
},
{
action: openRetentionModal,
@@ -461,10 +510,21 @@ const ObjectDetailPanel = ({
disabled:
!distributedSetup ||
!!actualInfo.is_delete_marker ||
- !hasPermission(objectResources, [IAM_SCOPES.S3_GET_OBJECT_RETENTION]) ||
- selectedVersion !== "",
+ !canChangeRetention ||
+ selectedVersion !== "" ||
+ !locking,
icon: ,
- tooltip: "Change Retention rules for this File",
+ tooltip: canChangeRetention
+ ? locking
+ ? "Change Retention rules for this File"
+ : "Object Locking must be enabled on this bucket in order to set Retention Rules"
+ : permissionTooltipHelper(
+ [
+ IAM_SCOPES.S3_GET_OBJECT_RETENTION,
+ IAM_SCOPES.S3_PUT_OBJECT_RETENTION,
+ ],
+ "change Retention Rules for this object"
+ ),
},
{
action: () => {
@@ -472,11 +532,17 @@ const ObjectDetailPanel = ({
},
label: "Tags",
disabled:
- !!actualInfo.is_delete_marker ||
- selectedVersion !== "" ||
- !hasPermission(objectResources, [IAM_SCOPES.S3_PUT_OBJECT_TAGGING]),
+ !!actualInfo.is_delete_marker || selectedVersion !== "" || !canSetTags,
icon: ,
- tooltip: "Change Tags for this File",
+ tooltip: canSetTags
+ ? "Change Tags for this File"
+ : permissionTooltipHelper(
+ [
+ IAM_SCOPES.S3_PUT_OBJECT_TAGGING,
+ IAM_SCOPES.S3_GET_OBJECT_TAGGING,
+ ],
+ "set Tags on this object"
+ ),
},
{
action: () => {
@@ -487,9 +553,14 @@ const ObjectDetailPanel = ({
!distributedSetup ||
!!actualInfo.is_delete_marker ||
selectedVersion !== "" ||
- !hasPermission(objectResources, [IAM_SCOPES.ADMIN_INSPECT_DATA]),
+ !canInspect,
icon: ,
- tooltip: "Inspect this file",
+ tooltip: canInspect
+ ? "Inspect this file"
+ : permissionTooltipHelper(
+ [IAM_SCOPES.ADMIN_INSPECT_DATA],
+ "inspect this file"
+ ),
},
{
action: () => {
@@ -505,12 +576,19 @@ const ObjectDetailPanel = ({
disabled:
!distributedSetup ||
!(actualInfo.version_id && actualInfo.version_id !== "null") ||
- !hasPermission(objectResources, [
- IAM_SCOPES.S3_GET_BUCKET_VERSIONING,
- IAM_SCOPES.S3_PUT_BUCKET_VERSIONING,
- IAM_SCOPES.S3_GET_OBJECT_VERSION,
- ]),
- tooltip: "Display Versions for this file",
+ !canChangeVersioning,
+ tooltip: canChangeVersioning
+ ? actualInfo.version_id && actualInfo.version_id !== "null"
+ ? "Display Versions for this file"
+ : ""
+ : permissionTooltipHelper(
+ [
+ IAM_SCOPES.S3_GET_BUCKET_VERSIONING,
+ IAM_SCOPES.S3_PUT_BUCKET_VERSIONING,
+ IAM_SCOPES.S3_GET_OBJECT_VERSION,
+ ],
+ "display all versions of this object"
+ ),
},
];
@@ -621,35 +699,51 @@ const ObjectDetailPanel = ({
}
items={multiActionButtons}
/>
-
-
-
+
- }
- iconLocation={"start"}
- fullWidth
- variant={"secondary"}
- onClick={() => {
- setDeleteOpen(true);
- }}
- disabled={selectedVersion === "" && actualInfo.is_delete_marker}
- style={{
- width: "calc(100% - 44px)",
- margin: "8px 0",
- }}
- label={`Delete${selectedVersion !== "" ? " version" : ""}`}
- />
-
-
+
+ }
+ iconLocation={"start"}
+ fullWidth
+ variant={"secondary"}
+ onClick={() => {
+ setDeleteOpen(true);
+ }}
+ disabled={
+ selectedVersion === "" && actualInfo.is_delete_marker
+ }
+ style={{
+ width: "calc(100% - 44px)",
+ margin: "8px 0",
+ }}
+ label={`Delete${selectedVersion !== "" ? " version" : ""}`}
+ />
+
+
+
Object Info
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/UploadFilesButton.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/UploadFilesButton.tsx
index 7199b559f..580fe2e84 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/UploadFilesButton.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/UploadFilesButton.tsx
@@ -22,7 +22,10 @@ import withStyles from "@mui/styles/withStyles";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import { UploadFolderIcon, UploadIcon } from "../../../../icons";
-import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
+import {
+ IAM_SCOPES,
+ permissionTooltipHelper,
+} from "../../../../common/SecureComponent/permissions";
import { hasPermission } from "../../../../common/SecureComponent";
import { Button } from "mds";
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
@@ -78,7 +81,16 @@ const UploadFilesButton = ({
return (
-
+
{!hidePathButton && (
-
+
+
)}
{additionalOptions}