forked from freeipa/freeipa-webui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The 'sync OTP Token' link must redirect to a page to allow syncing the OTP token keys as in the current WebUI. Signed-off-by: Carla Martinez <[email protected]>
- Loading branch information
Showing
4 changed files
with
274 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import React from "react"; | ||
// PatternFly | ||
import { | ||
Form, | ||
FormGroup, | ||
TextInput, | ||
ActionGroup, | ||
Button, | ||
LoginPage, | ||
ListVariant, | ||
} from "@patternfly/react-core"; | ||
// Images | ||
import BrandImg from "src/assets/images/product-name.png"; | ||
import BackgroundImg from "src/assets/images/login-screen-background.jpg"; | ||
// Hooks | ||
import useAlerts from "src/hooks/useAlerts"; | ||
// RPC | ||
import { | ||
MetaResponse, | ||
SyncOtpPayload, | ||
useSyncOtpMutation, | ||
} from "src/services/rpcAuth"; | ||
// React router DOM | ||
import { useNavigate } from "react-router-dom"; | ||
// Components | ||
import PasswordInput from "src/components/layouts/PasswordInput"; | ||
|
||
const SyncOtpPage = () => { | ||
// Navigate | ||
const navigate = useNavigate(); | ||
|
||
// Alerts to show in the UI | ||
const alerts = useAlerts(); | ||
|
||
// API calls | ||
const [syncOtpToken] = useSyncOtpMutation(); | ||
|
||
// Main states | ||
const [uid, setUid] = React.useState<string>(""); | ||
const [password, setPassword] = React.useState<string>(""); | ||
const [firstOtp, setFirstOtp] = React.useState<string>(""); | ||
const [secondOtp, setSecondOtp] = React.useState<string>(""); | ||
const [tokenId, setTokenId] = React.useState<string>(""); | ||
const [btnSpinning, setBtnSpinning] = React.useState<boolean>(false); | ||
|
||
// Visibility of fields | ||
const [passwordHidden, setPasswordHidden] = React.useState<boolean>(true); | ||
const [newFirstOtpHidden, setFirstOtpHidden] = React.useState<boolean>(true); | ||
const [secondOtpHidden, setSecondOtpHidden] = React.useState<boolean>(true); | ||
|
||
// Sync button should be disabled if some conditions are met | ||
const evaluateSyncButtonDisabled = () => { | ||
if ( | ||
uid === undefined || | ||
password === "" || | ||
firstOtp === "" || | ||
secondOtp === "" | ||
) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
|
||
const isSyncButtonDisabled = evaluateSyncButtonDisabled(); | ||
|
||
// Clear fields when the sync operation failed | ||
const clearFields = () => { | ||
setUid(""); | ||
setPassword(""); | ||
setFirstOtp(""); | ||
setSecondOtp(""); | ||
setTokenId(""); | ||
}; | ||
|
||
// Sync API call | ||
const onSyncOtp = () => { | ||
const payload: SyncOtpPayload = { | ||
user: uid, | ||
password: password, | ||
first_code: firstOtp, | ||
second_code: secondOtp, | ||
token: tokenId, | ||
}; | ||
|
||
setBtnSpinning(true); | ||
|
||
syncOtpToken(payload).then((response) => { | ||
if ("error" in response) { | ||
const receivedError = response.error as MetaResponse; | ||
const reason = receivedError.response?.headers.get( | ||
"x-ipa-tokensync-result" | ||
); | ||
|
||
if (reason === "invalid-credentials") { | ||
alerts.addAlert( | ||
"sync-otp-error", | ||
"Token sync rejected. The username, password or token codes are not correct.", | ||
"danger" | ||
); | ||
clearFields(); | ||
} else if (reason === "ok") { | ||
alerts.addAlert( | ||
"sync-otp-success", | ||
"OTP token synced successfully", | ||
"success" | ||
); | ||
navigate("/login", { | ||
replace: true, | ||
state: { | ||
alertMessage: "OTP token synced successfully", | ||
}, | ||
}); | ||
} | ||
setBtnSpinning(false); | ||
} | ||
}); | ||
}; | ||
|
||
// Form fields | ||
const formFields = ( | ||
<Form isHorizontal> | ||
<FormGroup label="Username" fieldId="username" required> | ||
<TextInput | ||
id="username" | ||
name="user" | ||
type="text" | ||
value={uid} | ||
onChange={(_ev, newUid) => setUid(newUid)} | ||
isRequired={true} | ||
/> | ||
</FormGroup> | ||
<FormGroup label="Password" fieldId="password" required> | ||
<PasswordInput | ||
id="form-password" | ||
name="password" | ||
value={password} | ||
onChange={setPassword} | ||
onRevealHandler={setPasswordHidden} | ||
passwordHidden={passwordHidden} | ||
isRequired={true} | ||
/> | ||
</FormGroup> | ||
<FormGroup label="First OTP" fieldId="firstotp" required> | ||
<PasswordInput | ||
id="form-first-otp" | ||
name="first_code" | ||
value={firstOtp} | ||
onChange={setFirstOtp} | ||
onRevealHandler={setFirstOtpHidden} | ||
passwordHidden={newFirstOtpHidden} | ||
isRequired={true} | ||
/> | ||
</FormGroup> | ||
<FormGroup label="Second OTP" fieldId="secondotp" required> | ||
<PasswordInput | ||
id="form-second-otp" | ||
name="second_code" | ||
value={secondOtp} | ||
onChange={setSecondOtp} | ||
onRevealHandler={setSecondOtpHidden} | ||
passwordHidden={secondOtpHidden} | ||
isRequired={true} | ||
/> | ||
</FormGroup> | ||
<FormGroup label="Token ID" fieldId="tokenid"> | ||
<TextInput | ||
id="form-token-id" | ||
name="token" | ||
value={tokenId} | ||
onChange={(_ev, newToken) => setTokenId(newToken)} | ||
/> | ||
</FormGroup> | ||
<ActionGroup> | ||
<Button variant="link" onClick={() => navigate(-1)}> | ||
Cancel | ||
</Button> | ||
<Button | ||
variant="primary" | ||
isDisabled={isSyncButtonDisabled || btnSpinning} | ||
onClick={onSyncOtp} | ||
isLoading={btnSpinning} | ||
> | ||
{btnSpinning ? "Syncing OTP token " : "Sync OTP token"} | ||
</Button> | ||
</ActionGroup> | ||
</Form> | ||
); | ||
|
||
return ( | ||
<> | ||
<alerts.ManagedAlerts /> | ||
<LoginPage | ||
style={{ whiteSpace: "pre-line" }} | ||
footerListVariants={ListVariant.inline} | ||
brandImgSrc={BrandImg} | ||
brandImgAlt="FreeIPA logo" | ||
backgroundImgSrc={BackgroundImg} | ||
textContent={ | ||
"OTP (One-Time Password): Generate new OTP code for each OTP field." | ||
} | ||
loginTitle="Sync OTP token" | ||
> | ||
{formFields} | ||
</LoginPage> | ||
</> | ||
); | ||
}; | ||
|
||
export default SyncOtpPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters