github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/AccessRulePanel.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  import React, { Fragment, useEffect, useState } from "react";
    18  import { AddIcon, Button, DataTable, SectionTitle, HelpTip } from "mds";
    19  import { useSelector } from "react-redux";
    20  import { useParams } from "react-router-dom";
    21  import { api } from "api";
    22  import { AccessRule as IAccessRule } from "api/consoleApi";
    23  import { errorToHandler } from "api/errors";
    24  import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
    25  import {
    26    hasPermission,
    27    SecureComponent,
    28  } from "../../../../common/SecureComponent";
    29  import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice";
    30  import { selBucketDetailsLoading } from "./bucketDetailsSlice";
    31  import { useAppDispatch } from "../../../../store";
    32  import withSuspense from "../../Common/Components/withSuspense";
    33  import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
    34  
    35  const AddAccessRuleModal = withSuspense(
    36    React.lazy(() => import("./AddAccessRule")),
    37  );
    38  const DeleteAccessRuleModal = withSuspense(
    39    React.lazy(() => import("./DeleteAccessRule")),
    40  );
    41  const EditAccessRuleModal = withSuspense(
    42    React.lazy(() => import("./EditAccessRule")),
    43  );
    44  
    45  const AccessRule = () => {
    46    const dispatch = useAppDispatch();
    47    const params = useParams();
    48  
    49    const loadingBucket = useSelector(selBucketDetailsLoading);
    50  
    51    const [loadingAccessRules, setLoadingAccessRules] = useState<boolean>(true);
    52    const [accessRules, setAccessRules] = useState<IAccessRule[] | undefined>([]);
    53    const [addAccessRuleOpen, setAddAccessRuleOpen] = useState<boolean>(false);
    54    const [deleteAccessRuleOpen, setDeleteAccessRuleOpen] =
    55      useState<boolean>(false);
    56    const [accessRuleToDelete, setAccessRuleToDelete] = useState<string>("");
    57    const [editAccessRuleOpen, setEditAccessRuleOpen] = useState<boolean>(false);
    58    const [accessRuleToEdit, setAccessRuleToEdit] = useState<string>("");
    59    const [initialAccess, setInitialAccess] = useState<string>("");
    60  
    61    const bucketName = params.bucketName || "";
    62  
    63    const displayAccessRules = hasPermission(bucketName, [
    64      IAM_SCOPES.S3_GET_BUCKET_POLICY,
    65      IAM_SCOPES.S3_GET_ACTIONS,
    66    ]);
    67  
    68    const deleteAccessRules = hasPermission(bucketName, [
    69      IAM_SCOPES.S3_DELETE_BUCKET_POLICY,
    70    ]);
    71  
    72    const editAccessRules = hasPermission(bucketName, [
    73      IAM_SCOPES.S3_PUT_BUCKET_POLICY,
    74      IAM_SCOPES.S3_PUT_ACTIONS,
    75    ]);
    76  
    77    useEffect(() => {
    78      if (loadingBucket) {
    79        setLoadingAccessRules(true);
    80      }
    81    }, [loadingBucket, setLoadingAccessRules]);
    82  
    83    const AccessRuleActions = [
    84      {
    85        type: "delete",
    86        disableButtonFunction: () => !deleteAccessRules,
    87        onClick: (accessRule: any) => {
    88          setDeleteAccessRuleOpen(true);
    89          setAccessRuleToDelete(accessRule.prefix);
    90        },
    91      },
    92      {
    93        type: "view",
    94        disableButtonFunction: () => !editAccessRules,
    95        onClick: (accessRule: any) => {
    96          setAccessRuleToEdit(accessRule.prefix);
    97          setInitialAccess(accessRule.access);
    98          setEditAccessRuleOpen(true);
    99        },
   100      },
   101    ];
   102  
   103    useEffect(() => {
   104      dispatch(setHelpName("bucket_detail_prefix"));
   105      // eslint-disable-next-line react-hooks/exhaustive-deps
   106    }, []);
   107  
   108    useEffect(() => {
   109      if (loadingAccessRules) {
   110        if (displayAccessRules) {
   111          api.bucket
   112            .listAccessRulesWithBucket(bucketName)
   113            .then((res) => {
   114              setAccessRules(res.data.accessRules);
   115              setLoadingAccessRules(false);
   116            })
   117            .catch((err) => {
   118              dispatch(setErrorSnackMessage(errorToHandler(err)));
   119              setLoadingAccessRules(false);
   120            });
   121        } else {
   122          setLoadingAccessRules(false);
   123        }
   124      }
   125    }, [loadingAccessRules, dispatch, displayAccessRules, bucketName]);
   126  
   127    const closeAddAccessRuleModal = () => {
   128      setAddAccessRuleOpen(false);
   129      setLoadingAccessRules(true);
   130    };
   131  
   132    const closeDeleteAccessRuleModal = () => {
   133      setDeleteAccessRuleOpen(false);
   134      setLoadingAccessRules(true);
   135    };
   136  
   137    const closeEditAccessRuleModal = () => {
   138      setEditAccessRuleOpen(false);
   139      setLoadingAccessRules(true);
   140    };
   141  
   142    return (
   143      <Fragment>
   144        {addAccessRuleOpen && (
   145          <AddAccessRuleModal
   146            modalOpen={addAccessRuleOpen}
   147            onClose={closeAddAccessRuleModal}
   148            bucket={bucketName}
   149          />
   150        )}
   151        {deleteAccessRuleOpen && (
   152          <DeleteAccessRuleModal
   153            modalOpen={deleteAccessRuleOpen}
   154            onClose={closeDeleteAccessRuleModal}
   155            bucket={bucketName}
   156            toDelete={accessRuleToDelete}
   157          />
   158        )}
   159        {editAccessRuleOpen && (
   160          <EditAccessRuleModal
   161            modalOpen={editAccessRuleOpen}
   162            onClose={closeEditAccessRuleModal}
   163            bucket={bucketName}
   164            toEdit={accessRuleToEdit}
   165            initial={initialAccess}
   166          />
   167        )}
   168        <SectionTitle
   169          separator
   170          sx={{ marginBottom: 15 }}
   171          actions={
   172            <SecureComponent
   173              scopes={[
   174                IAM_SCOPES.S3_GET_BUCKET_POLICY,
   175                IAM_SCOPES.S3_PUT_BUCKET_POLICY,
   176                IAM_SCOPES.S3_GET_ACTIONS,
   177                IAM_SCOPES.S3_PUT_ACTIONS,
   178              ]}
   179              resource={bucketName}
   180              matchAll
   181              errorProps={{ disabled: true }}
   182            >
   183              <TooltipWrapper tooltip={"Add Access Rule"}>
   184                <Button
   185                  id={"add-bucket-access-rule"}
   186                  onClick={() => {
   187                    setAddAccessRuleOpen(true);
   188                  }}
   189                  label={"Add Access Rule"}
   190                  icon={<AddIcon />}
   191                  variant={"callAction"}
   192                />
   193              </TooltipWrapper>
   194            </SecureComponent>
   195          }
   196        >
   197          <HelpTip
   198            content={
   199              <Fragment>
   200                Setting an{" "}
   201                <a
   202                  href="https://min.io/docs/minio/linux/reference/minio-mc/mc-anonymous-set.html"
   203                  target="blank"
   204                >
   205                  Anonymous
   206                </a>{" "}
   207                policy allows clients to access the Bucket or prefix contents and
   208                perform actions consistent with the specified policy without
   209                authentication.
   210              </Fragment>
   211            }
   212            placement="right"
   213          >
   214            Anonymous Access
   215          </HelpTip>
   216        </SectionTitle>
   217        <SecureComponent
   218          scopes={[IAM_SCOPES.S3_GET_BUCKET_POLICY, IAM_SCOPES.S3_GET_ACTIONS]}
   219          resource={bucketName}
   220          errorProps={{ disabled: true }}
   221        >
   222          <DataTable
   223            itemActions={AccessRuleActions}
   224            columns={[
   225              {
   226                label: "Prefix",
   227                elementKey: "prefix",
   228                renderFunction: (prefix: string) => {
   229                  return prefix || "/";
   230                },
   231              },
   232              { label: "Access", elementKey: "access" },
   233            ]}
   234            isLoading={loadingAccessRules}
   235            records={accessRules || []}
   236            entityName="Access Rules"
   237            idField="prefix"
   238          />
   239        </SecureComponent>
   240      </Fragment>
   241    );
   242  };
   243  
   244  export default AccessRule;