Implement deleting of TOTP codes. Closes #2.
This commit is contained in:
parent
4d04070446
commit
1ccb432cbc
|
@ -26,6 +26,7 @@ import { PwGenPage } from "./pages/PwGen";
|
||||||
import { SecretsHomePage } from "./pages/Secrets/SecretsHome";
|
import { SecretsHomePage } from "./pages/Secrets/SecretsHome";
|
||||||
import { SetLanguagePage } from "./pages/SetLanguage";
|
import { SetLanguagePage } from "./pages/SetLanguage";
|
||||||
import { SetVaultURLPage } from "./pages/SetVaultURL";
|
import { SetVaultURLPage } from "./pages/SetVaultURL";
|
||||||
|
import { TOTPDeletePage } from "./pages/Secrets/TOTP/TOTPDelete";
|
||||||
import { TOTPViewPage } from "./pages/Secrets/TOTP/TOTPView";
|
import { TOTPViewPage } from "./pages/Secrets/TOTP/TOTPView";
|
||||||
import { TransitDecryptPage } from "./pages/Secrets/Transit/TransitDecrypt";
|
import { TransitDecryptPage } from "./pages/Secrets/Transit/TransitDecrypt";
|
||||||
import { TransitEncryptPage } from "./pages/Secrets/Transit/TransitEncrypt";
|
import { TransitEncryptPage } from "./pages/Secrets/Transit/TransitEncrypt";
|
||||||
|
@ -58,6 +59,7 @@ export const allPages: pagesList = {
|
||||||
ME: new MePage(),
|
ME: new MePage(),
|
||||||
TOTP: new TOTPViewPage(),
|
TOTP: new TOTPViewPage(),
|
||||||
NEW_TOTP: new NewTOTPPage(),
|
NEW_TOTP: new NewTOTPPage(),
|
||||||
|
TOTP_DELETE: new TOTPDeletePage(),
|
||||||
LOGIN: new LoginPage(),
|
LOGIN: new LoginPage(),
|
||||||
SET_VAULT_URL: new SetVaultURLPage(),
|
SET_VAULT_URL: new SetVaultURLPage(),
|
||||||
UNSEAL: new UnsealPage(),
|
UNSEAL: new UnsealPage(),
|
||||||
|
|
15
src/api/totp/deleteTOTP.ts
Normal file
15
src/api/totp/deleteTOTP.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||||
|
|
||||||
|
export async function deleteTOTP(baseMount: string, name: string): Promise<void> {
|
||||||
|
const request = new Request(appendAPIURL(`/v1/${baseMount}/keys/${name}`), {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: getHeaders(),
|
||||||
|
});
|
||||||
|
const resp = await fetch(request);
|
||||||
|
if (!resp.ok) {
|
||||||
|
const data = (await resp.json()) as { errors?: string[] };
|
||||||
|
if ("errors" in data) {
|
||||||
|
throw new Error(data.errors[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/pages/Secrets/TOTP/TOTPDelete.tsx
Normal file
45
src/pages/Secrets/TOTP/TOTPDelete.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { Page } from "../../../types/Page";
|
||||||
|
import { SecretTitleElement } from "../SecretTitleElement";
|
||||||
|
import { deleteTOTP } from "../../../api/totp/deleteTOTP";
|
||||||
|
import { render } from "preact";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
export class TOTPDeletePage extends Page {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async cleanup(): Promise<void> {
|
||||||
|
this.state.secretItem = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async goBack(): Promise<void> {
|
||||||
|
this.state.secretItem = "";
|
||||||
|
await this.router.changePage("TOTP");
|
||||||
|
}
|
||||||
|
async render(): Promise<void> {
|
||||||
|
render(
|
||||||
|
<div>
|
||||||
|
<h5>{i18next.t("totp_delete_text")}</h5>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-danger"
|
||||||
|
onClick={async () => {
|
||||||
|
await deleteTOTP(this.state.baseMount, this.state.secretItem);
|
||||||
|
await this.goBack();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("kv_delete_btn")}
|
||||||
|
</button>
|
||||||
|
</div>,
|
||||||
|
this.router.pageContentElement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPageTitle(): Promise<Element | string> {
|
||||||
|
return await SecretTitleElement(this.router, i18next.t("totp_delete_suffix"));
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return i18next.t("totp_delete_title");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
import { Component, JSX, render } from "preact";
|
import { Component, JSX, render } from "preact";
|
||||||
import { CopyableInputBox } from "../../../elements/ReactCopyableInputBox";
|
import { CopyableInputBox } from "../../../elements/ReactCopyableInputBox";
|
||||||
import { DoesNotExistError } from "../../../types/internalErrors";
|
import { DoesNotExistError } from "../../../types/internalErrors";
|
||||||
|
import { MarginInline } from "../../../elements/ReactMarginInline";
|
||||||
import { Page } from "../../../types/Page";
|
import { Page } from "../../../types/Page";
|
||||||
|
import { PageRouter } from "z-pagerouter";
|
||||||
|
import { PageState } from "../../../PageState";
|
||||||
import { SecretTitleElement } from "../SecretTitleElement";
|
import { SecretTitleElement } from "../SecretTitleElement";
|
||||||
import { getTOTPCode } from "../../../api/totp/getTOTPCode";
|
import { getTOTPCode } from "../../../api/totp/getTOTPCode";
|
||||||
import { getTOTPKeys } from "../../../api/totp/getTOTPKeys";
|
import { getTOTPKeys } from "../../../api/totp/getTOTPKeys";
|
||||||
|
@ -9,7 +12,7 @@ import { setErrorText } from "../../../pageUtils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class RefreshingTOTPGridItem extends Component<
|
export class RefreshingTOTPGridItem extends Component<
|
||||||
{ baseMount: string; totpKey: string },
|
{ baseMount: string; totpKey: string; router: PageRouter },
|
||||||
{ totpValue: string }
|
{ totpValue: string }
|
||||||
> {
|
> {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -36,6 +39,20 @@ export class RefreshingTOTPGridItem extends Component<
|
||||||
<div class="uk-grid uk-grid-small uk-text-left" uk-grid>
|
<div class="uk-grid uk-grid-small uk-text-left" uk-grid>
|
||||||
<CopyableInputBox text={this.props.totpKey} copyable />
|
<CopyableInputBox text={this.props.totpKey} copyable />
|
||||||
<CopyableInputBox text={this.state.totpValue} copyable />
|
<CopyableInputBox text={this.state.totpValue} copyable />
|
||||||
|
<div>
|
||||||
|
<MarginInline>
|
||||||
|
<button
|
||||||
|
class="uk-button uk-button-danger"
|
||||||
|
onClick={async () => {
|
||||||
|
const state = this.props.router.state as PageState;
|
||||||
|
state.secretItem = this.props.totpKey;
|
||||||
|
await this.props.router.changePage("TOTP_DELETE");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("totp_view_delete_btn")}
|
||||||
|
</button>
|
||||||
|
</MarginInline>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,6 +71,7 @@ export class TOTPViewPage extends Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(): Promise<void> {
|
async render(): Promise<void> {
|
||||||
|
this.state.secretItem = "";
|
||||||
render(
|
render(
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
@ -72,7 +90,11 @@ export class TOTPViewPage extends Page {
|
||||||
try {
|
try {
|
||||||
const elem = await Promise.all(
|
const elem = await Promise.all(
|
||||||
Array.from(await getTOTPKeys(this.state.baseMount)).map(async (key) => (
|
Array.from(await getTOTPKeys(this.state.baseMount)).map(async (key) => (
|
||||||
<RefreshingTOTPGridItem baseMount={this.state.baseMount} totpKey={key} />
|
<RefreshingTOTPGridItem
|
||||||
|
baseMount={this.state.baseMount}
|
||||||
|
totpKey={key}
|
||||||
|
router={this.router}
|
||||||
|
/>
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
return elem;
|
return elem;
|
||||||
|
|
7
src/translations/en.js
vendored
7
src/translations/en.js
vendored
|
@ -146,6 +146,7 @@ module.exports = {
|
||||||
// TOTP View Page
|
// TOTP View Page
|
||||||
totp_view_title: "TOTP",
|
totp_view_title: "TOTP",
|
||||||
totp_view_new_btn: "New",
|
totp_view_new_btn: "New",
|
||||||
|
totp_view_delete_btn: "Delete",
|
||||||
totp_view_loading: "Loading TOTP Codes..",
|
totp_view_loading: "Loading TOTP Codes..",
|
||||||
totp_view_empty: "You seem to have no TOTP codes here, would you like to create one?",
|
totp_view_empty: "You seem to have no TOTP codes here, would you like to create one?",
|
||||||
totp_view_loading_box: "Loading..",
|
totp_view_loading_box: "Loading..",
|
||||||
|
@ -160,6 +161,12 @@ module.exports = {
|
||||||
totp_new_key_input: "Key",
|
totp_new_key_input: "Key",
|
||||||
totp_new_add_btn: "Add TOTP Key",
|
totp_new_add_btn: "Add TOTP Key",
|
||||||
|
|
||||||
|
// TOTP Delete Page
|
||||||
|
totp_delete_title: "Delete TOTP Key",
|
||||||
|
totp_delete_suffix: " (delete)",
|
||||||
|
totp_delete_text: "Are you sure you want to delete this TOTP secret?",
|
||||||
|
totp_delete_button: "Delete",
|
||||||
|
|
||||||
// Transit View Page
|
// Transit View Page
|
||||||
transit_view_title: "Transit View",
|
transit_view_title: "Transit View",
|
||||||
transit_view_none_here_text:
|
transit_view_none_here_text:
|
||||||
|
|
Loading…
Reference in a new issue