170 lines
5.5 KiB
TypeScript
170 lines
5.5 KiB
TypeScript
import { Component, JSX, createRef } from "preact";
|
|
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
|
|
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
|
|
import { Form } from "../../../elements/Form";
|
|
import { InputWithTitle } from "../../../elements/InputWithTitle";
|
|
import { Margin } from "../../../elements/Margin";
|
|
import { MarginInline } from "../../../elements/MarginInline";
|
|
import { NewTOTPResp } from "../../../../api/types/totp";
|
|
import { SecretTitleElement } from "../SecretTitleElement";
|
|
import { route } from "preact-router";
|
|
import { setErrorText } from "../../../../pageUtils";
|
|
import i18next from "i18next";
|
|
|
|
export class TOTPNewGeneratedForm extends Component<
|
|
{ baseMount: string } & DefaultPageProps,
|
|
{ exportedData: NewTOTPResp }
|
|
> {
|
|
uriInputRef = createRef<HTMLInputElement>();
|
|
|
|
render(): JSX.Element {
|
|
if (!this.state.exportedData) {
|
|
return (
|
|
<Form onSubmit={(data) => this.onSubmit(data)}>
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("common_name")}>
|
|
<input
|
|
class="uk-input uk-form-width-medium"
|
|
name="name"
|
|
type="text"
|
|
placeholder={i18next.t("common_name")}
|
|
required
|
|
/>
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_issuer")}>
|
|
<input class="uk-input uk-form-width-medium" name="issuer" type="text" required />
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_account_name")}>
|
|
<input
|
|
class="uk-input uk-form-width-medium"
|
|
name="account_name"
|
|
type="text"
|
|
required
|
|
/>
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_algorithm")}>
|
|
<select class="uk-select uk-form-width-medium" name="algorithm">
|
|
{["SHA512", "SHA256", "SHA1"].map((type) => (
|
|
<option label={type} value={type}>
|
|
{type}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_key_size")}>
|
|
<input
|
|
class="uk-input uk-form-width-medium"
|
|
name="key_size"
|
|
type="number"
|
|
value="20"
|
|
/>
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_period")}>
|
|
<input class="uk-input uk-form-width-medium" name="period" type="text" value="30s" />
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_digits")}>
|
|
<input class="uk-input uk-form-width-medium" name="digits" type="number" value="6" />
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<Margin>
|
|
<InputWithTitle title={i18next.t("totp_new_generated_export")}>
|
|
<input class="uk-checkbox" name="exported" type="checkbox" value="yes" checked />
|
|
</InputWithTitle>
|
|
</Margin>
|
|
|
|
<p id="errorText" class="uk-text-danger" />
|
|
|
|
<MarginInline>
|
|
<button class="uk-button uk-button-primary" type="submit">
|
|
{i18next.t("common_create")}
|
|
</button>
|
|
</MarginInline>
|
|
</Form>
|
|
);
|
|
} else {
|
|
return (
|
|
<>
|
|
<p>{i18next.t("totp_new_generated_warning")}</p>
|
|
<img src={"data:image/png;base64," + this.state.exportedData.barcode} />
|
|
<CopyableInputBox copyable text={this.state.exportedData.url} />
|
|
<button
|
|
class="uk-button uk-button-primary"
|
|
onClick={async () => {
|
|
route("/secrets/totp/list/" + this.props.baseMount);
|
|
}}
|
|
>
|
|
{i18next.t("common_back")}
|
|
</button>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
async onSubmit(data: FormData): Promise<void> {
|
|
const isExported = data.get("exported") == "yes" ? true : false;
|
|
|
|
const parms = {
|
|
generate: true,
|
|
name: data.get("name") as string,
|
|
issuer: data.get("issuer") as string,
|
|
account_name: data.get("account_name") as string,
|
|
exported: isExported,
|
|
key_size: parseInt(data.get("key_size") as string),
|
|
period: data.get("period") as string,
|
|
algorithm: data.get("algorithm") as string,
|
|
digits: parseInt(data.get("digits") as string),
|
|
};
|
|
|
|
try {
|
|
const ret = await this.props.api.addNewTOTP(this.props.baseMount, parms);
|
|
if (!isExported) {
|
|
route("/secrets/totp/list/" + this.props.baseMount);
|
|
} else {
|
|
this.setState({ exportedData: ret });
|
|
}
|
|
} catch (e: unknown) {
|
|
const error = e as Error;
|
|
setErrorText(`API Error: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
export class TOTPNewGenerated extends Component<DefaultPageProps> {
|
|
render() {
|
|
const baseMount = this.props.matches["baseMount"];
|
|
return (
|
|
<>
|
|
<SecretTitleElement
|
|
type="totp"
|
|
baseMount={baseMount}
|
|
suffix={i18next.t("totp_new_generated_suffix")}
|
|
/>
|
|
<TOTPNewGeneratedForm
|
|
settings={this.props.settings}
|
|
api={this.props.api}
|
|
baseMount={baseMount}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
}
|