show/hide key

This commit is contained in:
Sebastian 2023-03-13 00:24:28 -03:00
parent a1af7945d1
commit 3267f56dc3
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
4 changed files with 65 additions and 16 deletions

View File

@ -23,7 +23,7 @@ import { InputProps, useField } from "./useField.js";
export interface Props<T> extends InputProps<T> { export interface Props<T> extends InputProps<T> {
expand?: boolean; expand?: boolean;
inputType?: "text" | "number"; inputType?: "text" | "number" | "password";
addonBefore?: ComponentChildren; addonBefore?: ComponentChildren;
addonAfter?: ComponentChildren; addonAfter?: ComponentChildren;
toStr?: (v?: any) => string; toStr?: (v?: any) => string;

View File

@ -20,7 +20,7 @@
*/ */
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { ComponentChildren, h, VNode } from "preact"; import { ComponentChildren, Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { useInstanceContext } from "../../context/instance.js"; import { useInstanceContext } from "../../context/instance.js";
import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js"; import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
@ -65,16 +65,25 @@ export function ConfirmModal({
<section class="modal-card-body">{children}</section> <section class="modal-card-body">{children}</section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<div class="buttons is-right" style={{ width: "100%" }}> <div class="buttons is-right" style={{ width: "100%" }}>
<button class="button " onClick={onCancel}> {onConfirm ? (
<i18n.Translate>Cancel</i18n.Translate> <Fragment>
</button> <button class="button " onClick={onCancel}>
<button <i18n.Translate>Cancel</i18n.Translate>
class={danger ? "button is-danger " : "button is-info "} </button>
disabled={disabled}
onClick={onConfirm} <button
> class={danger ? "button is-danger " : "button is-info "}
<i18n.Translate>{label}</i18n.Translate> disabled={disabled}
</button> onClick={onConfirm}
>
<i18n.Translate>{label}</i18n.Translate>
</button>
</Fragment>
) : (
<button class="button " onClick={onCancel}>
<i18n.Translate>Close</i18n.Translate>
</button>
)}
</div> </div>
</footer> </footer>
</div> </div>

View File

@ -96,20 +96,30 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
: template.pos_algorithm === 2 : template.pos_algorithm === 2
? `otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30` ? `otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
: undefined; : undefined;
const keySlice = template.pos_key?.substring(0, 4);
const oauthUriWithoutSecret = !template.pos_algorithm
? undefined
: template.pos_algorithm === 1
? `otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
: template.pos_algorithm === 2
? `otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
: undefined;
return ( return (
<div> <div>
{oauthUri && ( {oauthUri && (
<ConfirmModal <ConfirmModal
description="Setup TOTP" description="Setup TOTP"
active={setupTOTP} active={setupTOTP}
onConfirm={() => { onCancel={() => {
setSetupTOTP(false); setSetupTOTP(false);
}} }}
> >
<p>Scan this qr code with your TOTP device</p> <p>Scan this qr code with your TOTP device</p>
<QR text={oauthUri} /> <QR text={oauthUri} />
<pre style={{ textAlign: "center" }}> <pre style={{ textAlign: "center" }}>
<a href={oauthUri}>{oauthUri}</a> <a href={oauthUri}>{oauthUriWithoutSecret}</a>
</pre> </pre>
</ConfirmModal> </ConfirmModal>
)} )}

View File

@ -57,6 +57,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
const backend = useBackendContext(); const backend = useBackendContext();
const [showKey, setShowKey] = useState(false);
const [state, setState] = useState<Partial<Entity>>(template); const [state, setState] = useState<Partial<Entity>>(template);
const errors: FormErrors<Entity> = { const errors: FormErrors<Entity> = {
@ -161,14 +162,26 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
convert={(v) => Number(v)} convert={(v) => Number(v)}
/> />
{state.pos_algorithm && state.pos_algorithm > 0 ? ( {state.pos_algorithm && state.pos_algorithm > 0 ? (
<Input<Entity> <InputWithAddon<Entity>
name="pos_key" name="pos_key"
label={i18n.str`Point-of-sale key`} label={i18n.str`Point-of-sale key`}
inputType={showKey ? "text" : "password"}
help="" help=""
expand
tooltip={i18n.str`Useful to validate the purchase`} tooltip={i18n.str`Useful to validate the purchase`}
addonAfter={
<span class="icon">
{showKey ? (
<i class="mdi mdi-eye" />
) : (
<i class="mdi mdi-eye-off" />
)}
</span>
}
side={ side={
<span data-tooltip={i18n.str`generate random secret key`}> <span>
<button <button
data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-3" class="button is-info mr-3"
onClick={(e) => { onClick={(e) => {
const pos_key = randomBase32Key(); const pos_key = randomBase32Key();
@ -177,6 +190,23 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
> >
<i18n.Translate>random</i18n.Translate> <i18n.Translate>random</i18n.Translate>
</button> </button>
<button
data-tooltip={
showKey
? i18n.str`show secret key`
: i18n.str`hide secret key`
}
class="button is-info mr-3"
onClick={(e) => {
setShowKey(!showKey);
}}
>
{showKey ? (
<i18n.Translate>hide</i18n.Translate>
) : (
<i18n.Translate>show</i18n.Translate>
)}
</button>
</span> </span>
} }
/> />