Add vault-generated totp code support.
This commit is contained in:
parent
5be6596295
commit
7608df51b3
|
@ -2,32 +2,32 @@ import { Component } from "preact";
|
||||||
import { settings } from "./globalSettings";
|
import { settings } from "./globalSettings";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import style_dark from "./scss/main-dark.scss" assert {type: "css"};
|
import style_dark from "./scss/main-dark.scss" assert { type: "css" };
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import style_light from "./scss/main-light.scss" assert {type: "css"};
|
import style_light from "./scss/main-light.scss" assert { type: "css" };
|
||||||
|
|
||||||
export const default_theme = "dark";
|
export const default_theme = "dark";
|
||||||
|
|
||||||
const themes: { [key: string]: string } = {
|
const themes: { [key: string]: string } = {
|
||||||
"dark": style_dark,
|
dark: style_dark as string,
|
||||||
"light": style_light,
|
light: style_light as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ThemeLoader extends Component<unknown, { sheet: string }> {
|
||||||
|
componentDidMount() {
|
||||||
|
this.setCorrectStyle(settings.theme);
|
||||||
|
settings.registerListener((key: string) => {
|
||||||
|
if (key != "theme") return;
|
||||||
|
this.setCorrectStyle(settings.theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setCorrectStyle(theme: string) {
|
||||||
|
this.setState({ sheet: themes[theme] });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (!this.state.sheet) return;
|
||||||
|
return <style>{this.state.sheet}</style>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThemeLoader extends Component<{}, { sheet: string }> {
|
|
||||||
componentDidMount() {
|
|
||||||
this.setCorrectStyle(settings.theme);
|
|
||||||
settings.registerListener((key: string) => {
|
|
||||||
if (key != "theme") return;
|
|
||||||
this.setCorrectStyle(settings.theme);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setCorrectStyle(theme: string) {
|
|
||||||
this.setState({ sheet: themes[theme] })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.state.sheet) return;
|
|
||||||
return <style>{this.state.sheet}</style>
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
} from "./types/transit";
|
} from "./types/transit";
|
||||||
import { DoesNotExistError } from "../types/internalErrors";
|
import { DoesNotExistError } from "../types/internalErrors";
|
||||||
import { MountType, MountsType, NewMountParams } from "./types/mount";
|
import { MountType, MountsType, NewMountParams } from "./types/mount";
|
||||||
|
import { NewTOTPData, NewTOTPResp } from "./types/totp";
|
||||||
import { SealStatusType } from "./types/seal";
|
import { SealStatusType } from "./types/seal";
|
||||||
import { SecretMetadataType } from "./types/secret";
|
import { SecretMetadataType } from "./types/secret";
|
||||||
import { Settings } from "../settings/Settings";
|
import { Settings } from "../settings/Settings";
|
||||||
|
@ -461,7 +462,7 @@ export class API {
|
||||||
return data.data.keys;
|
return data.data.keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addNewTOTP(baseMount: string, parms: { name: string }): Promise<void> {
|
async addNewTOTP(baseMount: string, parms: NewTOTPData): Promise<NewTOTPResp> {
|
||||||
const request = new Request(
|
const request = new Request(
|
||||||
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
|
||||||
{
|
{
|
||||||
|
@ -475,6 +476,8 @@ export class API {
|
||||||
);
|
);
|
||||||
const resp = await fetch(request);
|
const resp = await fetch(request);
|
||||||
await checkResponse(resp);
|
await checkResponse(resp);
|
||||||
|
const data = (await resp.json()) as { data: NewTOTPResp };
|
||||||
|
return data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteTOTP(baseMount: string, name: string): Promise<void> {
|
async deleteTOTP(baseMount: string, name: string): Promise<void> {
|
||||||
|
|
18
src/api/types/totp.ts
Normal file
18
src/api/types/totp.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
export type NewTOTPData = {
|
||||||
|
name: string;
|
||||||
|
generate: boolean;
|
||||||
|
exported?: boolean;
|
||||||
|
key_size?: number;
|
||||||
|
url?: string;
|
||||||
|
key?: string;
|
||||||
|
issuer?: string;
|
||||||
|
account_name?: string;
|
||||||
|
period?: number | string;
|
||||||
|
algorithm?: string;
|
||||||
|
digits?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NewTOTPResp = {
|
||||||
|
url: string;
|
||||||
|
barcode: string;
|
||||||
|
};
|
|
@ -25,13 +25,13 @@ import { formatDistance } from "./formatDistance";
|
||||||
//import { pageList } from "./allPages";
|
//import { pageList } from "./allPages";
|
||||||
import { Main } from "./pages";
|
import { Main } from "./pages";
|
||||||
import { NavBar } from "./ui/elements/NavBar";
|
import { NavBar } from "./ui/elements/NavBar";
|
||||||
|
import { ThemeLoader } from "./ThemeLoader";
|
||||||
import { api } from "./globalAPI";
|
import { api } from "./globalAPI";
|
||||||
import { getCurrentUrl, route } from "preact-router";
|
import { getCurrentUrl, route } from "preact-router";
|
||||||
import { playground } from "./playground";
|
import { playground } from "./playground";
|
||||||
import { render } from "preact";
|
import { render } from "preact";
|
||||||
import { settings } from "./globalSettings";
|
import { settings } from "./globalSettings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { ThemeLoader } from "./ThemeLoader";
|
|
||||||
|
|
||||||
async function onLoad(): Promise<void> {
|
async function onLoad(): Promise<void> {
|
||||||
document.documentElement.dir = settings.pageDirection;
|
document.documentElement.dir = settings.pageDirection;
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { SetVaultURL } from "./ui/pages/SetVaultURL";
|
||||||
import { TOTPDelete } from "./ui/pages/Secrets/TOTP/TOTPDelete";
|
import { TOTPDelete } from "./ui/pages/Secrets/TOTP/TOTPDelete";
|
||||||
import { TOTPList } from "./ui/pages/Secrets/TOTP/TOTPList";
|
import { TOTPList } from "./ui/pages/Secrets/TOTP/TOTPList";
|
||||||
import { TOTPNew } from "./ui/pages/Secrets/TOTP/TOTPNew";
|
import { TOTPNew } from "./ui/pages/Secrets/TOTP/TOTPNew";
|
||||||
|
import { TOTPNewGenerated } from "./ui/pages/Secrets/TOTP/TOTPNewGenerated";
|
||||||
import { TransitDecrypt } from "./ui/pages/Secrets/Transit/TransitDecrypt";
|
import { TransitDecrypt } from "./ui/pages/Secrets/Transit/TransitDecrypt";
|
||||||
import { TransitEncrypt } from "./ui/pages/Secrets/Transit/TransitEncrypt";
|
import { TransitEncrypt } from "./ui/pages/Secrets/Transit/TransitEncrypt";
|
||||||
import { TransitList } from "./ui/pages/Secrets/Transit/TransitList";
|
import { TransitList } from "./ui/pages/Secrets/Transit/TransitList";
|
||||||
|
@ -81,6 +82,7 @@ export const Main = () => (
|
||||||
|
|
||||||
<TOTPList path="/secrets/totp/list/:baseMount" settings={settings} api={api} />
|
<TOTPList path="/secrets/totp/list/:baseMount" settings={settings} api={api} />
|
||||||
<TOTPNew path="/secrets/totp/new/:baseMount" settings={settings} api={api} />
|
<TOTPNew path="/secrets/totp/new/:baseMount" settings={settings} api={api} />
|
||||||
|
<TOTPNewGenerated path="/secrets/totp/new_generated/:baseMount" settings={settings} api={api} />
|
||||||
<TOTPDelete path="/secrets/totp/delete/:baseMount/:item" settings={settings} api={api} />
|
<TOTPDelete path="/secrets/totp/delete/:baseMount/:item" settings={settings} api={api} />
|
||||||
|
|
||||||
<TransitNew path="/secrets/transit/new/:baseMount" settings={settings} api={api} />
|
<TransitNew path="/secrets/transit/new/:baseMount" settings={settings} api={api} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { settings } from "./globalSettings";
|
|
||||||
import { Settings } from "./settings/Settings";
|
import { Settings } from "./settings/Settings";
|
||||||
|
import { settings } from "./globalSettings";
|
||||||
|
|
||||||
// Playground is a way to debug and test things.
|
// Playground is a way to debug and test things.
|
||||||
// Anything you put in here is gonna be run on page initial load
|
// Anything you put in here is gonna be run on page initial load
|
||||||
|
@ -12,7 +12,6 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Please empty this function before committing.
|
// Please empty this function before committing.
|
||||||
export async function playground(): Promise<void> {
|
export async function playground(): Promise<void> {
|
||||||
console.log("Welcome to Playground!");
|
console.log("Welcome to Playground!");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { default_theme } from "../ThemeLoader";
|
|
||||||
import { StorageType } from "./storage/StorageType";
|
import { StorageType } from "./storage/StorageType";
|
||||||
|
import { default_theme } from "../ThemeLoader";
|
||||||
|
|
||||||
type OnChangeListener = (key: string) => void;
|
type OnChangeListener = (key: string) => void;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ export class Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
alertChange(key: string) {
|
alertChange(key: string) {
|
||||||
for (let listener of this.listeners) {
|
for (const listener of this.listeners) {
|
||||||
listener(key);
|
listener(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/translations/en.js
vendored
13
src/translations/en.js
vendored
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
// Common buttons / placeholders
|
// Common buttons / placeholders
|
||||||
common_new: "New",
|
common_new: "New",
|
||||||
common_view: "View",
|
common_view: "View",
|
||||||
|
common_back: "Back",
|
||||||
common_edit: "Edit",
|
common_edit: "Edit",
|
||||||
common_create: "Create",
|
common_create: "Create",
|
||||||
common_delete: "Delete",
|
common_delete: "Delete",
|
||||||
|
@ -176,6 +177,18 @@ module.exports = {
|
||||||
totp_new_switch_to_qr_btn: "Switch to QR Input",
|
totp_new_switch_to_qr_btn: "Switch to QR Input",
|
||||||
totp_new_switch_back_to_manual_input_btn: "Switch back to manual input",
|
totp_new_switch_back_to_manual_input_btn: "Switch back to manual input",
|
||||||
|
|
||||||
|
totp_new_generated: "New Generated",
|
||||||
|
totp_new_generated_suffix: "(new generated)",
|
||||||
|
totp_new_generated_warning:
|
||||||
|
"Make sure to save this information somewhere safe as there is no way to get it back short of Vault's raw api.",
|
||||||
|
totp_new_generated_issuer: "Issuer",
|
||||||
|
totp_new_generated_account_name: "Account Name",
|
||||||
|
totp_new_generated_algorithm: "Algorithm",
|
||||||
|
totp_new_generated_key_size: "Key Size (bytes)",
|
||||||
|
totp_new_generated_period: "Period (Refresh Time)",
|
||||||
|
totp_new_generated_digits: "Digits (Length 6/8)",
|
||||||
|
totp_new_generated_export: "Show QR & URI (Recommended)",
|
||||||
|
|
||||||
// TOTP Delete Page
|
// TOTP Delete Page
|
||||||
totp_delete_title: "Delete TOTP Key",
|
totp_delete_title: "Delete TOTP Key",
|
||||||
totp_delete_suffix: " (delete)",
|
totp_delete_suffix: " (delete)",
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Component, JSX, createRef } from "preact";
|
import { Component, JSX, createRef } from "preact";
|
||||||
import { DefaultPageProps } from "../../types/DefaultPageProps";
|
import { DefaultPageProps } from "../../types/DefaultPageProps";
|
||||||
|
import { InputWithTitle } from "../elements/InputWithTitle";
|
||||||
import { PageTitle } from "../elements/PageTitle";
|
import { PageTitle } from "../elements/PageTitle";
|
||||||
import { addClipboardNotifications, setErrorText } from "../../pageUtils";
|
import { addClipboardNotifications, setErrorText } from "../../pageUtils";
|
||||||
import { route } from "preact-router";
|
import { route } from "preact-router";
|
||||||
import ClipboardJS from "clipboard";
|
import ClipboardJS from "clipboard";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { InputWithTitle } from "../elements/InputWithTitle";
|
|
||||||
|
|
||||||
export class CopyLink extends Component<{ text: string; data: string }, unknown> {
|
export class CopyLink extends Component<{ text: string; data: string }, unknown> {
|
||||||
linkRef = createRef<HTMLAnchorElement>();
|
linkRef = createRef<HTMLAnchorElement>();
|
||||||
|
@ -55,7 +55,7 @@ export class Me extends Component<DefaultPageProps, MeState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
themeSelectRef = createRef<HTMLSelectElement>()
|
themeSelectRef = createRef<HTMLSelectElement>();
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
|
@ -111,12 +111,20 @@ export class Me extends Component<DefaultPageProps, MeState> {
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<InputWithTitle title="Theme">
|
<InputWithTitle title="Theme">
|
||||||
<select ref={this.themeSelectRef} class="uk-select uk-form-width-medium" onChange={() => {
|
<select
|
||||||
let newTheme = this.themeSelectRef.current.value;
|
ref={this.themeSelectRef}
|
||||||
this.props.settings.theme = newTheme;
|
class="uk-select uk-form-width-medium"
|
||||||
}}>
|
onChange={() => {
|
||||||
|
const newTheme = this.themeSelectRef.current.value;
|
||||||
|
this.props.settings.theme = newTheme;
|
||||||
|
}}
|
||||||
|
>
|
||||||
<option label="Dark" value="dark" selected={this.props.settings.theme == "dark"} />
|
<option label="Dark" value="dark" selected={this.props.settings.theme == "dark"} />
|
||||||
<option label="Light" value="light" selected={this.props.settings.theme == "light"} />
|
<option
|
||||||
|
label="Light"
|
||||||
|
value="light"
|
||||||
|
selected={this.props.settings.theme == "light"}
|
||||||
|
/>
|
||||||
</select>
|
</select>
|
||||||
</InputWithTitle>
|
</InputWithTitle>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { DoesNotExistError } from "../../../../types/internalErrors";
|
||||||
import { Grid, GridSizes } from "../../../elements/Grid";
|
import { Grid, GridSizes } from "../../../elements/Grid";
|
||||||
import { MarginInline } from "../../../elements/MarginInline";
|
import { MarginInline } from "../../../elements/MarginInline";
|
||||||
import { SecretTitleElement } from "../SecretTitleElement";
|
import { SecretTitleElement } from "../SecretTitleElement";
|
||||||
import { delSecretsEngineURL, totpNewURL } from "../../pageLinks";
|
import { delSecretsEngineURL, totpNewGeneratedURL, totpNewURL } from "../../pageLinks";
|
||||||
import { removeDoubleSlash } from "../../../../utils";
|
import { removeDoubleSlash } from "../../../../utils";
|
||||||
import { route } from "preact-router";
|
import { route } from "preact-router";
|
||||||
import { setErrorText } from "../../../../pageUtils";
|
import { setErrorText } from "../../../../pageUtils";
|
||||||
|
@ -138,6 +138,16 @@ export class TOTPList extends Component<DefaultPageProps, TOTPListState> {
|
||||||
{i18next.t("common_new")}
|
{i18next.t("common_new")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{totpCaps.includes("create") && (
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-primary"
|
||||||
|
onClick={async () => {
|
||||||
|
route(totpNewGeneratedURL(baseMount));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("totp_new_generated")}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{mountCaps.includes("delete") && (
|
{mountCaps.includes("delete") && (
|
||||||
<button
|
<button
|
||||||
class="uk-button uk-button-danger"
|
class="uk-button uk-button-danger"
|
||||||
|
|
170
src/ui/pages/Secrets/TOTP/TOTPNewGenerated.tsx
Normal file
170
src/ui/pages/Secrets/TOTP/TOTPNewGenerated.tsx
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(parms);
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,10 @@ export function totpNewURL(baseMount: string): string {
|
||||||
return `/secrets/totp/new/${baseMount}`;
|
return `/secrets/totp/new/${baseMount}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function totpNewGeneratedURL(baseMount: string): string {
|
||||||
|
return `/secrets/totp/new_generated/${baseMount}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function totpListURL(baseMount: string): string {
|
export function totpListURL(baseMount: string): string {
|
||||||
return `/secrets/totp/list/${baseMount}`;
|
return `/secrets/totp/list/${baseMount}`;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue