github.com/minio/console@v1.4.1/web-app/src/screens/Console/Support/GetApiKeyModal.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, { useState } from "react";
    18  import { Box, FormLayout, InfoIcon, InputBox, LockIcon, UsersIcon } from "mds";
    19  import { useAppDispatch } from "../../../store";
    20  import { setErrorSnackMessage } from "../../../systemSlice";
    21  import ConfirmDialog from "../Common/ModalWrapper/ConfirmDialog";
    22  import { api } from "api";
    23  import {
    24    ApiError,
    25    ApiKey,
    26    HttpResponse,
    27    SubnetLoginResponse,
    28  } from "api/consoleApi";
    29  import { errorToHandler } from "api/errors";
    30  
    31  interface IGetApiKeyModalProps {
    32    open: boolean;
    33    closeModal: () => void;
    34    onSet: (apiKey: string) => void;
    35  }
    36  
    37  const GetApiKeyModal = ({ open, closeModal, onSet }: IGetApiKeyModalProps) => {
    38    const dispatch = useAppDispatch();
    39    const [email, setEmail] = useState<string>("");
    40    const [password, setPassword] = useState("");
    41    const [mfaToken, setMfaToken] = useState("");
    42    const [subnetOTP, setSubnetOTP] = useState("");
    43    const [loadingSave, setLoadingSave] = useState<boolean>(false);
    44  
    45    const onError = (err: ApiError) => {
    46      dispatch(setErrorSnackMessage(errorToHandler(err)));
    47      closeModal();
    48      setEmail("");
    49      setPassword("");
    50      setMfaToken("");
    51      setSubnetOTP("");
    52    };
    53  
    54    const onConfirm = () => {
    55      if (mfaToken !== "") {
    56        submitSubnetMfa();
    57      } else {
    58        submitSubnetLogin();
    59      }
    60    };
    61  
    62    const submitSubnetMfa = () => {
    63      setLoadingSave(true);
    64      api.subnet
    65        .subnetLoginMfa({
    66          username: email,
    67          otp: subnetOTP,
    68          mfa_token: mfaToken,
    69        })
    70        .then((res: HttpResponse<SubnetLoginResponse, ApiError>) => {
    71          if (res.data && res.data.access_token) {
    72            getApiKey(res.data.access_token);
    73          }
    74        })
    75        .catch((res: HttpResponse<SubnetLoginResponse, ApiError>) => {
    76          onError(res.error);
    77        })
    78        .finally(() => setLoadingSave(false));
    79    };
    80  
    81    const getApiKey = (access_token: string) => {
    82      setLoadingSave(true);
    83      api.subnet
    84        .subnetApiKey({
    85          token: access_token,
    86        })
    87        .then((res: HttpResponse<ApiKey, ApiError>) => {
    88          if (res.data && res.data.apiKey) {
    89            onSet(res.data.apiKey);
    90            closeModal();
    91          }
    92        })
    93        .catch((res: HttpResponse<SubnetLoginResponse, ApiError>) => {
    94          onError(res.error);
    95        })
    96        .finally(() => setLoadingSave(false));
    97    };
    98  
    99    const submitSubnetLogin = () => {
   100      setLoadingSave(true);
   101      api.subnet
   102        .subnetLogin({ username: email, password })
   103        .then((res: HttpResponse<SubnetLoginResponse, ApiError>) => {
   104          if (res.data && res.data.mfa_token) {
   105            setMfaToken(res.data.mfa_token);
   106          }
   107        })
   108        .catch((res: HttpResponse<SubnetLoginResponse, ApiError>) => {
   109          onError(res.error);
   110        })
   111        .finally(() => setLoadingSave(false));
   112    };
   113  
   114    const getDialogContent = () => {
   115      if (mfaToken === "") {
   116        return getCredentialsDialog();
   117      }
   118      return getMFADialog();
   119    };
   120  
   121    const getCredentialsDialog = () => {
   122      return (
   123        <FormLayout withBorders={false} containerPadding={false}>
   124          <InputBox
   125            id="subnet-email"
   126            name="subnet-email"
   127            onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
   128              setEmail(event.target.value)
   129            }
   130            label="Email"
   131            value={email}
   132            overlayIcon={<UsersIcon />}
   133          />
   134          <InputBox
   135            id="subnet-password"
   136            name="subnet-password"
   137            onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
   138              setPassword(event.target.value)
   139            }
   140            label="Password"
   141            type={"password"}
   142            value={password}
   143          />
   144        </FormLayout>
   145      );
   146    };
   147  
   148    const getMFADialog = () => {
   149      return (
   150        <Box sx={{ display: "flex" }}>
   151          <Box sx={{ display: "flex", flexFlow: "column", flex: "2" }}>
   152            <Box
   153              sx={{
   154                fontSize: 14,
   155                display: "flex",
   156                flexFlow: "column",
   157                marginTop: 20,
   158                marginBottom: 20,
   159              }}
   160            >
   161              Two-Factor Authentication
   162            </Box>
   163  
   164            <Box>
   165              Please enter the 6-digit verification code that was sent to your
   166              email address. This code will be valid for 5 minutes.
   167            </Box>
   168  
   169            <Box
   170              sx={{
   171                flex: "1",
   172                marginTop: "30px",
   173              }}
   174            >
   175              <InputBox
   176                overlayIcon={<LockIcon />}
   177                id="subnet-otp"
   178                name="subnet-otp"
   179                onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
   180                  setSubnetOTP(event.target.value)
   181                }
   182                placeholder=""
   183                label=""
   184                value={subnetOTP}
   185              />
   186            </Box>
   187          </Box>
   188        </Box>
   189      );
   190    };
   191  
   192    return open ? (
   193      <ConfirmDialog
   194        title={"Get API Key from SUBNET"}
   195        confirmText={"Get API Key"}
   196        isOpen={open}
   197        titleIcon={<InfoIcon />}
   198        isLoading={loadingSave}
   199        cancelText={"Cancel"}
   200        onConfirm={onConfirm}
   201        onClose={closeModal}
   202        confirmButtonProps={{
   203          variant: "callAction",
   204          disabled: !email || !password || loadingSave,
   205          hidden: true,
   206        }}
   207        cancelButtonProps={{
   208          disabled: loadingSave,
   209        }}
   210        confirmationContent={getDialogContent()}
   211      />
   212    ) : null;
   213  };
   214  
   215  export default GetApiKeyModal;