Add TransitRewrap page.
This commit is contained in:
parent
562ced442e
commit
c635c2c5c7
|
@ -15,6 +15,7 @@ import { SetVaultURLPage } from "./pages/SetVaultURL";
|
|||
import { TOTPViewPage } from "./pages/TOTP/TOTPView";
|
||||
import { TransitDecryptPage } from "./pages/Transit/TransitDecrypt";
|
||||
import { TransitEncryptPage } from "./pages/Transit/TransitEncrypt";
|
||||
import { TransitRewrapPage } from "./pages/Transit/TransitRewrap";
|
||||
import { TransitViewPage } from "./pages/Transit/TransitView";
|
||||
import { TransitViewSecretPage } from "./pages/Transit/TransitViewSecret";
|
||||
import { UnsealPage } from "./pages/Unseal";
|
||||
|
@ -36,6 +37,7 @@ export const allPages: pagesList = {
|
|||
TRANSIT_VIEW_SECRET: new TransitViewSecretPage(),
|
||||
TRANSIT_ENCRYPT: new TransitEncryptPage(),
|
||||
TRANSIT_DECRYPT: new TransitDecryptPage(),
|
||||
TRANSIT_REWRAP: new TransitRewrapPage(),
|
||||
KEY_VALUE_VIEW: new KeyValueViewPage(),
|
||||
KEY_VALUE_SECRET: new KeyValueSecretPage(),
|
||||
KEY_VALUE_VERSIONS: new KeyValueVersionsPage(),
|
||||
|
|
34
src/api/transit/transitRewrap.ts
Normal file
34
src/api/transit/transitRewrap.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||
import { removeDoubleSlash } from "../../utils";
|
||||
|
||||
type RewrapResult = {
|
||||
ciphertext: string;
|
||||
}
|
||||
|
||||
type RewrapPayload = {
|
||||
ciphertext: string;
|
||||
key_version?: number;
|
||||
}
|
||||
|
||||
export async function transitRewrap(
|
||||
baseMount: string,
|
||||
name: string,
|
||||
payload: RewrapPayload
|
||||
): Promise<RewrapResult> {
|
||||
const request = new Request(appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/rewrap/${name}`)), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...getHeaders(),
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const response = await fetch(request);
|
||||
if (!response.ok) {
|
||||
const json = await response.json();
|
||||
throw new Error(json.errors[0]);
|
||||
} else {
|
||||
const json = await response.json();
|
||||
return json.data;
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ export type TransitKeyBaseType = {
|
|||
// Type returned when calling getTransitKey
|
||||
export type TransitKeyType = TransitKeyBaseType & {
|
||||
keys: {
|
||||
[version: string]: number;
|
||||
[version: number]: number;
|
||||
};
|
||||
min_decryption_version: number;
|
||||
min_encryption_version: number;
|
||||
|
|
12
src/elements/Option.ts
Normal file
12
src/elements/Option.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { makeElement } from "../htmlUtils";
|
||||
|
||||
export function Option(label: string, value: string): HTMLElement {
|
||||
return makeElement({
|
||||
tag: "option",
|
||||
text: label,
|
||||
attributes: {
|
||||
label: label,
|
||||
value: value,
|
||||
}
|
||||
})
|
||||
}
|
|
@ -4,6 +4,7 @@ import { Page } from "../types/Page";
|
|||
import { makeElement } from "../htmlUtils";
|
||||
import { setPageContent } from "../pageUtils";
|
||||
import i18next from 'i18next';
|
||||
import { Option } from "../elements/Option";
|
||||
|
||||
const passwordLengthMin = 1;
|
||||
const passwordLengthMax = 64;
|
||||
|
@ -43,17 +44,6 @@ function genPassword(options = passwordOptionsDefault) {
|
|||
return pw;
|
||||
}
|
||||
|
||||
function Option(label, value) {
|
||||
return makeElement({
|
||||
tag: "option",
|
||||
text: label,
|
||||
attributes: {
|
||||
label: label,
|
||||
value: value,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export class PwGenPage extends Page {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
117
src/pages/Transit/TransitRewrap.ts
Normal file
117
src/pages/Transit/TransitRewrap.ts
Normal file
|
@ -0,0 +1,117 @@
|
|||
import { CopyableModal } from "../../elements/CopyableModal";
|
||||
import { FileUploadInput } from "../../elements/FileUploadInput";
|
||||
import { Margin } from "../../elements/Margin";
|
||||
import { Page } from "../../types/Page";
|
||||
import { changePage, setErrorText, setPageContent, setTitleElement } from "../../pageUtils";
|
||||
import { makeElement } from "../../htmlUtils";
|
||||
import { pageState } from "../../globalPageState";
|
||||
import UIkit from 'uikit/dist/js/uikit.min.js';
|
||||
import i18next from "i18next";
|
||||
import { getTransitKey } from "../../api/transit/getTransitKey";
|
||||
import { objectToMap } from "../../utils";
|
||||
import { Option } from "../../elements/Option";
|
||||
import { transitRewrap } from "../../api/transit/transitRewrap";
|
||||
|
||||
type versionOption = { version: string; label: string }
|
||||
|
||||
export class TransitRewrapPage extends Page {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
changePage("TRANSIT_VIEW_SECRET");
|
||||
}
|
||||
|
||||
transitRewrapForm: HTMLFormElement;
|
||||
|
||||
async render(): Promise<void> {
|
||||
setTitleElement(pageState);
|
||||
let transitKey = await getTransitKey(pageState.currentBaseMount, pageState.currentSecret);
|
||||
|
||||
let stringVersions = Array.from(objectToMap(transitKey.keys).keys()).reverse() as any as string[];
|
||||
let versions = stringVersions.map((val) => parseInt(val, 10)) as any as number[];
|
||||
|
||||
// get the selectable version options in the same
|
||||
// format the official UI uses.
|
||||
// e.g: ["2 (latest)", "1"]
|
||||
|
||||
let options: versionOption[] = versions.map((val): versionOption => {
|
||||
let i18nkey = val == Math.max(...versions) ?
|
||||
"transit_rewrap_latest_version_option_text"
|
||||
:
|
||||
"transit_rewrap_version_option_text";
|
||||
return {
|
||||
version: String(val),
|
||||
label: i18next.t(i18nkey, { version_num: String(val) }),
|
||||
}
|
||||
})
|
||||
|
||||
setPageContent("");
|
||||
this.transitRewrapForm = makeElement({
|
||||
tag: "form",
|
||||
children: [
|
||||
makeElement({
|
||||
tag: "select",
|
||||
name: "version",
|
||||
class: ["uk-select", "uk-width-1-2"],
|
||||
children: options.map((option): HTMLElement => Option(option.label, option.version))
|
||||
}),
|
||||
Margin(makeElement({
|
||||
tag: "textarea",
|
||||
class: ["uk-textarea", "uk-width-1-2"],
|
||||
attributes: {
|
||||
placeholder: i18next.t("transit_rewrap_input_placeholder"),
|
||||
name: "ciphertext",
|
||||
}
|
||||
})),
|
||||
makeElement({
|
||||
tag: "p",
|
||||
id: "errorText",
|
||||
class: "uk-text-danger"
|
||||
}),
|
||||
makeElement({
|
||||
tag: "button",
|
||||
class: ["uk-button", "uk-button-primary"],
|
||||
text: i18next.t("transit_rewrap_rewrap_btn"),
|
||||
attributes: {
|
||||
type: "submit",
|
||||
}
|
||||
})
|
||||
]
|
||||
}) as HTMLFormElement;
|
||||
setPageContent(this.transitRewrapForm);
|
||||
this.transitRewrapForm.addEventListener("submit", async function (e: Event) {
|
||||
e.preventDefault();
|
||||
await this.transitRewrapFormHandler();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
async transitRewrapFormHandler(): Promise<void> {
|
||||
const formData = new FormData(this.transitRewrapForm);
|
||||
const ciphertext = formData.get("ciphertext") as string;
|
||||
try {
|
||||
let res = await transitRewrap(
|
||||
pageState.currentBaseMount,
|
||||
pageState.currentSecret,
|
||||
{
|
||||
ciphertext: formData.get("ciphertext") as string,
|
||||
key_version: parseInt(formData.get("version") as string, 10),
|
||||
}
|
||||
);
|
||||
const modal = CopyableModal(i18next.t("transit_rewrap_result_modal_title"), res.ciphertext);
|
||||
document.body.querySelector("#pageContent").appendChild(modal);
|
||||
UIkit.modal(modal).show();
|
||||
} catch (e) {
|
||||
setErrorText(`API Error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
get titleSuffix(): string {
|
||||
return i18next.t("transit_rewrap_suffix");
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return i18next.t("transit_rewrap_title");
|
||||
}
|
||||
}
|
|
@ -41,6 +41,14 @@ export class TransitViewSecretPage extends Page {
|
|||
iconText: i18next.t("transit_view_decrypt_icon_text"),
|
||||
onclick: () => { changePage("TRANSIT_DECRYPT"); }
|
||||
}),
|
||||
Tile({
|
||||
condition: transitKey.supports_decryption,
|
||||
title: i18next.t("transit_view_rewrap_text"),
|
||||
description: i18next.t("transit_view_rewrap_description"),
|
||||
icon: "code",
|
||||
iconText: i18next.t("transit_view_rewrap_icon_text"),
|
||||
onclick: () => { changePage("TRANSIT_REWRAP"); }
|
||||
}),
|
||||
]
|
||||
}));
|
||||
}
|
||||
|
|
14
src/translations/en.js
vendored
14
src/translations/en.js
vendored
|
@ -133,6 +133,9 @@ module.exports = {
|
|||
"transit_view_decrypt_text": "Decrypt",
|
||||
"transit_view_decrypt_description": "Decrypt some cyphertext.",
|
||||
"transit_view_decrypt_icon_text": "Decryption Icon",
|
||||
"transit_view_rewrap_text": "Rewrap",
|
||||
"transit_view_rewrap_description": "Rewrap ciphertext using a different key version.",
|
||||
"transit_view_rewrap_icon_text": "Rewrap Icon",
|
||||
|
||||
// Transit Encrypt Page
|
||||
"transit_encrypt_title": "Transit Encrypt",
|
||||
|
@ -142,11 +145,20 @@ module.exports = {
|
|||
"transit_encrypt_encrypt_btn": "Encrypt",
|
||||
"transit_encrypt_encryption_result_modal_title": "Encryption Result",
|
||||
|
||||
// Transit decrypt Page
|
||||
// Transit Decrypt Page
|
||||
"transit_decrypt_title": "Transit Decrypt",
|
||||
"transit_decrypt_suffix": " (decrypt)",
|
||||
"transit_decrypt_input_placeholder": "Cyphertext",
|
||||
"transit_decrypt_decode_checkbox": "Should the plaintext be base64 decoded?",
|
||||
"transit_decrypt_decrypt_btn": "Decrypt",
|
||||
"transit_decrypt_decryption_result_modal_title": "Decryption Result",
|
||||
|
||||
// Transit Rewrap Page
|
||||
"transit_rewrap_title": "Transit Rewrap",
|
||||
"transit_rewrap_suffix": " (rewrap)",
|
||||
"transit_rewrap_version_option_text": "{{version_num}}",
|
||||
"transit_rewrap_latest_version_option_text": "{{version_num}} (latest)",
|
||||
"transit_rewrap_input_placeholder": "Cyphertext",
|
||||
"transit_rewrap_rewrap_btn": "Rewrap",
|
||||
"transit_rewrap_result_modal_title": "Rewrap Result",
|
||||
}
|
Loading…
Reference in a new issue