github.com/minio/console@v1.4.1/web-app/src/screens/Console/Account/EditServiceAccount.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2022 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, { useEffect, useState, Fragment } from "react";
    18  import {
    19    Box,
    20    Button,
    21    ChangeAccessPolicyIcon,
    22    DateTimeInput,
    23    Grid,
    24    InputBox,
    25    Switch,
    26  } from "mds";
    27  import { api } from "api";
    28  import { errorToHandler } from "api/errors";
    29  import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
    30  import { ApiError } from "api/consoleApi";
    31  import { useAppDispatch } from "store";
    32  import { encodeURLString } from "common/utils";
    33  import { setErrorSnackMessage, setModalErrorSnackMessage } from "systemSlice";
    34  import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
    35  import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
    36  import { DateTime } from "luxon";
    37  
    38  interface IServiceAccountPolicyProps {
    39    open: boolean;
    40    selectedAccessKey: string | null;
    41    closeModalAndRefresh: () => void;
    42  }
    43  
    44  const EditServiceAccount = ({
    45    open,
    46    selectedAccessKey,
    47    closeModalAndRefresh,
    48  }: IServiceAccountPolicyProps) => {
    49    const dispatch = useAppDispatch();
    50    const [loading, setLoading] = useState<boolean>(false);
    51    const [policyDefinition, setPolicyDefinition] = useState<any>("");
    52  
    53    const [name, setName] = useState<string>("");
    54    const [description, setDescription] = useState<string>("");
    55    const [expiry, setExpiry] = useState<any>();
    56    const [status, setStatus] = useState<string | undefined>("enabled");
    57  
    58    useEffect(() => {
    59      if (!loading && selectedAccessKey !== "") {
    60        const sourceAccKey = encodeURLString(selectedAccessKey);
    61        setLoading(true);
    62        api.serviceAccounts
    63          .getServiceAccount(sourceAccKey)
    64          .then((res) => {
    65            setLoading(false);
    66            const saInfo = res.data;
    67  
    68            setName(saInfo?.name || "");
    69  
    70            if (saInfo?.expiration) {
    71              setExpiry(DateTime.fromISO(saInfo?.expiration));
    72            }
    73  
    74            setDescription(saInfo?.description || "");
    75            setStatus(saInfo.accountStatus);
    76  
    77            setPolicyDefinition(saInfo.policy || "");
    78          })
    79          .catch((err) => {
    80            setLoading(false);
    81            dispatch(setModalErrorSnackMessage(errorToHandler(err)));
    82          });
    83      }
    84      // eslint-disable-next-line react-hooks/exhaustive-deps
    85    }, [selectedAccessKey]);
    86  
    87    const setPolicy = (event: React.FormEvent, newPolicy: string) => {
    88      event.preventDefault();
    89      api.serviceAccounts
    90        .updateServiceAccount(encodeURLString(selectedAccessKey), {
    91          policy: newPolicy,
    92          description: description,
    93          expiry: expiry,
    94          name: name,
    95          status: status,
    96        })
    97        .then(() => {
    98          closeModalAndRefresh();
    99        })
   100        .catch(async (res) => {
   101          const err = (await res.json()) as ApiError;
   102          dispatch(setErrorSnackMessage(errorToHandler(err)));
   103        });
   104    };
   105  
   106    return (
   107      <ModalWrapper
   108        title={`Edit details of - ${selectedAccessKey}`}
   109        modalOpen={open}
   110        onClose={() => {
   111          closeModalAndRefresh();
   112        }}
   113        titleIcon={<ChangeAccessPolicyIcon />}
   114      >
   115        <form
   116          noValidate
   117          autoComplete="off"
   118          onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
   119            setPolicy(e, policyDefinition);
   120          }}
   121        >
   122          <Grid container>
   123            <Grid item xs={12}>
   124              <CodeMirrorWrapper
   125                label={`Access Key Policy`}
   126                value={policyDefinition}
   127                onChange={(value) => {
   128                  setPolicyDefinition(value);
   129                }}
   130                editorHeight={"350px"}
   131                helptip={
   132                  <Fragment>
   133                    <a
   134                      target="blank"
   135                      href="https://min.io/docs/minio/kubernetes/upstream/administration/identity-access-management/policy-based-access-control.html#policy-document-structure"
   136                    >
   137                      Guide to access policy structure
   138                    </a>
   139                  </Fragment>
   140                }
   141              />
   142            </Grid>
   143            <Box
   144              sx={{
   145                marginBottom: "15px",
   146                marginTop: "15px",
   147                display: "flex",
   148                width: "100%",
   149                "& label": { width: "195px" },
   150              }}
   151            >
   152              <DateTimeInput
   153                noLabelMinWidth
   154                value={expiry}
   155                onChange={(e) => {
   156                  setExpiry(e);
   157                }}
   158                id="expiryTime"
   159                label={"Expiry"}
   160                timeFormat={"24h"}
   161                secondsSelector={false}
   162              />
   163            </Box>
   164            <Grid
   165              xs={12}
   166              sx={{
   167                marginBottom: "15px",
   168              }}
   169            >
   170              <InputBox
   171                value={name}
   172                size={120}
   173                label={"Name"}
   174                id={"name"}
   175                name={"name"}
   176                type={"text"}
   177                placeholder={"Enter a name"}
   178                onChange={(e) => {
   179                  setName(e.target.value);
   180                }}
   181              />
   182            </Grid>
   183            <Grid
   184              xs={12}
   185              sx={{
   186                marginBottom: "15px",
   187              }}
   188            >
   189              <InputBox
   190                size={120}
   191                value={description}
   192                label={"Description"}
   193                id={"description"}
   194                name={"description"}
   195                type={"text"}
   196                placeholder={"Enter a description"}
   197                onChange={(e) => {
   198                  setDescription(e.target.value);
   199                }}
   200              />
   201            </Grid>
   202            <Grid
   203              xs={12}
   204              sx={{
   205                display: "flex",
   206                alignItems: "center",
   207                justifyContent: "start",
   208                fontWeight: 600,
   209                color: "rgb(7, 25, 62)",
   210                gap: 2,
   211                marginBottom: "15px",
   212              }}
   213            >
   214              <label style={{ width: "150px" }}>Status</label>
   215              <Box
   216                sx={{
   217                  padding: "2px",
   218                }}
   219              >
   220                <Switch
   221                  style={{
   222                    gap: "115px",
   223                  }}
   224                  indicatorLabels={["Enabled", "Disabled"]}
   225                  checked={status === "on"}
   226                  id="saStatus"
   227                  name="saStatus"
   228                  label=""
   229                  onChange={(e) => {
   230                    setStatus(e.target.checked ? "on" : "off");
   231                  }}
   232                  value="yes"
   233                />
   234              </Box>
   235            </Grid>
   236            <Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
   237              <Button
   238                id={"cancel-sa-policy"}
   239                type="button"
   240                variant="regular"
   241                onClick={() => {
   242                  closeModalAndRefresh();
   243                }}
   244                disabled={loading}
   245                label={"Cancel"}
   246              />
   247              <Button
   248                id={"save-sa-policy"}
   249                type="submit"
   250                variant="callAction"
   251                color="primary"
   252                disabled={loading}
   253                label={"Update"}
   254              />
   255            </Grid>
   256          </Grid>
   257        </form>
   258      </ModalWrapper>
   259    );
   260  };
   261  
   262  export default EditServiceAccount;