Re-implement policy handling in react (#1234)
Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -40,18 +40,12 @@ type Bucket struct {
|
||||
// access
|
||||
Access *BucketAccess `json:"access,omitempty"`
|
||||
|
||||
// allowed actions
|
||||
AllowedActions []string `json:"allowedActions"`
|
||||
|
||||
// creation date
|
||||
CreationDate string `json:"creation_date,omitempty"`
|
||||
|
||||
// details
|
||||
Details *BucketDetails `json:"details,omitempty"`
|
||||
|
||||
// manage
|
||||
Manage bool `json:"manage,omitempty"`
|
||||
|
||||
// name
|
||||
// Required: true
|
||||
// Min Length: 3
|
||||
|
||||
@@ -49,8 +49,8 @@ type SessionResponse struct {
|
||||
// pages
|
||||
Pages []string `json:"pages"`
|
||||
|
||||
// policy
|
||||
Policy *IamPolicy `json:"policy,omitempty"`
|
||||
// permissions
|
||||
Permissions map[string][]string `json:"permissions,omitempty"`
|
||||
|
||||
// status
|
||||
// Enum: [ok]
|
||||
@@ -61,10 +61,6 @@ type SessionResponse struct {
|
||||
func (m *SessionResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validatePolicy(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateStatus(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -75,23 +71,6 @@ func (m *SessionResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SessionResponse) validatePolicy(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Policy) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Policy != nil {
|
||||
if err := m.Policy.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("policy")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var sessionResponseTypeStatusPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
@@ -131,31 +110,8 @@ func (m *SessionResponse) validateStatus(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this session response based on the context it is used
|
||||
// ContextValidate validates this session response based on context it is used
|
||||
func (m *SessionResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidatePolicy(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SessionResponse) contextValidatePolicy(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Policy != nil {
|
||||
if err := m.Policy.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("policy")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// This file is part of MinIO Orchestrator
|
||||
// Copyright (c) 2021 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/>.
|
||||
|
||||
package acl
|
||||
|
||||
import iampolicy "github.com/minio/pkg/iam/policy"
|
||||
|
||||
var BucketViewerRole = iampolicy.NewActionSet(
|
||||
iampolicy.ListBucketAction,
|
||||
iampolicy.GetObjectAction,
|
||||
)
|
||||
|
||||
var BucketEditorRole = iampolicy.NewActionSet(
|
||||
iampolicy.ListBucketAction,
|
||||
iampolicy.GetObjectAction,
|
||||
iampolicy.DeleteObjectAction,
|
||||
iampolicy.PutObjectAction,
|
||||
)
|
||||
var BucketAdminRole = iampolicy.NewActionSet(
|
||||
iampolicy.AbortMultipartUploadAction,
|
||||
iampolicy.CreateBucketAction,
|
||||
iampolicy.DeleteBucketAction,
|
||||
iampolicy.ForceDeleteBucketAction,
|
||||
iampolicy.DeleteBucketPolicyAction,
|
||||
iampolicy.GetBucketLocationAction,
|
||||
iampolicy.GetBucketNotificationAction,
|
||||
iampolicy.GetBucketPolicyAction,
|
||||
iampolicy.HeadBucketAction,
|
||||
iampolicy.ListAllMyBucketsAction,
|
||||
iampolicy.GetBucketPolicyStatusAction,
|
||||
iampolicy.ListBucketVersionsAction,
|
||||
iampolicy.ListBucketMultipartUploadsAction,
|
||||
iampolicy.ListenNotificationAction,
|
||||
iampolicy.ListenBucketNotificationAction,
|
||||
iampolicy.ListMultipartUploadPartsAction,
|
||||
iampolicy.PutBucketLifecycleAction,
|
||||
iampolicy.GetBucketLifecycleAction,
|
||||
iampolicy.PutBucketNotificationAction,
|
||||
iampolicy.PutBucketPolicyAction,
|
||||
iampolicy.BypassGovernanceRetentionAction,
|
||||
iampolicy.PutObjectRetentionAction,
|
||||
iampolicy.GetObjectRetentionAction,
|
||||
iampolicy.GetObjectLegalHoldAction,
|
||||
iampolicy.PutObjectLegalHoldAction,
|
||||
iampolicy.GetBucketObjectLockConfigurationAction,
|
||||
iampolicy.PutBucketObjectLockConfigurationAction,
|
||||
iampolicy.GetBucketTaggingAction,
|
||||
iampolicy.PutBucketTaggingAction,
|
||||
iampolicy.GetObjectVersionAction,
|
||||
iampolicy.GetObjectVersionTaggingAction,
|
||||
iampolicy.DeleteObjectVersionAction,
|
||||
iampolicy.DeleteObjectVersionTaggingAction,
|
||||
iampolicy.PutObjectVersionTaggingAction,
|
||||
iampolicy.GetObjectTaggingAction,
|
||||
iampolicy.PutObjectTaggingAction,
|
||||
iampolicy.DeleteObjectTaggingAction,
|
||||
iampolicy.PutBucketEncryptionAction,
|
||||
iampolicy.GetBucketEncryptionAction,
|
||||
iampolicy.PutBucketVersioningAction,
|
||||
iampolicy.GetBucketVersioningAction,
|
||||
iampolicy.GetReplicationConfigurationAction,
|
||||
iampolicy.PutReplicationConfigurationAction,
|
||||
iampolicy.ReplicateObjectAction,
|
||||
iampolicy.ReplicateDeleteAction,
|
||||
iampolicy.ReplicateTagsAction,
|
||||
iampolicy.GetObjectVersionForReplicationAction,
|
||||
iampolicy.AllActions,
|
||||
iampolicy.AllAdminActions,
|
||||
)
|
||||
69
portal-ui/src/common/SecureComponent/SecureComponent.tsx
Normal file
69
portal-ui/src/common/SecureComponent/SecureComponent.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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, { cloneElement } from "react";
|
||||
import { store } from "../../store";
|
||||
import { hasAccessToResource } from "./permissions";
|
||||
|
||||
export const hasPermission = (
|
||||
resource: string | undefined,
|
||||
scopes: string[],
|
||||
matchAll?: boolean
|
||||
) => {
|
||||
if (!resource) {
|
||||
return false;
|
||||
}
|
||||
const state = store.getState();
|
||||
const sessionGrants = state.console.session.permissions;
|
||||
const resourceGrants =
|
||||
sessionGrants[resource] ||
|
||||
sessionGrants[`arn:aws:s3:::${resource}/*`] ||
|
||||
[];
|
||||
const globalGrants = sessionGrants["arn:aws:s3:::*"] || [];
|
||||
return hasAccessToResource(
|
||||
[...resourceGrants, ...globalGrants],
|
||||
scopes,
|
||||
matchAll
|
||||
);
|
||||
};
|
||||
|
||||
interface ISecureComponentProps {
|
||||
errorProps?: any;
|
||||
RenderError?: any;
|
||||
matchAll?: boolean;
|
||||
children: any;
|
||||
scopes: string[];
|
||||
resource: string;
|
||||
}
|
||||
|
||||
const SecureComponent = ({
|
||||
children,
|
||||
RenderError = () => <></>,
|
||||
errorProps = null,
|
||||
matchAll = false,
|
||||
scopes = [],
|
||||
resource,
|
||||
}: ISecureComponentProps) => {
|
||||
const permissionGranted = hasPermission(resource, scopes, matchAll);
|
||||
if (!permissionGranted && !errorProps) return <RenderError />;
|
||||
if (!permissionGranted && errorProps) {
|
||||
return cloneElement(children, { ...errorProps });
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default SecureComponent;
|
||||
177
portal-ui/src/common/SecureComponent/permissions.ts
Normal file
177
portal-ui/src/common/SecureComponent/permissions.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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/>.
|
||||
|
||||
// hasAccessToResource receives a list of user permissions to perform on a specific resource, then compares those permissions against
|
||||
// a list of required permissions and return true or false depending of the level of required access (match all permissions,
|
||||
// match some of the permissions)
|
||||
export const hasAccessToResource = (
|
||||
userPermissionsOnBucket: string[] | null | undefined,
|
||||
requiredPermissions: string[],
|
||||
matchAll?: boolean
|
||||
) => {
|
||||
if (!userPermissionsOnBucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const s3All = userPermissionsOnBucket.includes(IAM_SCOPES.S3_ALL_ACTIONS);
|
||||
const AdminAll = userPermissionsOnBucket.includes(
|
||||
IAM_SCOPES.ADMIN_ALL_ACTIONS
|
||||
);
|
||||
|
||||
const permissions = requiredPermissions.filter(function (n) {
|
||||
return (
|
||||
userPermissionsOnBucket.indexOf(n) !== -1 ||
|
||||
(n.indexOf("s3:") !== -1 && s3All) ||
|
||||
(n.indexOf("admin:") !== -1 && AdminAll)
|
||||
);
|
||||
});
|
||||
return matchAll
|
||||
? permissions.length === requiredPermissions.length
|
||||
: permissions.length > 0;
|
||||
};
|
||||
|
||||
export const IAM_ROLES = {
|
||||
viewer: "VIEWER",
|
||||
editor: "EDITOR",
|
||||
owner: "OWNER",
|
||||
admin: "ADMIN",
|
||||
};
|
||||
|
||||
export const IAM_SCOPES = {
|
||||
S3_LIST_BUCKET: "s3:ListBucket",
|
||||
S3_GET_BUCKET_POLICY: "s3:GetBucketPolicy",
|
||||
S3_PUT_BUCKET_POLICY: "s3:PutBucketPolicy",
|
||||
S3_GET_OBJECT: "s3:GetObject",
|
||||
S3_PUT_OBJECT: "s3:PutObject",
|
||||
S3_GET_OBJECT_LEGAL_HOLD: "s3:GetObjectLegalHold",
|
||||
S3_PUT_OBJECT_LEGAL_HOLD: "s3:PutObjectLegalHold",
|
||||
S3_DELETE_OBJECT: "s3:DeleteObject",
|
||||
S3_GET_BUCKET_VERSIONING: "s3:GetBucketVersioning",
|
||||
S3_PUT_BUCKET_VERSIONING: "s3:PutBucketVersioning",
|
||||
S3_GET_OBJECT_RETENTION: "s3:GetObjectRetention",
|
||||
S3_PUT_OBJECT_RETENTION: "s3:PutObjectRetention",
|
||||
S3_GET_OBJECT_TAGGING: "s3:GetObjectTagging",
|
||||
S3_PUT_OBJECT_TAGGING: "s3:PutObjectTagging",
|
||||
S3_DELETE_OBJECT_TAGGING: "s3:DeleteObjectTagging",
|
||||
S3_GET_BUCKET_ENCRYPTION_CONFIGURATION: "s3:GetEncryptionConfiguration",
|
||||
S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION: "s3:PutEncryptionConfiguration",
|
||||
S3_CREATE_BUCKET: "s3:CreateBucket",
|
||||
S3_DELETE_BUCKET: "s3:DeleteBucket",
|
||||
S3_FORCE_DELETE_BUCKET: "s3:ForceDeleteBucket",
|
||||
S3_GET_BUCKET_NOTIFICATIONS: "s3:GetBucketNotification",
|
||||
S3_LISTEN_BUCKET_NOTIFICATIONS: "s3:ListenBucketNotification",
|
||||
S3_PUT_BUCKET_NOTIFICATIONS: "s3:PutBucketNotification",
|
||||
S3_GET_REPLICATION_CONFIGURATION: "s3:GetReplicationConfiguration",
|
||||
S3_PUT_REPLICATION_CONFIGURATION: "s3:PutReplicationConfiguration",
|
||||
S3_GET_LIFECYCLE_CONFIGURATION: "s3:GetLifecycleConfiguration",
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION: "s3:PutLifecycleConfiguration",
|
||||
S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION:
|
||||
"s3:GetBucketObjectLockConfiguration",
|
||||
S3_PUT_BUCKET_OBJECT_LOCK_CONFIGURATION:
|
||||
"s3:PutBucketObjectLockConfiguration",
|
||||
ADMIN_GET_POLICY: "admin:GetPolicy",
|
||||
ADMIN_LIST_USERS: "admin:ListUsers",
|
||||
ADMIN_LIST_USER_POLICIES: "admin:ListUserPolicies",
|
||||
ADMIN_SERVER_INFO: "admin:ServerInfo",
|
||||
ADMIN_GET_BUCKET_QUOTA: "admin:GetBucketQuota",
|
||||
ADMIN_SET_BUCKET_QUOTA: "admin:SetBucketQuota",
|
||||
ADMIN_LIST_TIERS: "admin:ListTier",
|
||||
ADMIN_LIST_GROUPS: "admin:ListGroups",
|
||||
S3_GET_OBJECT_VERSION_FOR_REPLICATION: "s3:GetObjectVersionForReplication",
|
||||
S3_REPLICATE_TAGS: "s3:ReplicateTags",
|
||||
S3_REPLICATE_DELETE: "s3:ReplicateDelete",
|
||||
S3_REPLICATE_OBJECT: "s3:ReplicateObject",
|
||||
S3_PUT_OBJECT_VERSION_TAGGING: "s3:PutObjectVersionTagging",
|
||||
S3_DELETE_OBJECT_VERSION_TAGGING: "s3:DeleteObjectVersionTagging",
|
||||
S3_DELETE_OBJECT_VERSION: "s3:DeleteObjectVersion",
|
||||
S3_GET_OBJECT_VERSION_TAGGING: "s3:GetObjectVersionTagging",
|
||||
S3_GET_OBJECT_VERSION: "s3:GetObjectVersion",
|
||||
S3_PUT_BUCKET_TAGGING: "s3:PutBucketTagging",
|
||||
S3_GET_BUCKET_TAGGING: "s3:GetBucketTagging",
|
||||
S3_BYPASS_GOVERNANCE_RETENTION: "s3:BypassGovernanceRetention",
|
||||
S3_LIST_MULTIPART_UPLOAD_PARTS: "s3:ListMultipartUploadParts",
|
||||
S3_LISTEN_NOTIFICATIONS: "s3:ListenNotification",
|
||||
S3_LIST_BUCKET_MULTIPART_UPLOADS: "s3:ListBucketMultipartUploads",
|
||||
S3_LIST_BUCKET_VERSIONS: "s3:ListBucketVersions",
|
||||
S3_GET_BUCKET_POLICY_STATUS: "s3:GetBucketPolicyStatus",
|
||||
S3_LIST_ALL_MY_BUCKETS: "s3:ListAllMyBuckets",
|
||||
S3_HEAD_BUCKET: "s3:HeadBucket",
|
||||
S3_GET_BUCKET_LOCATION: "s3:GetBucketLocation",
|
||||
S3_DELETE_BUCKET_POLICY: "s3:DeleteBucketPolicy",
|
||||
S3_ABORT_MULTIPART_UPLOAD: "s3:AbortMultipartUpload",
|
||||
S3_ALL_ACTIONS: "s3:*",
|
||||
ADMIN_ALL_ACTIONS: "admin:*",
|
||||
};
|
||||
|
||||
export const IAM_PERMISSIONS = {
|
||||
[IAM_ROLES.admin]: [
|
||||
IAM_SCOPES.S3_ALL_ACTIONS,
|
||||
IAM_SCOPES.ADMIN_ALL_ACTIONS,
|
||||
IAM_SCOPES.S3_REPLICATE_OBJECT,
|
||||
IAM_SCOPES.S3_REPLICATE_DELETE,
|
||||
IAM_SCOPES.S3_REPLICATE_TAGS,
|
||||
IAM_SCOPES.S3_GET_OBJECT_VERSION_FOR_REPLICATION,
|
||||
IAM_SCOPES.S3_PUT_REPLICATION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_BUCKET_VERSIONING,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_VERSIONING,
|
||||
IAM_SCOPES.S3_GET_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_DELETE_OBJECT_TAGGING,
|
||||
IAM_SCOPES.S3_PUT_OBJECT_TAGGING,
|
||||
IAM_SCOPES.S3_GET_OBJECT_TAGGING,
|
||||
IAM_SCOPES.S3_PUT_OBJECT_VERSION_TAGGING,
|
||||
IAM_SCOPES.S3_DELETE_OBJECT_VERSION_TAGGING,
|
||||
IAM_SCOPES.S3_DELETE_OBJECT_VERSION,
|
||||
IAM_SCOPES.S3_GET_OBJECT_VERSION_TAGGING,
|
||||
IAM_SCOPES.S3_GET_OBJECT_VERSION,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_TAGGING,
|
||||
IAM_SCOPES.S3_GET_BUCKET_TAGGING,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_OBJECT_LOCK_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD,
|
||||
IAM_SCOPES.S3_GET_OBJECT_LEGAL_HOLD,
|
||||
IAM_SCOPES.S3_GET_OBJECT_RETENTION,
|
||||
IAM_SCOPES.S3_PUT_OBJECT_RETENTION,
|
||||
IAM_SCOPES.S3_BYPASS_GOVERNANCE_RETENTION,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_POLICY,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_LIST_MULTIPART_UPLOAD_PARTS,
|
||||
IAM_SCOPES.S3_LISTEN_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_LISTEN_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_LIST_BUCKET_MULTIPART_UPLOADS,
|
||||
IAM_SCOPES.S3_LIST_BUCKET_VERSIONS,
|
||||
IAM_SCOPES.S3_GET_BUCKET_POLICY_STATUS,
|
||||
IAM_SCOPES.S3_LIST_ALL_MY_BUCKETS,
|
||||
IAM_SCOPES.S3_HEAD_BUCKET,
|
||||
IAM_SCOPES.S3_GET_BUCKET_POLICY,
|
||||
IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_GET_BUCKET_LOCATION,
|
||||
IAM_SCOPES.S3_DELETE_BUCKET_POLICY,
|
||||
IAM_SCOPES.S3_FORCE_DELETE_BUCKET,
|
||||
IAM_SCOPES.S3_DELETE_BUCKET,
|
||||
IAM_SCOPES.S3_CREATE_BUCKET,
|
||||
IAM_SCOPES.S3_ABORT_MULTIPART_UPLOAD,
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
],
|
||||
};
|
||||
|
||||
export const S3_ALL_RESOURCES = "arn:aws:s3:::*";
|
||||
export const CONSOLE_UI_RESOURCE = "console-ui";
|
||||
@@ -30,14 +30,11 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import api from "../../../../common/api";
|
||||
import history from "../../../../history";
|
||||
import { BucketInfo } from "../types";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import {
|
||||
ADMIN_GET_POLICY,
|
||||
ADMIN_LIST_GROUPS,
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
ADMIN_LIST_USERS,
|
||||
} from "../../../../types";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
session: state.console.session,
|
||||
@@ -79,13 +76,17 @@ const AccessDetails = ({
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
const displayPoliciesList = displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
const displayPoliciesList = hasPermission(bucketName, [
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
]);
|
||||
|
||||
const displayUsersList = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[ADMIN_GET_POLICY, ADMIN_LIST_USERS, ADMIN_LIST_GROUPS],
|
||||
const displayUsersList = hasPermission(
|
||||
bucketName,
|
||||
[
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
@@ -135,7 +136,6 @@ const AccessDetails = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingPolicies) {
|
||||
console.log("displayPoliciesList", displayPoliciesList);
|
||||
if (displayPoliciesList) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/bucket-policy/${bucketName}`)
|
||||
@@ -167,24 +167,37 @@ const AccessDetails = ({
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
>
|
||||
<Tab label="Policies" {...a11yProps(0)} />
|
||||
{displayPoliciesList && <Tab label="Policies" {...a11yProps(0)} />}
|
||||
{displayUsersList && <Tab label="Users" {...a11yProps(1)} />}
|
||||
</Tabs>
|
||||
<Paper>
|
||||
<TabPanel index={0} value={curTab}>
|
||||
<TableWrapper
|
||||
disabled={!displayPoliciesList}
|
||||
noBackground={true}
|
||||
itemActions={PolicyActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingPolicies}
|
||||
records={bucketPolicy}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_LIST_USER_POLICIES]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<TableWrapper
|
||||
noBackground={true}
|
||||
itemActions={PolicyActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingPolicies}
|
||||
records={bucketPolicy}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
</SecureComponent>
|
||||
</TabPanel>
|
||||
{displayUsersList && (
|
||||
<TabPanel index={1} value={curTab}>
|
||||
|
||||
<TabPanel index={1} value={curTab}>
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<TableWrapper
|
||||
noBackground={true}
|
||||
itemActions={userTableActions}
|
||||
@@ -194,8 +207,8 @@ const AccessDetails = ({
|
||||
entityName="Users"
|
||||
idField="accessKey"
|
||||
/>
|
||||
</TabPanel>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</TabPanel>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -38,9 +38,11 @@ import {
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { BucketInfo } from "../types";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import { S3_GET_BUCKET_POLICY, S3_PUT_BUCKET_POLICY } from "../../../../types";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -146,15 +148,17 @@ const AccessRule = ({
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
const displayAccessRules = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_POLICY,
|
||||
const displayAccessRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_POLICY,
|
||||
]);
|
||||
|
||||
const displayAddAccessRules = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_BUCKET_POLICY, S3_PUT_BUCKET_POLICY],
|
||||
true
|
||||
);
|
||||
const deleteAccessRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_DELETE_BUCKET_POLICY,
|
||||
]);
|
||||
|
||||
const editAccessRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_PUT_BUCKET_POLICY,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
@@ -165,6 +169,7 @@ const AccessRule = ({
|
||||
const AccessRuleActions = [
|
||||
{
|
||||
type: "delete",
|
||||
disableButtonFunction: () => !deleteAccessRules,
|
||||
onClick: (accessRule: any) => {
|
||||
setDeleteAccessRuleOpen(true);
|
||||
setAccessRuleToDelete(accessRule.prefix);
|
||||
@@ -172,6 +177,7 @@ const AccessRule = ({
|
||||
},
|
||||
{
|
||||
type: "view",
|
||||
disableButtonFunction: () => !editAccessRules,
|
||||
onClick: (accessRule: any) => {
|
||||
setAccessRuleToEdit(accessRule.prefix);
|
||||
setInitialAccess(accessRule.access);
|
||||
@@ -247,7 +253,14 @@ const AccessRule = ({
|
||||
)}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<PanelTitle>Access Rules</PanelTitle>
|
||||
{displayAddAccessRules && (
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_GET_BUCKET_POLICY,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_POLICY,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -260,22 +273,27 @@ const AccessRule = ({
|
||||
>
|
||||
Add Access Rule
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Paper>
|
||||
<TableWrapper
|
||||
disabled={!displayAccessRules}
|
||||
noBackground={true}
|
||||
itemActions={AccessRuleActions}
|
||||
columns={[
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Access", elementKey: "access" },
|
||||
]}
|
||||
isLoading={loadingAccessRules}
|
||||
records={accessRules}
|
||||
entityName="Access Rules"
|
||||
idField="prefix"
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_POLICY]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
noBackground={true}
|
||||
itemActions={AccessRuleActions}
|
||||
columns={[
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Access", elementKey: "access" },
|
||||
]}
|
||||
isLoading={loadingAccessRules}
|
||||
records={accessRules}
|
||||
entityName="Access Rules"
|
||||
idField="prefix"
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -31,6 +31,11 @@ import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
import { SettingsIcon } from "../../../../icons";
|
||||
import { BucketInfo } from "../types";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import SecureComponent from "../../../../common/SecureComponent/SecureComponent";
|
||||
import {
|
||||
IAM_PERMISSIONS,
|
||||
IAM_ROLES,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
interface IBrowserHandlerProps {
|
||||
fileMode: boolean;
|
||||
@@ -82,21 +87,22 @@ const BrowserHandler = ({
|
||||
</Fragment>
|
||||
}
|
||||
actions={
|
||||
bucketInfo?.manage && (
|
||||
<Fragment>
|
||||
<Tooltip title={"Configure Bucket"}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Configure Bucket"
|
||||
component="span"
|
||||
onClick={openBucketConfiguration}
|
||||
size="large"
|
||||
>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Fragment>
|
||||
)
|
||||
<SecureComponent
|
||||
scopes={IAM_PERMISSIONS[IAM_ROLES.admin]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<Tooltip title={"Configure Bucket"}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Configure Bucket"
|
||||
component="span"
|
||||
onClick={openBucketConfiguration}
|
||||
size="large"
|
||||
>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</SecureComponent>
|
||||
}
|
||||
/>
|
||||
<Grid>{fileMode ? <ObjectDetails /> : <ListObjects />}</Grid>
|
||||
|
||||
@@ -52,24 +52,13 @@ import DeleteBucket from "../ListBuckets/DeleteBucket";
|
||||
import AccessRulePanel from "./AccessRulePanel";
|
||||
import RefreshIcon from "../../../../icons/RefreshIcon";
|
||||
import BoxIconButton from "../../Common/BoxIconButton/BoxIconButton";
|
||||
import {
|
||||
ADMIN_GET_POLICY,
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
ADMIN_LIST_USERS,
|
||||
S3_DELETE_BUCKET,
|
||||
S3_FORCE_DELETE_BUCKET,
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
S3_GET_BUCKET_POLICY,
|
||||
S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
S3_PUT_REPLICATION_CONFIGURATION,
|
||||
} from "../../../../types";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../../Common/VerticalTabs/VerticalTabs";
|
||||
import BackLink from "../../../../common/BackLink";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -337,25 +326,25 @@ const BucketDetails = ({
|
||||
}
|
||||
title={bucketName}
|
||||
subTitle={
|
||||
displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_BUCKET_POLICY],
|
||||
false
|
||||
) && (
|
||||
<Fragment>
|
||||
Access:{" "}
|
||||
<span className={classes.capitalize}>
|
||||
{bucketInfo?.access.toLowerCase()}
|
||||
</span>
|
||||
</Fragment>
|
||||
)
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_POLICY]}
|
||||
resource={bucketName}
|
||||
>
|
||||
Access:{" "}
|
||||
<span className={classes.capitalize}>
|
||||
{bucketInfo?.access.toLowerCase()}
|
||||
</span>
|
||||
</SecureComponent>
|
||||
}
|
||||
actions={
|
||||
<Fragment>
|
||||
{displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_DELETE_BUCKET,
|
||||
S3_FORCE_DELETE_BUCKET,
|
||||
]) && (
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_DELETE_BUCKET,
|
||||
IAM_SCOPES.S3_FORCE_DELETE_BUCKET,
|
||||
]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<BoxIconButton
|
||||
tooltip={"Delete"}
|
||||
color="primary"
|
||||
@@ -367,7 +356,7 @@ const BucketDetails = ({
|
||||
>
|
||||
<DeleteIcon />
|
||||
</BoxIconButton>
|
||||
)}
|
||||
</SecureComponent>
|
||||
<BoxIconButton
|
||||
tooltip={"Refresh"}
|
||||
color="primary"
|
||||
@@ -450,9 +439,9 @@ const BucketDetails = ({
|
||||
label: "Events",
|
||||
value: "events",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
disabled: !hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
]),
|
||||
to: getRoutePath("events"),
|
||||
},
|
||||
@@ -464,9 +453,9 @@ const BucketDetails = ({
|
||||
component: Link,
|
||||
disabled:
|
||||
!distributedSetup ||
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
S3_PUT_REPLICATION_CONFIGURATION,
|
||||
!hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_REPLICATION_CONFIGURATION,
|
||||
]),
|
||||
to: getRoutePath("replication"),
|
||||
},
|
||||
@@ -478,9 +467,9 @@ const BucketDetails = ({
|
||||
component: Link,
|
||||
disabled:
|
||||
!distributedSetup ||
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
!hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
]),
|
||||
to: getRoutePath("lifecycle"),
|
||||
},
|
||||
@@ -490,10 +479,10 @@ const BucketDetails = ({
|
||||
label: "Access Audit",
|
||||
value: "access",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_GET_POLICY,
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
ADMIN_LIST_USERS,
|
||||
disabled: !hasPermission(bucketName, [
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
]),
|
||||
to: getRoutePath("access"),
|
||||
},
|
||||
@@ -503,8 +492,8 @@ const BucketDetails = ({
|
||||
label: "Access Rules",
|
||||
value: "prefix",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_POLICY,
|
||||
disabled: !hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_POLICY,
|
||||
]),
|
||||
to: getRoutePath("prefix"),
|
||||
},
|
||||
|
||||
@@ -36,13 +36,11 @@ import api from "../../../../common/api";
|
||||
import DeleteEvent from "./DeleteEvent";
|
||||
import AddEvent from "./AddEvent";
|
||||
import HelpBox from "../../../../common/HelpBox";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import {
|
||||
ADMIN_SERVER_INFO,
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
} from "../../../../types";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -76,16 +74,10 @@ const BucketEventsPanel = ({
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
const displayEvents = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
const displayEvents = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS,
|
||||
]);
|
||||
|
||||
const displaySubscribeToEvents = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_BUCKET_NOTIFICATIONS, ADMIN_SERVER_INFO],
|
||||
true
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setLoadingEvents(true);
|
||||
@@ -156,7 +148,14 @@ const BucketEventsPanel = ({
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<PanelTitle>Events</PanelTitle>
|
||||
{displaySubscribeToEvents && (
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.ADMIN_SERVER_INFO,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -168,28 +167,33 @@ const BucketEventsPanel = ({
|
||||
>
|
||||
Subscribe to Event
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
disabled={!displayEvents}
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "SQS", elementKey: "arn" },
|
||||
{
|
||||
label: "Events",
|
||||
elementKey: "events",
|
||||
renderFunction: eventsDisplay,
|
||||
},
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Suffix", elementKey: "suffix" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={records}
|
||||
entityName="Events"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_NOTIFICATIONS]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "SQS", elementKey: "arn" },
|
||||
{
|
||||
label: "Events",
|
||||
elementKey: "events",
|
||||
renderFunction: eventsDisplay,
|
||||
},
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Suffix", elementKey: "suffix" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={records}
|
||||
entityName="Events"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
{!loadingEvents && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -37,13 +37,11 @@ import EditLifecycleConfiguration from "./EditLifecycleConfiguration";
|
||||
import AddLifecycleModal from "./AddLifecycleModal";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import HelpBox from "../../../../common/HelpBox";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import {
|
||||
ADMIN_LIST_TIERS,
|
||||
S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
} from "../../../../types";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -76,16 +74,10 @@ const BucketLifecyclePanel = ({
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
const displayLifeCycleRules = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
const displayLifeCycleRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
]);
|
||||
|
||||
const displayAddLifeCycleRules = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_LIFECYCLE_CONFIGURATION, ADMIN_LIST_TIERS],
|
||||
true
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setLoadingLifecycle(true);
|
||||
@@ -212,7 +204,14 @@ const BucketLifecyclePanel = ({
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<PanelTitle>Lifecycle Rules</PanelTitle>
|
||||
{displayAddLifeCycleRules && (
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.ADMIN_LIST_TIERS,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -224,19 +223,25 @@ const BucketLifecyclePanel = ({
|
||||
>
|
||||
Add Lifecycle Rule
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={lifecycleColumns}
|
||||
isLoading={loadingLifecycle}
|
||||
records={lifecycleRecords}
|
||||
entityName="Lifecycle"
|
||||
customEmptyMessage="There are no Lifecycle rules yet"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={lifecycleColumns}
|
||||
isLoading={loadingLifecycle}
|
||||
records={lifecycleRecords}
|
||||
entityName="Lifecycle"
|
||||
customEmptyMessage="There are no Lifecycle rules yet"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
{!loadingLifecycle && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -40,12 +40,11 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import AddReplicationModal from "./AddReplicationModal";
|
||||
import DeleteReplicationRule from "./DeleteReplicationRule";
|
||||
import HelpBox from "../../../../common/HelpBox";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import {
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
S3_PUT_REPLICATION_CONFIGURATION,
|
||||
} from "../../../../types";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
interface IBucketReplicationProps {
|
||||
classes: any;
|
||||
@@ -82,16 +81,10 @@ const BucketReplicationPanel = ({
|
||||
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
const displayReplicationRules = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
const displayReplicationRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION,
|
||||
]);
|
||||
|
||||
const displayAddReplicationRules = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_REPLICATION_CONFIGURATION],
|
||||
true
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setLoadingReplication(true);
|
||||
@@ -182,7 +175,11 @@ const BucketReplicationPanel = ({
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<PanelTitle>Replication</PanelTitle>
|
||||
{displayAddReplicationRules && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_REPLICATION_CONFIGURATION]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -194,39 +191,44 @@ const BucketReplicationPanel = ({
|
||||
>
|
||||
Add Replication Rule
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
disabled={!displayReplicationRules}
|
||||
itemActions={replicationTableActions}
|
||||
columns={[
|
||||
{
|
||||
label: "Priority",
|
||||
elementKey: "priority",
|
||||
},
|
||||
{
|
||||
label: "Destination",
|
||||
elementKey: "destination",
|
||||
renderFunction: ruleDestDisplay,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "prefix",
|
||||
},
|
||||
{
|
||||
label: "Tags",
|
||||
elementKey: "tags",
|
||||
renderFunction: tagDisplay,
|
||||
},
|
||||
{ label: "Status", elementKey: "status" },
|
||||
]}
|
||||
isLoading={loadingReplication}
|
||||
records={replicationRules}
|
||||
entityName="Replication Rules"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
itemActions={replicationTableActions}
|
||||
columns={[
|
||||
{
|
||||
label: "Priority",
|
||||
elementKey: "priority",
|
||||
},
|
||||
{
|
||||
label: "Destination",
|
||||
elementKey: "destination",
|
||||
renderFunction: ruleDestDisplay,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "prefix",
|
||||
},
|
||||
{
|
||||
label: "Tags",
|
||||
elementKey: "tags",
|
||||
renderFunction: tagDisplay,
|
||||
},
|
||||
{ label: "Status", elementKey: "status" },
|
||||
]}
|
||||
isLoading={loadingReplication}
|
||||
records={replicationRules}
|
||||
entityName="Replication Rules"
|
||||
idField="id"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
|
||||
@@ -53,21 +53,7 @@ import GavelIcon from "@mui/icons-material/Gavel";
|
||||
import EnableQuota from "./EnableQuota";
|
||||
import { setBucketDetailsLoad } from "../actions";
|
||||
import ReportedUsageIcon from "../../../../icons/ReportedUsageIcon";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import {
|
||||
ADMIN_GET_BUCKET_QUOTA,
|
||||
ADMIN_SET_BUCKET_QUOTA,
|
||||
S3_GET_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION,
|
||||
S3_GET_BUCKET_POLICY,
|
||||
S3_GET_BUCKET_VERSIONING,
|
||||
S3_GET_OBJECT_RETENTION,
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
S3_PUT_BUCKET_POLICY,
|
||||
S3_PUT_BUCKET_VERSIONING,
|
||||
S3_PUT_OBJECT_RETENTION,
|
||||
} from "../../../../types";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import Chip from "@mui/material/Chip";
|
||||
@@ -75,6 +61,9 @@ import AddIcon from "@mui/icons-material/Add";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import AddBucketTagModal from "./AddBucketTagModal";
|
||||
import DeleteBucketTagModal from "./DeleteBucketTagModal";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
interface IBucketSummaryProps {
|
||||
classes: any;
|
||||
@@ -185,62 +174,18 @@ const BucketSummary = ({
|
||||
accessPolicy = bucketInfo.access;
|
||||
}
|
||||
|
||||
const displayGetBucketPolicy = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_POLICY,
|
||||
const displayGetBucketObjectLockConfiguration = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION,
|
||||
]);
|
||||
|
||||
const displayPutBucketPolicy = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_PUT_BUCKET_POLICY,
|
||||
const displayGetBucketEncryptionConfiguration = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
]);
|
||||
|
||||
const displayGetReplicationConfiguration = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_REPLICATION_CONFIGURATION]
|
||||
);
|
||||
|
||||
const displayGetBucketObjectLockConfiguration = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION]
|
||||
);
|
||||
|
||||
const displayGetBucketEncryptionConfiguration = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_BUCKET_ENCRYPTION_CONFIGURATION]
|
||||
);
|
||||
|
||||
const displayPutBucketEncryptionConfiguration = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION]
|
||||
);
|
||||
|
||||
const displayGetBucketVersioning = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_BUCKET_VERSIONING]
|
||||
);
|
||||
|
||||
const displayPutBucketVersioning = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_BUCKET_VERSIONING]
|
||||
);
|
||||
|
||||
const displayGetBucketQuota = displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_GET_BUCKET_QUOTA,
|
||||
const displayGetBucketQuota = hasPermission(bucketName, [
|
||||
IAM_SCOPES.ADMIN_GET_BUCKET_QUOTA,
|
||||
]);
|
||||
|
||||
const displaySetBucketQuota = displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_SET_BUCKET_QUOTA,
|
||||
]);
|
||||
|
||||
const displayGetObjectRetention = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_GET_OBJECT_RETENTION]
|
||||
);
|
||||
|
||||
const displayPutObjectRetention = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_OBJECT_RETENTION]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setBucketLoading(true);
|
||||
@@ -576,34 +521,45 @@ const BucketSummary = ({
|
||||
<Grid item xs={8}>
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
{displayGetBucketPolicy && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_POLICY]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Access Policy:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
<Button
|
||||
disabled={!displayPutBucketPolicy}
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setAccessPolicyScreenOpen(true);
|
||||
}}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_BUCKET_POLICY]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
{bucketLoading ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
accessPolicy.toLowerCase()
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setAccessPolicyScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
{bucketLoading ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
accessPolicy.toLowerCase()
|
||||
)}
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</SecureComponent>
|
||||
{distributedSetup && (
|
||||
<Fragment>
|
||||
{displayGetReplicationConfiguration && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Replication:</td>
|
||||
<td className={classes.doubleElement}>
|
||||
@@ -612,16 +568,24 @@ const BucketSummary = ({
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayGetBucketObjectLockConfiguration && (
|
||||
</SecureComponent>
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION,
|
||||
]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Object Locking:</td>
|
||||
<td>{!hasObjectLocking ? "Disabled" : "Enabled"}</td>
|
||||
</tr>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Fragment>
|
||||
)}
|
||||
{displayGetBucketEncryptionConfiguration && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_ENCRYPTION_CONFIGURATION]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Encryption:</td>
|
||||
<td>
|
||||
@@ -632,20 +596,27 @@ const BucketSummary = ({
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled={!displayPutBucketEncryptionConfiguration}
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setEnableEncryptionScreenOpen(true);
|
||||
}}
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION,
|
||||
]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
{encryptionEnabled ? "Enabled" : "Disabled"}
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setEnableEncryptionScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
{encryptionEnabled ? "Enabled" : "Disabled"}
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</SecureComponent>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Tags:</td>
|
||||
<td>
|
||||
@@ -705,39 +676,48 @@ const BucketSummary = ({
|
||||
</Paper>
|
||||
<br />
|
||||
<br />
|
||||
{distributedSetup && displayGetBucketVersioning && (
|
||||
<Fragment>
|
||||
<Paper className={classes.paperContainer} elevation={1}>
|
||||
<Grid container>
|
||||
<Grid item xs={quotaEnabled ? 9 : 12}>
|
||||
<h2>Versioning</h2>
|
||||
<hr className={classes.hrClass} />
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Versioning:</td>
|
||||
<td>
|
||||
{loadingVersioning ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Button
|
||||
disabled={!displayPutBucketVersioning}
|
||||
{distributedSetup && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_BUCKET_VERSIONING]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<Fragment>
|
||||
<Paper className={classes.paperContainer} elevation={1}>
|
||||
<Grid container>
|
||||
<Grid item xs={quotaEnabled ? 9 : 12}>
|
||||
<h2>Versioning</h2>
|
||||
<hr className={classes.hrClass} />
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Versioning:</td>
|
||||
<td>
|
||||
{loadingVersioning ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={setBucketVersioning}
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_BUCKET_VERSIONING]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
{isVersioned ? "Enabled" : "Disabled"}
|
||||
</Button>
|
||||
</Fragment>
|
||||
)}
|
||||
</td>
|
||||
{displayGetBucketQuota && (
|
||||
<Fragment>
|
||||
<Button
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={setBucketVersioning}
|
||||
>
|
||||
{isVersioned ? "Enabled" : "Disabled"}
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
)}
|
||||
</td>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_GET_BUCKET_QUOTA]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<td className={classes.titleCol}>Quota:</td>
|
||||
<td>
|
||||
{loadingQuota ? (
|
||||
@@ -747,112 +727,123 @@ const BucketSummary = ({
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_SET_BUCKET_QUOTA]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<Button
|
||||
disabled={!displaySetBucketQuota}
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={setBucketQuota}
|
||||
>
|
||||
{quotaEnabled ? "Enabled" : "Disabled"}
|
||||
</Button>
|
||||
</Fragment>
|
||||
</SecureComponent>
|
||||
)}
|
||||
</td>
|
||||
</SecureComponent>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
{quotaEnabled && quota && (
|
||||
<Grid item xs={3} className={classes.reportedUsage}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon} xs={2}>
|
||||
<GavelIcon />
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography className={classes.elementTitle}>
|
||||
{cap(quota?.type)} Quota
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{niceBytes(`${quota?.quota}`)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Paper>
|
||||
<br />
|
||||
<br />
|
||||
</Fragment>
|
||||
</SecureComponent>
|
||||
)}
|
||||
|
||||
{hasObjectLocking && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_OBJECT_RETENTION]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<h2>Retention</h2>
|
||||
<hr className={classes.hrClass} />
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
<tr className={classes.gridContainer}>
|
||||
<td className={classes.titleCol}>Status:</td>
|
||||
<td>
|
||||
{loadingRetention ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_OBJECT_RETENTION]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<Button
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setRetentionConfigOpen(true);
|
||||
}}
|
||||
>
|
||||
{!retentionEnabled ? "Disabled" : "Enabled"}
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
)}
|
||||
</td>
|
||||
{retentionConfig === null ? (
|
||||
<td colSpan={2}> </td>
|
||||
) : (
|
||||
<Fragment>
|
||||
<td className={classes.titleCol}>Mode:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{retentionConfig && retentionConfig.mode}
|
||||
</td>
|
||||
</Fragment>
|
||||
)}
|
||||
</tr>
|
||||
<tr className={classes.gridContainer}>
|
||||
{retentionConfig === null ? (
|
||||
<td colSpan={2}></td>
|
||||
) : (
|
||||
<Fragment>
|
||||
<td className={classes.titleCol}>Valitidy:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{retentionConfig && retentionConfig.validity}{" "}
|
||||
{retentionConfig &&
|
||||
(retentionConfig.validity === 1
|
||||
? retentionConfig.unit.slice(0, -1)
|
||||
: retentionConfig.unit)}
|
||||
</td>
|
||||
</Fragment>
|
||||
)}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
{quotaEnabled && quota && (
|
||||
<Grid item xs={3} className={classes.reportedUsage}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon} xs={2}>
|
||||
<GavelIcon />
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Typography className={classes.elementTitle}>
|
||||
{cap(quota?.type)} Quota
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{niceBytes(`${quota?.quota}`)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Paper>
|
||||
<br />
|
||||
<br />
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{hasObjectLocking && displayGetObjectRetention && (
|
||||
<Paper className={classes.paperContainer}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<h2>Retention</h2>
|
||||
<hr className={classes.hrClass} />
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
<tr className={classes.gridContainer}>
|
||||
<td className={classes.titleCol}>Status:</td>
|
||||
<td>
|
||||
{loadingRetention ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Button
|
||||
disabled={!displayPutObjectRetention}
|
||||
color="primary"
|
||||
className={classes.anchorButton}
|
||||
onClick={() => {
|
||||
setRetentionConfigOpen(true);
|
||||
}}
|
||||
>
|
||||
{!retentionEnabled ? "Disabled" : "Enabled"}
|
||||
</Button>
|
||||
</Fragment>
|
||||
)}
|
||||
</td>
|
||||
{retentionConfig === null ? (
|
||||
<td colSpan={2}> </td>
|
||||
) : (
|
||||
<Fragment>
|
||||
<td className={classes.titleCol}>Mode:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{retentionConfig && retentionConfig.mode}
|
||||
</td>
|
||||
</Fragment>
|
||||
)}
|
||||
</tr>
|
||||
<tr className={classes.gridContainer}>
|
||||
{retentionConfig === null ? (
|
||||
<td colSpan={2}></td>
|
||||
) : (
|
||||
<Fragment>
|
||||
<td className={classes.titleCol}>Valitidy:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{retentionConfig && retentionConfig.validity}{" "}
|
||||
{retentionConfig &&
|
||||
(retentionConfig.validity === 1
|
||||
? retentionConfig.unit.slice(0, -1)
|
||||
: retentionConfig.unit)}
|
||||
</td>
|
||||
</Fragment>
|
||||
)}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</SecureComponent>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,11 @@ import { Box, Button, Grid, Typography } from "@mui/material";
|
||||
import { niceBytes, prettyNumber } from "../../../../common/utils";
|
||||
import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
IAM_PERMISSIONS,
|
||||
IAM_ROLES,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
import SecureComponent from "../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -213,7 +218,10 @@ const BucketListItem = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4} textAlign={"right"}>
|
||||
{bucket.manage && (
|
||||
<SecureComponent
|
||||
scopes={IAM_PERMISSIONS[IAM_ROLES.admin]}
|
||||
resource={bucket.name}
|
||||
>
|
||||
<Link
|
||||
to={`/buckets/${bucket.name}/admin`}
|
||||
style={{ textDecoration: "none" }}
|
||||
@@ -226,7 +234,7 @@ const BucketListItem = ({
|
||||
Manage
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</SecureComponent>
|
||||
<Link
|
||||
to={`/buckets/${bucket.name}/browse`}
|
||||
style={{ textDecoration: "none" }}
|
||||
|
||||
@@ -21,7 +21,7 @@ import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Button, LinearProgress } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Bucket, BucketList, HasPermissionResponse } from "../types";
|
||||
import { Bucket, BucketList } from "../types";
|
||||
import { AddIcon, BucketsIcon } from "../../../../icons";
|
||||
import { AppState } from "../../../../store";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
@@ -45,6 +45,11 @@ import RefreshIcon from "../../../../icons/RefreshIcon";
|
||||
import AButton from "../../Common/AButton/AButton";
|
||||
import MultipleBucketsIcon from "../../../../icons/MultipleBucketsIcon";
|
||||
import SelectMultipleIcon from "../../../../icons/SelectMultipleIcon";
|
||||
import SecureComponent from "../../../../common/SecureComponent/SecureComponent";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_SCOPES,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -150,49 +155,12 @@ const ListBuckets = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
const [loadingPerms, setLoadingPerms] = useState<boolean>(true);
|
||||
const [canCreateBucket, setCanCreateBucket] = useState<boolean>(false);
|
||||
const [selectedBuckets, setSelectedBuckets] = useState<string[]>([]);
|
||||
const [replicationModalOpen, setReplicationModalOpen] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [bulkSelect, setBulkSelect] = useState<boolean>(false);
|
||||
|
||||
// check the permissions for creating bucket
|
||||
useEffect(() => {
|
||||
if (loadingPerms) {
|
||||
api
|
||||
.invoke("POST", `/api/v1/has-permission`, {
|
||||
actions: [
|
||||
{
|
||||
id: "createBucket",
|
||||
action: "s3:CreateBucket",
|
||||
},
|
||||
],
|
||||
})
|
||||
.then((res: HasPermissionResponse) => {
|
||||
setLoadingPerms(false);
|
||||
if (!res.permissions) {
|
||||
return;
|
||||
}
|
||||
const actions = res.permissions ? res.permissions : [];
|
||||
|
||||
let canCreate = actions.find((s) => s.id === "createBucket");
|
||||
if (canCreate && canCreate.can) {
|
||||
setCanCreateBucket(true);
|
||||
} else {
|
||||
setCanCreateBucket(false);
|
||||
}
|
||||
|
||||
setLoadingPerms(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoadingPerms(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loadingPerms, setErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
const fetchRecords = () => {
|
||||
@@ -340,7 +308,10 @@ const ListBuckets = ({
|
||||
>
|
||||
<RefreshIcon />
|
||||
</BoxIconButton>
|
||||
{canCreateBucket && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_CREATE_BUCKET]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -352,7 +323,7 @@ const ListBuckets = ({
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -94,16 +94,14 @@ import {
|
||||
FileZipIcon,
|
||||
} from "../../../../../../icons";
|
||||
import ShareFile from "../ObjectDetails/ShareFile";
|
||||
import { displayComponent } from "../../../../../../utils/permissions";
|
||||
import {
|
||||
S3_DELETE_OBJECT,
|
||||
S3_LIST_BUCKET,
|
||||
S3_PUT_OBJECT,
|
||||
} from "../../../../../../types";
|
||||
import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
|
||||
import { setBucketDetailsLoad, setBucketInfo } from "../../../actions";
|
||||
import { AppState } from "../../../../../../store";
|
||||
import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||
import BoxIconButton from "../../../../Common/BoxIconButton/BoxIconButton";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const commonIcon = {
|
||||
backgroundRepeat: "no-repeat",
|
||||
@@ -291,16 +289,12 @@ const ListObjects = ({
|
||||
|
||||
const fileUpload = useRef<HTMLInputElement>(null);
|
||||
|
||||
const displayPutObject = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_PUT_OBJECT,
|
||||
const displayDeleteObject = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_DELETE_OBJECT,
|
||||
]);
|
||||
|
||||
const displayDeleteObject = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_DELETE_OBJECT,
|
||||
]);
|
||||
|
||||
const displayListObjects = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_LIST_BUCKET,
|
||||
const displayListObjects = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_LIST_BUCKET,
|
||||
]);
|
||||
|
||||
const updateMessage = () => {
|
||||
@@ -339,11 +333,7 @@ const ListObjects = ({
|
||||
}, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
loadingVersioning &&
|
||||
bucketInfo?.allowedActions &&
|
||||
bucketInfo?.name === bucketName
|
||||
) {
|
||||
if (loadingVersioning) {
|
||||
if (displayListObjects) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
|
||||
@@ -359,14 +349,7 @@ const ListObjects = ({
|
||||
setLoadingVersioning(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
bucketName,
|
||||
loadingVersioning,
|
||||
setErrorSnackMessage,
|
||||
bucketInfo?.allowedActions,
|
||||
bucketInfo?.name,
|
||||
displayListObjects,
|
||||
]);
|
||||
}, [bucketName, loadingVersioning, setErrorSnackMessage, displayListObjects]);
|
||||
|
||||
// Rewind
|
||||
useEffect(() => {
|
||||
@@ -423,11 +406,7 @@ const ListObjects = ({
|
||||
}, [internalPaths]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
loading &&
|
||||
bucketInfo?.allowedActions &&
|
||||
bucketInfo?.name === bucketName
|
||||
) {
|
||||
if (loading) {
|
||||
if (displayListObjects) {
|
||||
let pathPrefix = "";
|
||||
if (internalPaths) {
|
||||
@@ -1085,44 +1064,45 @@ const ListObjects = ({
|
||||
}
|
||||
actions={
|
||||
<Fragment>
|
||||
{displayPutObject && (
|
||||
<Fragment>
|
||||
<BoxIconButton
|
||||
tooltip={"Choose or create a new path"}
|
||||
color="primary"
|
||||
aria-label="Add a new folder"
|
||||
onClick={() => {
|
||||
setCreateFolderOpen(true);
|
||||
}}
|
||||
disabled={rewindEnabled}
|
||||
size="large"
|
||||
>
|
||||
<AddFolderIcon />
|
||||
</BoxIconButton>
|
||||
<BoxIconButton
|
||||
tooltip={"Upload file"}
|
||||
color="primary"
|
||||
aria-label="Refresh List"
|
||||
onClick={() => {
|
||||
if (fileUpload && fileUpload.current) {
|
||||
fileUpload.current.click();
|
||||
}
|
||||
}}
|
||||
disabled={rewindEnabled}
|
||||
size="large"
|
||||
>
|
||||
<UploadIcon />
|
||||
</BoxIconButton>
|
||||
<input
|
||||
type="file"
|
||||
multiple={true}
|
||||
onChange={(e) => uploadObject(e)}
|
||||
id="file-input"
|
||||
style={{ display: "none" }}
|
||||
ref={fileUpload}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
<SecureComponent
|
||||
resource={bucketName}
|
||||
scopes={[IAM_SCOPES.S3_PUT_OBJECT]}
|
||||
>
|
||||
<BoxIconButton
|
||||
tooltip={"Choose or create a new path"}
|
||||
color="primary"
|
||||
aria-label="Add a new folder"
|
||||
onClick={() => {
|
||||
setCreateFolderOpen(true);
|
||||
}}
|
||||
disabled={rewindEnabled}
|
||||
size="large"
|
||||
>
|
||||
<AddFolderIcon />
|
||||
</BoxIconButton>
|
||||
<BoxIconButton
|
||||
tooltip={"Upload file"}
|
||||
color="primary"
|
||||
aria-label="Refresh List"
|
||||
onClick={() => {
|
||||
if (fileUpload && fileUpload.current) {
|
||||
fileUpload.current.click();
|
||||
}
|
||||
}}
|
||||
disabled={rewindEnabled}
|
||||
size="large"
|
||||
>
|
||||
<UploadIcon />
|
||||
</BoxIconButton>
|
||||
<input
|
||||
type="file"
|
||||
multiple={true}
|
||||
onChange={(e) => uploadObject(e)}
|
||||
id="file-input"
|
||||
style={{ display: "none" }}
|
||||
ref={fileUpload}
|
||||
/>
|
||||
</SecureComponent>
|
||||
<Badge
|
||||
badgeContent=" "
|
||||
color="secondary"
|
||||
@@ -1161,7 +1141,10 @@ const ListObjects = ({
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
{displayListObjects && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_LIST_BUCKET]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<TextField
|
||||
placeholder="Search Objects"
|
||||
className={classes.searchField}
|
||||
@@ -1180,9 +1163,11 @@ const ListObjects = ({
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
)}
|
||||
|
||||
{displayDeleteObject && (
|
||||
</SecureComponent>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_DELETE_OBJECT]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
@@ -1194,33 +1179,38 @@ const ListObjects = ({
|
||||
>
|
||||
Delete Selected
|
||||
</Button>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
disabled={!displayListObjects}
|
||||
itemActions={tableActions}
|
||||
columns={rewindEnabled ? rewindModeColumns : listModeColumns}
|
||||
isLoading={rewindEnabled ? loadingRewind : loading}
|
||||
loadingMessage={loadingMessage}
|
||||
entityName="Objects"
|
||||
idField="name"
|
||||
records={payload}
|
||||
customPaperHeight={classes.browsePaper}
|
||||
selectedItems={selectedObjects}
|
||||
onSelect={selectListObjects}
|
||||
customEmptyMessage={`This location is empty${
|
||||
!rewindEnabled ? ", please try uploading a new file" : ""
|
||||
}`}
|
||||
sortConfig={{
|
||||
currentSort: currentSortField,
|
||||
currentDirection: sortDirection,
|
||||
triggerSort: sortChange,
|
||||
}}
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_LIST_BUCKET]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={rewindEnabled ? rewindModeColumns : listModeColumns}
|
||||
isLoading={rewindEnabled ? loadingRewind : loading}
|
||||
loadingMessage={loadingMessage}
|
||||
entityName="Objects"
|
||||
idField="name"
|
||||
records={payload}
|
||||
customPaperHeight={classes.browsePaper}
|
||||
selectedItems={selectedObjects}
|
||||
onSelect={selectListObjects}
|
||||
customEmptyMessage={`This location is empty${
|
||||
!rewindEnabled ? ", please try uploading a new file" : ""
|
||||
}`}
|
||||
sortConfig={{
|
||||
currentSort: currentSortField,
|
||||
currentDirection: sortDirection,
|
||||
triggerSort: sortChange,
|
||||
}}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -63,18 +63,7 @@ import {
|
||||
setSnackBarMessage,
|
||||
} from "../../../../../../actions";
|
||||
import { decodeFileName, encodeFileName } from "../../../../../../common/utils";
|
||||
import { BucketInfo } from "../../../types";
|
||||
import { displayComponent } from "../../../../../../utils/permissions";
|
||||
import {
|
||||
S3_DELETE_OBJECT,
|
||||
S3_DELETE_OBJECT_TAGGING,
|
||||
S3_GET_OBJECT_LEGAL_HOLD,
|
||||
S3_GET_OBJECT_RETENTION,
|
||||
S3_GET_OBJECT_TAGGING,
|
||||
S3_PUT_OBJECT_LEGAL_HOLD,
|
||||
S3_PUT_OBJECT_RETENTION,
|
||||
S3_PUT_OBJECT_TAGGING,
|
||||
} from "../../../../../../types";
|
||||
import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
|
||||
import SetRetention from "./SetRetention";
|
||||
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
|
||||
import DeleteObject from "../ListObjects/DeleteObject";
|
||||
@@ -91,6 +80,7 @@ import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../../../../Common/VerticalTabs/VerticalTabs";
|
||||
import BoxIconButton from "../../../../Common/BoxIconButton/BoxIconButton";
|
||||
import { RecoverIcon } from "../../../../../../icons";
|
||||
import SecureComponent from "../../../../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -271,8 +261,6 @@ const ObjectDetails = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [metadataLoad, setMetadataLoad] = useState<boolean>(true);
|
||||
const [metadata, setMetadata] = useState<any>({});
|
||||
const [loadingBucket, setLoadingBucket] = useState<boolean>(false);
|
||||
const [bucketInfo, setBucketInfo] = useState<any>(null);
|
||||
const [restoreVersionOpen, setRestoreVersionOpen] = useState<boolean>(false);
|
||||
const [restoreVersion, setRestoreVersion] = useState<string>("");
|
||||
|
||||
@@ -288,64 +276,6 @@ const ObjectDetails = ({
|
||||
objectNameArray = actualInfo.name.split("/");
|
||||
}
|
||||
|
||||
const displayObjectLegalHold = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_OBJECT_LEGAL_HOLD,
|
||||
]);
|
||||
|
||||
const displayEditObjectLegalHold = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_OBJECT_LEGAL_HOLD],
|
||||
true
|
||||
);
|
||||
|
||||
const displayObjectRetention = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_OBJECT_RETENTION,
|
||||
]);
|
||||
|
||||
const displayEditObjectRetention = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_OBJECT_RETENTION],
|
||||
true
|
||||
);
|
||||
|
||||
const displayObjectTag = displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_OBJECT_TAGGING,
|
||||
]);
|
||||
|
||||
const displayEditObjectTagging = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_PUT_OBJECT_TAGGING],
|
||||
true
|
||||
);
|
||||
|
||||
const displayRemoveObjectTagging = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_DELETE_OBJECT_TAGGING],
|
||||
true
|
||||
);
|
||||
|
||||
const displayDeleteObject = displayComponent(
|
||||
bucketInfo?.allowedActions,
|
||||
[S3_DELETE_OBJECT],
|
||||
true
|
||||
);
|
||||
|
||||
// bucket info
|
||||
useEffect(() => {
|
||||
if (!loadingBucket) {
|
||||
setLoadingBucket(true);
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}`)
|
||||
.then((res: BucketInfo) => {
|
||||
setBucketInfo(res);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoadingBucket(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [bucketName, loadingBucket, setBucketInfo, setErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadObjectData) {
|
||||
api
|
||||
@@ -682,7 +612,11 @@ const ObjectDetails = ({
|
||||
<DownloadIcon />
|
||||
</BoxIconButton>
|
||||
)}
|
||||
{displayDeleteObject && (
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_DELETE_OBJECT]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<BoxIconButton
|
||||
tooltip={"Delete Object"}
|
||||
color="primary"
|
||||
@@ -695,7 +629,7 @@ const ObjectDetails = ({
|
||||
>
|
||||
<DeleteIcon />
|
||||
</BoxIconButton>
|
||||
)}
|
||||
</SecureComponent>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
@@ -711,80 +645,105 @@ const ObjectDetails = ({
|
||||
<h1 className={classes.sectionTitle}>Details</h1>
|
||||
</div>
|
||||
<br />
|
||||
{(displayObjectLegalHold ||
|
||||
displayObjectRetention ||
|
||||
displayObjectTag) && (
|
||||
<Grid item xs={12}>
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
{displayObjectLegalHold && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>
|
||||
Legal Hold:
|
||||
</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.version_id &&
|
||||
actualInfo.version_id !== "null" ? (
|
||||
<Fragment>
|
||||
{actualInfo.legal_hold_status
|
||||
? actualInfo.legal_hold_status.toLowerCase()
|
||||
: "Off"}
|
||||
{displayEditObjectLegalHold && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="legal-hold"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
setLegalholdOpen(true);
|
||||
<Grid item xs={12}>
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_OBJECT_LEGAL_HOLD]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Legal Hold:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.version_id &&
|
||||
actualInfo.version_id !== "null" ? (
|
||||
<Fragment>
|
||||
{actualInfo.legal_hold_status
|
||||
? actualInfo.legal_hold_status.toLowerCase()
|
||||
: "Off"}
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="legal-hold"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
setLegalholdOpen(true);
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</SecureComponent>
|
||||
</Fragment>
|
||||
) : (
|
||||
"Disabled"
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</SecureComponent>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_OBJECT_RETENTION]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Retention:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.retention_mode
|
||||
? actualInfo.retention_mode.toLowerCase()
|
||||
: "None"}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_OBJECT_RETENTION]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="retention"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
openRetentionModal();
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</SecureComponent>
|
||||
</td>
|
||||
</tr>
|
||||
</SecureComponent>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_GET_OBJECT_TAGGING]}
|
||||
resource={bucketName}
|
||||
>
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Tags:</td>
|
||||
<td>
|
||||
{tagKeys &&
|
||||
tagKeys.map((tagKey, index) => {
|
||||
const tag = get(
|
||||
actualInfo,
|
||||
`tags.${tagKey}`,
|
||||
""
|
||||
);
|
||||
if (tag !== "") {
|
||||
return (
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_DELETE_OBJECT_TAGGING,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
errorProps={{
|
||||
deleteIcon: null,
|
||||
onDelete: null,
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Fragment>
|
||||
) : (
|
||||
"Disabled"
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectRetention && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Retention:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.retention_mode
|
||||
? actualInfo.retention_mode.toLowerCase()
|
||||
: "None"}
|
||||
{displayEditObjectRetention && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="retention"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
openRetentionModal();
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectTag && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Tags:</td>
|
||||
<td>
|
||||
{tagKeys &&
|
||||
tagKeys.map((tagKey, index) => {
|
||||
const tag = get(
|
||||
actualInfo,
|
||||
`tags.${tagKey}`,
|
||||
""
|
||||
);
|
||||
if (tag !== "") {
|
||||
return displayRemoveObjectTagging ? (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
@@ -796,39 +755,35 @@ const ObjectDetails = ({
|
||||
deleteTag(tagKey, tag);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
size="small"
|
||||
label={`${tagKey} : ${tag}`}
|
||||
color="primary"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{displayEditObjectTagging && (
|
||||
<Chip
|
||||
className={classes.tag}
|
||||
icon={<AddIcon />}
|
||||
clickable
|
||||
size="small"
|
||||
label="Add tag"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setTagModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
)}
|
||||
</SecureComponent>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.S3_PUT_OBJECT_TAGGING]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
>
|
||||
<Chip
|
||||
className={classes.tag}
|
||||
icon={<AddIcon />}
|
||||
clickable
|
||||
size="small"
|
||||
label="Add tag"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setTagModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</td>
|
||||
</tr>
|
||||
</SecureComponent>
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12}>
|
||||
<h2>Object Metadata</h2>
|
||||
|
||||
@@ -25,7 +25,6 @@ export interface Bucket {
|
||||
size?: number;
|
||||
objects?: number;
|
||||
rw_access?: RwAccess;
|
||||
allowedActions?: string[];
|
||||
manage: boolean;
|
||||
details?: Details;
|
||||
}
|
||||
@@ -42,8 +41,6 @@ export interface Details {
|
||||
export interface BucketInfo {
|
||||
name: string;
|
||||
access: string;
|
||||
allowedActions?: string[];
|
||||
manage?: boolean;
|
||||
}
|
||||
|
||||
export interface BucketList {
|
||||
|
||||
@@ -533,12 +533,21 @@ const TableWrapper = ({
|
||||
const clickAction = (rowItem: any) => {
|
||||
if (findView) {
|
||||
const valueClick = findView.sendOnlyId ? rowItem[idField] : rowItem;
|
||||
if (findView.to) {
|
||||
|
||||
let disabled = false;
|
||||
|
||||
if (findView.disableButtonFunction) {
|
||||
if (findView.disableButtonFunction(valueClick)) {
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (findView.to && !disabled) {
|
||||
history.push(`${findView.to}/${valueClick}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (findView.onClick) {
|
||||
if (findView.onClick && !disabled) {
|
||||
findView.onClick(valueClick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,7 @@ const initialState: ConsoleState = {
|
||||
pages: [],
|
||||
features: [],
|
||||
distributedMode: false,
|
||||
policy: {
|
||||
version: "",
|
||||
statement: [],
|
||||
},
|
||||
permissions: {},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -14,15 +14,8 @@
|
||||
// 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/>.
|
||||
|
||||
export interface ISessionPolicyStatement {
|
||||
action: string[];
|
||||
condition: any;
|
||||
effect: string;
|
||||
resource: string[];
|
||||
}
|
||||
export interface ISessionPolicy {
|
||||
version: string;
|
||||
statement: ISessionPolicyStatement[];
|
||||
export interface ISessionPermissions {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface ISessionResponse {
|
||||
@@ -31,5 +24,5 @@ export interface ISessionResponse {
|
||||
features: string[];
|
||||
operator: boolean;
|
||||
distributedMode: boolean;
|
||||
policy: ISessionPolicy;
|
||||
permissions: ISessionPermissions;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
export type AppState = ReturnType<typeof globalReducer>;
|
||||
|
||||
export const store = createStore(
|
||||
globalReducer,
|
||||
composeEnhancers(applyMiddleware(thunk))
|
||||
);
|
||||
|
||||
export default function configureStore() {
|
||||
return createStore(globalReducer, composeEnhancers(applyMiddleware(thunk)));
|
||||
return store;
|
||||
}
|
||||
|
||||
@@ -122,55 +122,3 @@ export type SystemActionTypes =
|
||||
| SetModalSnackMessage
|
||||
| SetModalErrorMessage
|
||||
| SetDistributedSetup;
|
||||
|
||||
// S3 Actions
|
||||
export const S3_LIST_BUCKET = "s3:ListBucket";
|
||||
export const S3_GET_BUCKET_POLICY = "s3:GetBucketPolicy";
|
||||
export const S3_PUT_BUCKET_POLICY = "s3:PutBucketPolicy";
|
||||
export const S3_GET_OBJECT = "s3:GetObject";
|
||||
export const S3_PUT_OBJECT = "s3:PutObject";
|
||||
export const S3_GET_OBJECT_LEGAL_HOLD = "s3:GetObjectLegalHold";
|
||||
export const S3_PUT_OBJECT_LEGAL_HOLD = "s3:PutObjectLegalHold";
|
||||
export const S3_DELETE_OBJECT = "s3:DeleteObject";
|
||||
export const S3_GET_BUCKET_VERSIONING = "s3:GetBucketVersioning";
|
||||
export const S3_PUT_BUCKET_VERSIONING = "s3:PutBucketVersioning";
|
||||
|
||||
export const S3_GET_OBJECT_RETENTION = "s3:GetObjectRetention";
|
||||
export const S3_PUT_OBJECT_RETENTION = "s3:PutObjectRetention";
|
||||
|
||||
export const S3_GET_OBJECT_TAGGING = "s3:GetObjectTagging";
|
||||
export const S3_PUT_OBJECT_TAGGING = "s3:PutObjectTagging";
|
||||
export const S3_DELETE_OBJECT_TAGGING = "s3:DeleteObjectTagging";
|
||||
|
||||
export const S3_GET_BUCKET_ENCRYPTION_CONFIGURATION =
|
||||
"s3:GetEncryptionConfiguration";
|
||||
export const S3_PUT_BUCKET_ENCRYPTION_CONFIGURATION =
|
||||
"s3:PutEncryptionConfiguration";
|
||||
export const S3_DELETE_BUCKET = "s3:DeleteBucket";
|
||||
export const S3_FORCE_DELETE_BUCKET = "s3:ForceDeleteBucket";
|
||||
export const S3_GET_BUCKET_NOTIFICATIONS = "s3:GetBucketNotification";
|
||||
export const S3_LISTEN_BUCKET_NOTIFICATIONS = "s3:ListenBucketNotification";
|
||||
export const S3_PUT_BUCKET_NOTIFICATIONS = "s3:PutBucketNotification";
|
||||
export const S3_GET_REPLICATION_CONFIGURATION =
|
||||
"s3:GetReplicationConfiguration";
|
||||
export const S3_PUT_REPLICATION_CONFIGURATION =
|
||||
"s3:PutReplicationConfiguration";
|
||||
export const S3_GET_LIFECYCLE_CONFIGURATION = "s3:GetLifecycleConfiguration";
|
||||
export const S3_PUT_LIFECYCLE_CONFIGURATION = "s3:PutLifecycleConfiguration";
|
||||
export const S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION =
|
||||
"s3:GetBucketObjectLockConfiguration";
|
||||
export const S3_PUT_BUCKET_OBJECT_LOCK_CONFIGURATION =
|
||||
"s3:PutBucketObjectLockConfiguration";
|
||||
|
||||
// Admin Actions
|
||||
export const ADMIN_GET_POLICY = "admin:GetPolicy";
|
||||
export const ADMIN_LIST_USERS = "admin:ListUsers";
|
||||
export const ADMIN_LIST_USER_POLICIES = "admin:ListUserPolicies";
|
||||
export const ADMIN_SERVER_INFO = "admin:ServerInfo";
|
||||
export const ADMIN_GET_BUCKET_QUOTA = "admin:GetBucketQuota";
|
||||
export const ADMIN_SET_BUCKET_QUOTA = "admin:SetBucketQuota";
|
||||
export const ADMIN_LIST_TIERS = "admin:ListTier";
|
||||
export const ADMIN_LIST_GROUPS = "admin:ListGroups";
|
||||
|
||||
export const S3_ALL_ACTIONS = "s3:*";
|
||||
export const ADMIN_ALL_ACTIONS = "admin:*";
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 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 { ADMIN_ALL_ACTIONS, S3_ALL_ACTIONS } from "../types";
|
||||
|
||||
// displayComponent receives a list of user permissions to perform on a specific resource, then compares those permissions against
|
||||
// a list of required permissions and return true or false depending of the level of required access (match all permissions,
|
||||
// match some of the permissions)
|
||||
export const displayComponent = (
|
||||
userPermissionsOnBucket: string[] | null | undefined,
|
||||
requiredPermissions: string[],
|
||||
matchAll?: boolean
|
||||
) => {
|
||||
if (!userPermissionsOnBucket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const s3All = userPermissionsOnBucket.includes(S3_ALL_ACTIONS);
|
||||
const AdminAll = userPermissionsOnBucket.includes(ADMIN_ALL_ACTIONS);
|
||||
|
||||
const permissions = requiredPermissions.filter(function (n) {
|
||||
return (
|
||||
userPermissionsOnBucket.indexOf(n) !== -1 ||
|
||||
(n.indexOf("s3:") !== -1 && s3All) ||
|
||||
(n.indexOf("admin:") !== -1 && AdminAll)
|
||||
);
|
||||
});
|
||||
return matchAll
|
||||
? permissions.length === requiredPermissions.length
|
||||
: permissions.length > 0;
|
||||
};
|
||||
@@ -458,7 +458,7 @@ func listExternalBucketsResponse(params user_api.ListExternalBucketsParams) (*mo
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
remoteClient := AdminClient{Client: remoteAdmin}
|
||||
buckets, err := getAccountBuckets(ctx, remoteClient, *params.Body.AccessKey)
|
||||
buckets, err := getAccountBuckets(ctx, remoteClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
@@ -3661,12 +3661,6 @@ func init() {
|
||||
"access": {
|
||||
"$ref": "#/definitions/bucketAccess"
|
||||
},
|
||||
"allowedActions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"creation_date": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3709,9 +3703,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"manage": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 3
|
||||
@@ -5204,9 +5195,14 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/iamPolicy"
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
@@ -9482,12 +9478,6 @@ func init() {
|
||||
"access": {
|
||||
"$ref": "#/definitions/bucketAccess"
|
||||
},
|
||||
"allowedActions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"creation_date": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -9530,9 +9520,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"manage": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 3
|
||||
@@ -11025,9 +11012,14 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"permissions": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/iamPolicy"
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
|
||||
@@ -24,10 +24,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
|
||||
"github.com/minio/console/pkg/acl"
|
||||
|
||||
"github.com/minio/mc/cmd"
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
"github.com/minio/minio-go/v7"
|
||||
@@ -290,25 +286,13 @@ func getBucketVersionedResponse(session *models.Principal, bucketName string) (*
|
||||
}
|
||||
|
||||
// getAccountBuckets fetches a list of all buckets allowed to that particular client from MinIO Servers
|
||||
func getAccountBuckets(ctx context.Context, client MinioAdmin, accessKey string) ([]*models.Bucket, error) {
|
||||
func getAccountBuckets(ctx context.Context, client MinioAdmin) ([]*models.Bucket, error) {
|
||||
info, err := client.AccountInfo(ctx)
|
||||
if err != nil {
|
||||
return []*models.Bucket{}, err
|
||||
}
|
||||
policyInfo, err := getAccountPolicy(ctx, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bucketInfos []*models.Bucket
|
||||
for _, bucket := range info.Buckets {
|
||||
var bucketAdminRole bool
|
||||
conditionValues := map[string][]string{
|
||||
condition.AWSUsername.Name(): {accessKey},
|
||||
}
|
||||
bucketActions := policyInfo.IsAllowedActions(bucket.Name, "", conditionValues)
|
||||
bucketAdminRoleActions := bucketActions.Intersection(acl.BucketAdminRole)
|
||||
bucketAdminRole = len(bucketAdminRoleActions) > 0
|
||||
|
||||
bucketElem := &models.Bucket{
|
||||
CreationDate: bucket.Created.Format(time.RFC3339),
|
||||
Details: &models.BucketDetails{
|
||||
@@ -321,7 +305,6 @@ func getAccountBuckets(ctx context.Context, client MinioAdmin, accessKey string)
|
||||
Name: swag.String(bucket.Name),
|
||||
Objects: int64(bucket.Objects),
|
||||
Size: int64(bucket.Size),
|
||||
Manage: bucketAdminRole,
|
||||
}
|
||||
|
||||
if bucket.Details != nil {
|
||||
@@ -358,7 +341,7 @@ func getListBucketsResponse(session *models.Principal) (*models.ListBucketsRespo
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
buckets, err := getAccountBuckets(ctx, adminClient, session.AccountAccessKey)
|
||||
buckets, err := getAccountBuckets(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
@@ -493,21 +476,12 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
// set bucket access policy
|
||||
if err := setBucketAccessPolicy(ctx, minioClient, bucketName, *req.Access); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// get updated bucket details and return it
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, bucketName, session.AccountAccessKey)
|
||||
bucket, err := getBucketInfo(ctx, minioClient, bucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
@@ -566,29 +540,7 @@ func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBu
|
||||
}
|
||||
|
||||
// getBucketInfo return bucket information including name, policy access, size and creation date
|
||||
func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdmin, bucketName string, accountName string) (*models.Bucket, error) {
|
||||
// Get Account Policy
|
||||
policyInfo, err := getAccountPolicy(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var bucketAdminRole bool
|
||||
// Retrieve list of allowed bucketActionsArray on the bucket
|
||||
// TODO: Add all the possible variables
|
||||
conditionValues := map[string][]string{
|
||||
condition.AWSUsername.Name(): {accountName},
|
||||
}
|
||||
|
||||
bucketActions := policyInfo.IsAllowedActions(bucketName, "", conditionValues)
|
||||
// Check if one of these bucketActionsArray belongs to administrative bucketActionsArray
|
||||
bucketAdminRoleActions := bucketActions.Intersection(acl.BucketAdminRole)
|
||||
bucketAdminRole = len(bucketAdminRoleActions) > 0
|
||||
var bucketActionsArray []string
|
||||
for _, action := range bucketActions.ToSlice() {
|
||||
bucketActionsArray = append(bucketActionsArray, string(action))
|
||||
}
|
||||
|
||||
func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (*models.Bucket, error) {
|
||||
var bucketAccess models.BucketAccess
|
||||
policyStr, err := client.getBucketPolicy(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
@@ -611,29 +563,21 @@ func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdm
|
||||
bucketAccess = models.BucketAccessCUSTOM
|
||||
}
|
||||
bucketTags, err := client.GetBucketTagging(ctx, bucketName)
|
||||
var bucket *models.Bucket
|
||||
if err == nil && bucketTags != nil {
|
||||
bucket = &models.Bucket{
|
||||
Name: &bucketName,
|
||||
Access: &bucketAccess,
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
AllowedActions: bucketActionsArray,
|
||||
Manage: bucketAdminRole,
|
||||
Details: &models.BucketDetails{Tags: bucketTags.ToMap()},
|
||||
}
|
||||
} else {
|
||||
bucket = &models.Bucket{
|
||||
Name: &bucketName,
|
||||
Access: &bucketAccess,
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
AllowedActions: bucketActionsArray,
|
||||
Manage: bucketAdminRole,
|
||||
Details: &models.BucketDetails{},
|
||||
}
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
LogError("error getting bucket tags: %v", err)
|
||||
}
|
||||
return bucket, nil
|
||||
bucketDetails := &models.BucketDetails{}
|
||||
if bucketTags != nil {
|
||||
bucketDetails.Tags = bucketTags.ToMap()
|
||||
}
|
||||
return &models.Bucket{
|
||||
Name: &bucketName,
|
||||
Access: &bucketAccess,
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
Details: bucketDetails,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getBucketInfoResponse calls getBucketInfo() to get the bucket's info
|
||||
@@ -647,16 +591,7 @@ func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfo
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, params.Name, session.AccountAccessKey)
|
||||
bucket, err := getBucketInfo(ctx, minioClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func TestListBucket(t *testing.T) {
|
||||
// get list buckets response this response should have Name, CreationDate, Size and Access
|
||||
// as part of of each bucket
|
||||
function := "getaAcountUsageInfo()"
|
||||
bucketList, err := getAccountBuckets(ctx, adminClient, "")
|
||||
bucketList, err := getAccountBuckets(ctx, adminClient)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func TestListBucket(t *testing.T) {
|
||||
minioAccountInfoMock = func(ctx context.Context) (madmin.AccountInfo, error) {
|
||||
return madmin.AccountInfo{}, errors.New("error")
|
||||
}
|
||||
_, err = getAccountBuckets(ctx, adminClient, "")
|
||||
_, err = getAccountBuckets(ctx, adminClient)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
@@ -257,7 +257,6 @@ func TestBucketInfo(t *testing.T) {
|
||||
// mock minIO client
|
||||
minClient := minioClientMock{}
|
||||
ctx := context.Background()
|
||||
adminClient := adminClientMock{}
|
||||
function := "getBucketInfo()"
|
||||
|
||||
// Test-1: getBucketInfo() get a bucket with PRIVATE access
|
||||
@@ -308,7 +307,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
return mockBucketList, nil
|
||||
}
|
||||
|
||||
bucketInfo, err := getBucketInfo(ctx, minClient, adminClient, bucketToSet, "user1")
|
||||
bucketInfo, err := getBucketInfo(ctx, minClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -330,7 +329,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -352,7 +351,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
}
|
||||
@@ -373,7 +372,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
CreationDate: "", // to be implemented
|
||||
Size: 0, // to be implemented
|
||||
}
|
||||
_, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet, "bucket1")
|
||||
_, err = getBucketInfo(ctx, minClient, bucketToSet)
|
||||
if assert.Error(err) {
|
||||
assert.Equal("invalid character 'p' looking for beginning of value", err.Error())
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/minio/pkg/bucket/policy/condition"
|
||||
|
||||
minioIAMPolicy "github.com/minio/pkg/iam/policy"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/acl"
|
||||
@@ -91,14 +95,82 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
}
|
||||
// by default every user starts with an empty array of available actions
|
||||
// by default every user starts with an empty array of available val
|
||||
// therefore we would have access only to pages that doesn't require any privilege
|
||||
// ie: service-account page
|
||||
var actions []string
|
||||
// if a policy is assigned to this user we parse the actions from there
|
||||
// if a policy is assigned to this user we parse the val from there
|
||||
if policy != nil {
|
||||
actions = acl.GetActionsStringFromPolicy(policy)
|
||||
}
|
||||
|
||||
// This actions will be global, meaning has to be attached to all resources
|
||||
conditionValues := map[string][]string{
|
||||
condition.AWSUsername.Name(): {session.AccountAccessKey},
|
||||
}
|
||||
defaultActions := policy.IsAllowedActions("", "", conditionValues)
|
||||
consoleResourceName := "console-ui"
|
||||
permissions := map[string]minioIAMPolicy.ActionSet{
|
||||
consoleResourceName: defaultActions,
|
||||
}
|
||||
deniedActions := map[string]minioIAMPolicy.ActionSet{}
|
||||
for _, statement := range policy.Statements {
|
||||
for _, resource := range statement.Resources.ToSlice() {
|
||||
resourceName := resource.String()
|
||||
statementActions := statement.Actions.ToSlice()
|
||||
if statement.Effect == "Allow" {
|
||||
// check if val are denied before adding them to the map
|
||||
var allowedActions []minioIAMPolicy.Action
|
||||
if dActions, ok := deniedActions[resourceName]; ok {
|
||||
for _, action := range statementActions {
|
||||
if len(dActions.Intersection(minioIAMPolicy.NewActionSet(action))) == 0 {
|
||||
// It's ok to allow this action
|
||||
allowedActions = append(allowedActions, action)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allowedActions = statementActions
|
||||
}
|
||||
|
||||
// Add validated actions
|
||||
if resourceActions, ok := permissions[resourceName]; ok {
|
||||
mergedActions := append(resourceActions.ToSlice(), allowedActions...)
|
||||
permissions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
|
||||
} else {
|
||||
mergedActions := append(defaultActions.ToSlice(), allowedActions...)
|
||||
permissions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
|
||||
}
|
||||
} else {
|
||||
// Add new banned actions to the map
|
||||
if resourceActions, ok := deniedActions[resourceName]; ok {
|
||||
mergedActions := append(resourceActions.ToSlice(), statementActions...)
|
||||
deniedActions[resourceName] = minioIAMPolicy.NewActionSet(mergedActions...)
|
||||
} else {
|
||||
deniedActions[resourceName] = statement.Actions
|
||||
}
|
||||
// Remove existing val from key if necessary
|
||||
if currentResourceActions, ok := permissions[resourceName]; ok {
|
||||
var newAllowedActions []minioIAMPolicy.Action
|
||||
for _, action := range currentResourceActions.ToSlice() {
|
||||
if len(deniedActions[resourceName].Intersection(minioIAMPolicy.NewActionSet(action))) == 0 {
|
||||
// It's ok to allow this action
|
||||
newAllowedActions = append(newAllowedActions, action)
|
||||
}
|
||||
}
|
||||
permissions[resourceName] = minioIAMPolicy.NewActionSet(newAllowedActions...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resourcePermissions := map[string][]string{}
|
||||
for key, val := range permissions {
|
||||
var resourceActions []string
|
||||
for _, action := range val.ToSlice() {
|
||||
resourceActions = append(resourceActions, string(action))
|
||||
}
|
||||
resourcePermissions[key] = resourceActions
|
||||
|
||||
}
|
||||
rawPolicy, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
@@ -114,7 +186,7 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
|
||||
Status: models.SessionResponseStatusOk,
|
||||
Operator: false,
|
||||
DistributedMode: isErasureMode(),
|
||||
Policy: sessionPolicy,
|
||||
Permissions: resourcePermissions,
|
||||
}
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
@@ -2363,8 +2363,6 @@ definitions:
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
manage:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
minLength: 3
|
||||
@@ -2411,10 +2409,6 @@ definitions:
|
||||
- hard
|
||||
creation_date:
|
||||
type: string
|
||||
allowedActions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
bucketEncryptionRequest:
|
||||
type: object
|
||||
@@ -3128,9 +3122,12 @@ definitions:
|
||||
type: boolean
|
||||
distributedMode:
|
||||
type: boolean
|
||||
policy:
|
||||
permissions:
|
||||
type: object
|
||||
$ref: "#/definitions/iamPolicy"
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
widgetResult:
|
||||
type: object
|
||||
|
||||
Reference in New Issue
Block a user