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 { TOTPViewPage } from "./pages/TOTP/TOTPView";
|
||||||
import { TransitDecryptPage } from "./pages/Transit/TransitDecrypt";
|
import { TransitDecryptPage } from "./pages/Transit/TransitDecrypt";
|
||||||
import { TransitEncryptPage } from "./pages/Transit/TransitEncrypt";
|
import { TransitEncryptPage } from "./pages/Transit/TransitEncrypt";
|
||||||
|
import { TransitRewrapPage } from "./pages/Transit/TransitRewrap";
|
||||||
import { TransitViewPage } from "./pages/Transit/TransitView";
|
import { TransitViewPage } from "./pages/Transit/TransitView";
|
||||||
import { TransitViewSecretPage } from "./pages/Transit/TransitViewSecret";
|
import { TransitViewSecretPage } from "./pages/Transit/TransitViewSecret";
|
||||||
import { UnsealPage } from "./pages/Unseal";
|
import { UnsealPage } from "./pages/Unseal";
|
||||||
|
@ -36,6 +37,7 @@ export const allPages: pagesList = {
|
||||||
TRANSIT_VIEW_SECRET: new TransitViewSecretPage(),
|
TRANSIT_VIEW_SECRET: new TransitViewSecretPage(),
|
||||||
TRANSIT_ENCRYPT: new TransitEncryptPage(),
|
TRANSIT_ENCRYPT: new TransitEncryptPage(),
|
||||||
TRANSIT_DECRYPT: new TransitDecryptPage(),
|
TRANSIT_DECRYPT: new TransitDecryptPage(),
|
||||||
|
TRANSIT_REWRAP: new TransitRewrapPage(),
|
||||||
KEY_VALUE_VIEW: new KeyValueViewPage(),
|
KEY_VALUE_VIEW: new KeyValueViewPage(),
|
||||||
KEY_VALUE_SECRET: new KeyValueSecretPage(),
|
KEY_VALUE_SECRET: new KeyValueSecretPage(),
|
||||||
KEY_VALUE_VERSIONS: new KeyValueVersionsPage(),
|
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
|
// Type returned when calling getTransitKey
|
||||||
export type TransitKeyType = TransitKeyBaseType & {
|
export type TransitKeyType = TransitKeyBaseType & {
|
||||||
keys: {
|
keys: {
|
||||||
[version: string]: number;
|
[version: number]: number;
|
||||||
};
|
};
|
||||||
min_decryption_version: number;
|
min_decryption_version: number;
|
||||||
min_encryption_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 { makeElement } from "../htmlUtils";
|
||||||
import { setPageContent } from "../pageUtils";
|
import { setPageContent } from "../pageUtils";
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
|
import { Option } from "../elements/Option";
|
||||||
|
|
||||||
const passwordLengthMin = 1;
|
const passwordLengthMin = 1;
|
||||||
const passwordLengthMax = 64;
|
const passwordLengthMax = 64;
|
||||||
|
@ -43,17 +44,6 @@ function genPassword(options = passwordOptionsDefault) {
|
||||||
return pw;
|
return pw;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Option(label, value) {
|
|
||||||
return makeElement({
|
|
||||||
tag: "option",
|
|
||||||
text: label,
|
|
||||||
attributes: {
|
|
||||||
label: label,
|
|
||||||
value: value,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PwGenPage extends Page {
|
export class PwGenPage extends Page {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
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"),
|
iconText: i18next.t("transit_view_decrypt_icon_text"),
|
||||||
onclick: () => { changePage("TRANSIT_DECRYPT"); }
|
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_text": "Decrypt",
|
||||||
"transit_view_decrypt_description": "Decrypt some cyphertext.",
|
"transit_view_decrypt_description": "Decrypt some cyphertext.",
|
||||||
"transit_view_decrypt_icon_text": "Decryption Icon",
|
"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 Page
|
||||||
"transit_encrypt_title": "Transit Encrypt",
|
"transit_encrypt_title": "Transit Encrypt",
|
||||||
|
@ -142,11 +145,20 @@ module.exports = {
|
||||||
"transit_encrypt_encrypt_btn": "Encrypt",
|
"transit_encrypt_encrypt_btn": "Encrypt",
|
||||||
"transit_encrypt_encryption_result_modal_title": "Encryption Result",
|
"transit_encrypt_encryption_result_modal_title": "Encryption Result",
|
||||||
|
|
||||||
// Transit decrypt Page
|
// Transit Decrypt Page
|
||||||
"transit_decrypt_title": "Transit Decrypt",
|
"transit_decrypt_title": "Transit Decrypt",
|
||||||
"transit_decrypt_suffix": " (decrypt)",
|
"transit_decrypt_suffix": " (decrypt)",
|
||||||
"transit_decrypt_input_placeholder": "Cyphertext",
|
"transit_decrypt_input_placeholder": "Cyphertext",
|
||||||
"transit_decrypt_decode_checkbox": "Should the plaintext be base64 decoded?",
|
"transit_decrypt_decode_checkbox": "Should the plaintext be base64 decoded?",
|
||||||
"transit_decrypt_decrypt_btn": "Decrypt",
|
"transit_decrypt_decrypt_btn": "Decrypt",
|
||||||
"transit_decrypt_decryption_result_modal_title": "Decryption Result",
|
"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