github.com/minio/console@v1.4.1/web-app/src/screens/Console/Policies/ListPolicies.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 {
    19    AddIcon,
    20    Button,
    21    DataTable,
    22    Grid,
    23    HelpBox,
    24    IAMPoliciesIcon,
    25    PageLayout,
    26  } from "mds";
    27  import { useNavigate } from "react-router-dom";
    28  import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
    29  import { ErrorResponseHandler } from "../../../common/types";
    30  import {
    31    CONSOLE_UI_RESOURCE,
    32    createPolicyPermissions,
    33    deletePolicyPermissions,
    34    IAM_PAGES,
    35    IAM_SCOPES,
    36    listPolicyPermissions,
    37    permissionTooltipHelper,
    38    viewPolicyPermissions,
    39  } from "../../../common/SecureComponent/permissions";
    40  import {
    41    hasPermission,
    42    SecureComponent,
    43  } from "../../../common/SecureComponent";
    44  import { Policy } from "../../../api/consoleApi";
    45  import { encodeURLString } from "../../../common/utils";
    46  import { setErrorSnackMessage, setHelpName } from "../../../systemSlice";
    47  import { useAppDispatch } from "../../../store";
    48  import { api } from "../../../api";
    49  import SearchBox from "../Common/SearchBox";
    50  import withSuspense from "../Common/Components/withSuspense";
    51  import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
    52  import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
    53  import HelpMenu from "../HelpMenu";
    54  
    55  const DeletePolicy = withSuspense(React.lazy(() => import("./DeletePolicy")));
    56  
    57  const ListPolicies = () => {
    58    const dispatch = useAppDispatch();
    59    const navigate = useNavigate();
    60  
    61    const [records, setRecords] = useState<Policy[]>([]);
    62    const [loading, setLoading] = useState<boolean>(false);
    63    const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
    64    const [selectedPolicy, setSelectedPolicy] = useState<string>("");
    65    const [filterPolicies, setFilterPolicies] = useState<string>("");
    66    const viewPolicy = hasPermission(CONSOLE_UI_RESOURCE, [
    67      IAM_SCOPES.ADMIN_GET_POLICY,
    68    ]);
    69  
    70    const canDeletePolicy = hasPermission(
    71      CONSOLE_UI_RESOURCE,
    72      deletePolicyPermissions,
    73    );
    74  
    75    const canDisplayPolicies = hasPermission(
    76      CONSOLE_UI_RESOURCE,
    77      listPolicyPermissions,
    78    );
    79  
    80    const canCreatePolicy = hasPermission(
    81      CONSOLE_UI_RESOURCE,
    82      createPolicyPermissions,
    83    );
    84  
    85    const canViewPolicy = hasPermission(
    86      CONSOLE_UI_RESOURCE,
    87      viewPolicyPermissions,
    88    );
    89  
    90    useEffect(() => {
    91      fetchRecords();
    92    }, []);
    93  
    94    useEffect(() => {
    95      if (loading) {
    96        if (canDisplayPolicies) {
    97          api.policies
    98            .listPolicies()
    99            .then((res) => {
   100              const policies = res.data.policies ?? [];
   101  
   102              policies.sort((pa, pb) => {
   103                if (pa.name! > pb.name!) {
   104                  return 1;
   105                }
   106  
   107                if (pa.name! < pb.name!) {
   108                  return -1;
   109                }
   110  
   111                return 0;
   112              });
   113  
   114              setLoading(false);
   115              setRecords(policies);
   116            })
   117            .catch((err: ErrorResponseHandler) => {
   118              setLoading(false);
   119              dispatch(setErrorSnackMessage(err));
   120            });
   121        } else {
   122          setLoading(false);
   123        }
   124      }
   125    }, [loading, setLoading, setRecords, dispatch, canDisplayPolicies]);
   126  
   127    const fetchRecords = () => {
   128      setLoading(true);
   129    };
   130  
   131    const closeDeleteModalAndRefresh = (refresh: boolean) => {
   132      setDeleteOpen(false);
   133  
   134      if (refresh) {
   135        fetchRecords();
   136      }
   137    };
   138  
   139    const confirmDeletePolicy = (policy: string) => {
   140      setDeleteOpen(true);
   141      setSelectedPolicy(policy);
   142    };
   143  
   144    const viewAction = (policy: any) => {
   145      navigate(`${IAM_PAGES.POLICIES}/${encodeURLString(policy.name)}`);
   146    };
   147  
   148    const tableActions = [
   149      {
   150        type: "view",
   151        onClick: viewAction,
   152        disableButtonFunction: () => !viewPolicy,
   153      },
   154      {
   155        type: "delete",
   156        onClick: confirmDeletePolicy,
   157        sendOnlyId: true,
   158        disableButtonFunction: () => !canDeletePolicy,
   159      },
   160    ];
   161  
   162    const filteredRecords = records.filter((elementItem) =>
   163      elementItem.name?.includes(filterPolicies),
   164    );
   165  
   166    useEffect(() => {
   167      dispatch(setHelpName("list_policies"));
   168      // eslint-disable-next-line react-hooks/exhaustive-deps
   169    }, []);
   170  
   171    return (
   172      <Fragment>
   173        {deleteOpen && (
   174          <DeletePolicy
   175            deleteOpen={deleteOpen}
   176            selectedPolicy={selectedPolicy}
   177            closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
   178          />
   179        )}
   180        <PageHeaderWrapper label="IAM Policies" actions={<HelpMenu />} />
   181  
   182        <PageLayout>
   183          <Grid container>
   184            <Grid item xs={12} sx={actionsTray.actionsTray}>
   185              <SearchBox
   186                onChange={setFilterPolicies}
   187                placeholder="Search Policies"
   188                value={filterPolicies}
   189                sx={{ maxWidth: 380 }}
   190              />
   191  
   192              <SecureComponent
   193                scopes={[IAM_SCOPES.ADMIN_CREATE_POLICY]}
   194                resource={CONSOLE_UI_RESOURCE}
   195                errorProps={{ disabled: true }}
   196              >
   197                <TooltipWrapper
   198                  tooltip={
   199                    canCreatePolicy
   200                      ? ""
   201                      : permissionTooltipHelper(
   202                          createPolicyPermissions,
   203                          "create a Policy",
   204                        )
   205                  }
   206                >
   207                  <Button
   208                    id={"create-policy"}
   209                    label={"Create Policy"}
   210                    variant="callAction"
   211                    icon={<AddIcon />}
   212                    onClick={() => {
   213                      navigate(`${IAM_PAGES.POLICY_ADD}`);
   214                    }}
   215                    disabled={!canCreatePolicy}
   216                  />
   217                </TooltipWrapper>
   218              </SecureComponent>
   219            </Grid>
   220            <Grid item xs={12}>
   221              <SecureComponent
   222                scopes={[IAM_SCOPES.ADMIN_LIST_USER_POLICIES]}
   223                resource={CONSOLE_UI_RESOURCE}
   224                errorProps={{ disabled: true }}
   225              >
   226                <TooltipWrapper
   227                  tooltip={
   228                    canViewPolicy
   229                      ? ""
   230                      : permissionTooltipHelper(
   231                          viewPolicyPermissions,
   232                          "view Policy details",
   233                        )
   234                  }
   235                >
   236                  <DataTable
   237                    itemActions={tableActions}
   238                    columns={[{ label: "Name", elementKey: "name" }]}
   239                    isLoading={loading}
   240                    records={filteredRecords}
   241                    entityName="Policies"
   242                    idField="name"
   243                  />
   244                </TooltipWrapper>
   245              </SecureComponent>
   246            </Grid>
   247            <Grid item xs={12} sx={{ marginTop: 15 }}>
   248              <HelpBox
   249                title={"Learn more about IAM POLICIES"}
   250                iconComponent={<IAMPoliciesIcon />}
   251                help={
   252                  <Fragment>
   253                    MinIO uses Policy-Based Access Control (PBAC) to define the
   254                    authorized actions and resources to which an authenticated
   255                    user has access. Each policy describes one or more actions and
   256                    conditions that outline the permissions of a user or group of
   257                    users.
   258                    <br />
   259                    <br />
   260                    MinIO PBAC is built for compatibility with AWS IAM policy
   261                    syntax, structure, and behavior. The MinIO documentation makes
   262                    a best-effort to cover IAM-specific behavior and
   263                    functionality. Consider deferring to the IAM documentation for
   264                    more complete documentation on AWS IAM-specific topics.
   265                    <br />
   266                    <br />
   267                    You can learn more at our{" "}
   268                    <a
   269                      href="https://min.io/docs/minio/linux/administration/identity-access-management.html?ref=con#access-management"
   270                      target="_blank"
   271                      rel="noopener"
   272                    >
   273                      documentation
   274                    </a>
   275                    .
   276                  </Fragment>
   277                }
   278              />
   279            </Grid>
   280          </Grid>
   281        </PageLayout>
   282      </Fragment>
   283    );
   284  };
   285  
   286  export default ListPolicies;