codekingpro/portable-devtools
114k
1/////////////////////////////////////////////////////////////2//3// pgAdmin 4 - PostgreSQL Tools4//5// Copyright (C) 2013 - 2024, The pgAdmin Development Team6// This software is released under the PostgreSQL Licence7//8//////////////////////////////////////////////////////////////9import React, { useState } from 'react';10import LoginImage from '../../img/login.svg?svgr';11import { InputSelect, InputText, MESSAGE_TYPE, NotifierMessage } from '../components/FormComponents';12import BasePage, { SecurityButton } from './BasePage';13import { useDelayedCaller } from '../custom_hooks';14import gettext from 'sources/gettext';15import PropTypes from 'prop-types';16 17function EmailValidateView({mfaView, sendEmailUrl, csrfHeader, csrfToken}) {18 const [inputValue, setInputValue] = useState('');19 const [error, setError] = useState('');20 const [sentMessage, setSentMessage] = useState('');21 const [sending, setSending] = useState(false);22 const [showResend, setShowResend] = useState(false);23 24 const showResendAfter = useDelayedCaller(()=>{25 setShowResend(true);26 });27 28 const sendCodeToEmail = ()=>{29 setSending(true);30 let accept = 'text/html; charset=utf-8;';31 32 fetch(sendEmailUrl, {33 method: 'POST',34 mode: 'cors',35 cache: 'no-cache',36 headers: {37 'Accept': accept,38 'Content-Type': 'application/json; charset=utf-8;',39 [csrfHeader]: csrfToken,40 },41 redirect: 'follow'42 }).then((resp) => {43 if (!resp.ok) {44 resp.text().then(msg => setError(msg));45 return;46 }47 return resp.json();48 }).then((resp) => {49 if (!resp)50 return;51 setSentMessage(resp.message);52 showResendAfter(20000);53 }).finally(()=>{54 setSending(false);55 });56 };57 58 return <>59 <div style={{textAlign: 'center'}} data-test="email-validate-view">{mfaView.description}</div>60 {sentMessage && <>61 <div>{sentMessage}</div>62 {showResend && <div>63 <span>{gettext('Haven\'t received an email?')} <a style={{color:'inherit', fontWeight: 'bold'}} href="#" onClick={sendCodeToEmail}>{gettext('Send again')}</a></span>64 </div>}65 <InputText value={inputValue} type="password" name="code" placeholder={mfaView.otp_placeholder}66 onChange={setInputValue} autoFocus67 />68 <SecurityButton value='Validate'>{gettext('Validate')}</SecurityButton>69 </>}70 {error && <NotifierMessage message={error} type={MESSAGE_TYPE.ERROR} closable={false} />}71 {!sentMessage &&72 <SecurityButton type="button" name="send_code" onClick={sendCodeToEmail} disabled={sending}>73 {sending ? mfaView.button_label_sending : mfaView.button_label}74 </SecurityButton>}75 </>;76}77 78EmailValidateView.propTypes = {79 mfaView: PropTypes.object,80 sendEmailUrl: PropTypes.string,81 csrfHeader: PropTypes.string,82 csrfToken: PropTypes.string83};84 85function AuthenticatorValidateView({mfaView}) {86 const [inputValue, setInputValue] = useState('');87 88 return <>89 <div data-test="auth-validate-view">{mfaView.auth_description}</div>90 <InputText value={inputValue} type="password" name="code" placeholder={mfaView.otp_placeholder}91 onChange={setInputValue} autoFocus92 />93 <SecurityButton value='Validate'>{gettext('Validate')}</SecurityButton>94 </>;95}96 97AuthenticatorValidateView.propTypes = {98 mfaView: PropTypes.object,99};100 101export default function MfaValidatePage({actionUrl, views, logoutUrl, sendEmailUrl, csrfHeader, csrfToken, ...props}) {102 const [method, setMethod] = useState(Object.values(views).find((v)=>v.selected)?.id);103 return (104 <BasePage title={gettext('Authentication')} pageImage={<LoginImage style={{height: '100%', width: '100%'}} />} {...props}>105 <form style={{display:'flex', gap:'15px', flexDirection:'column', minHeight: 0}} action={actionUrl} method="POST">106 <InputSelect value={method} options={Object.keys(views).map((k)=>({107 label: views[k].label,108 value: views[k].id,109 imageUrl: views[k].icon110 }))} onChange={setMethod} controlProps={{111 allowClear: false,112 }} />113 <div><input type='hidden' name='mfa_method' defaultValue={method} /></div>114 {method == 'email' && <EmailValidateView mfaView={views[method].view} sendEmailUrl={sendEmailUrl} csrfHeader={csrfHeader} csrfToken={csrfToken} />}115 {method == 'authenticator' && <AuthenticatorValidateView mfaView={views[method].view} />}116 <div style={{textAlign: 'right'}}>117 <a style={{color:'inherit'}} href={logoutUrl}>{gettext('Logout')}</a>118 </div>119 </form>120 </BasePage>121 );122}123 124MfaValidatePage.propTypes = {125 actionUrl: PropTypes.string,126 views: PropTypes.object,127 logoutUrl: PropTypes.string,128 sendEmailUrl: PropTypes.string,129 csrfHeader: PropTypes.string,130 csrfToken: PropTypes.string131};132 