Add ability to create new KV secrets engines.
This commit is contained in:
parent
c29dbfd766
commit
004b7b291f
|
@ -19,6 +19,8 @@ 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";
|
||||||
|
import { NewSecretsEnginePage } from "./pages/NewSecretsEngine";
|
||||||
|
import { NewKVEnginePage } from "./pages/NewEngines/NewKVEngine";
|
||||||
|
|
||||||
type pagesList = {
|
type pagesList = {
|
||||||
[key: string]: Page;
|
[key: string]: Page;
|
||||||
|
@ -45,4 +47,6 @@ export const allPages: pagesList = {
|
||||||
KEY_VALUE_DELETE: new KeyValueDeletePage(),
|
KEY_VALUE_DELETE: new KeyValueDeletePage(),
|
||||||
KEY_VALUE_SECRET_EDIT: new KeyValueSecretEditPage(),
|
KEY_VALUE_SECRET_EDIT: new KeyValueSecretEditPage(),
|
||||||
PW_GEN: new PwGenPage(),
|
PW_GEN: new PwGenPage(),
|
||||||
|
NEW_SECRETS_ENGINE: new NewSecretsEnginePage(),
|
||||||
|
NEW_KV_ENGINE: new NewKVEnginePage(),
|
||||||
};
|
};
|
||||||
|
|
31
src/api/sys/newMount.ts
Normal file
31
src/api/sys/newMount.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { appendAPIURL, getHeaders } from "../apiUtils";
|
||||||
|
import { removeDoubleSlash } from "../../utils";
|
||||||
|
|
||||||
|
type NewMountParams = {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
options?: {
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function newMount(parms: NewMountParams): Promise<void> {
|
||||||
|
const request = new Request(
|
||||||
|
appendAPIURL(removeDoubleSlash(`/v1/sys/mounts/${parms.name}`)),
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...getHeaders(),
|
||||||
|
},
|
||||||
|
body: JSON.stringify(parms),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,17 @@
|
||||||
import { makeElement } from "../htmlUtils";
|
import { makeElement } from "../htmlUtils";
|
||||||
|
|
||||||
type TileParams = {
|
type TileParams = {
|
||||||
condition: boolean;
|
condition?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
iconText: string;
|
iconText?: string;
|
||||||
onclick: () => void;
|
onclick: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Tile(params: TileParams): HTMLElement {
|
export function Tile(params: TileParams): HTMLElement {
|
||||||
if (!params.condition) return;
|
console.log(params.condition == undefined, params.condition);
|
||||||
|
if (params.condition == false) return;
|
||||||
return makeElement({
|
return makeElement({
|
||||||
tag: "a",
|
tag: "a",
|
||||||
class: "uk-link-heading",
|
class: "uk-link-heading",
|
||||||
|
@ -24,6 +25,7 @@ export function Tile(params: TileParams): HTMLElement {
|
||||||
class: "uk-h4",
|
class: "uk-h4",
|
||||||
text: params.title,
|
text: params.title,
|
||||||
children: makeElement({
|
children: makeElement({
|
||||||
|
condition: typeof params.icon == "string",
|
||||||
tag: "span",
|
tag: "span",
|
||||||
class: ["uk-icon", "uk-margin-small-left"],
|
class: ["uk-icon", "uk-margin-small-left"],
|
||||||
attributes: {
|
attributes: {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { makeElement } from "../htmlUtils";
|
||||||
import { pageState } from "../globalPageState";
|
import { pageState } from "../globalPageState";
|
||||||
import { sortedObjectMap } from "../utils";
|
import { sortedObjectMap } from "../utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { getCapabilitiesPath } from "../api/sys/getCapabilities";
|
||||||
|
|
||||||
export class HomePage extends Page {
|
export class HomePage extends Page {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -61,6 +62,18 @@ export class HomePage extends Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mountsCapabilities = await getCapabilitiesPath("/sys/mounts");
|
||||||
|
if (mountsCapabilities.includes("sudo") && mountsCapabilities.includes("create")) {
|
||||||
|
textList.appendChild(makeElement({
|
||||||
|
tag: "button",
|
||||||
|
text: i18next.t("home_new_secrets_engine_button"),
|
||||||
|
class: ["uk-button", "uk-button-primary", "uk-margin-top"],
|
||||||
|
onclick: async () => {
|
||||||
|
await changePage("NEW_SECRETS_ENGINE");
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
pageState.currentBaseMount = "";
|
pageState.currentBaseMount = "";
|
||||||
pageState.currentSecretPath = [];
|
pageState.currentSecretPath = [];
|
||||||
pageState.currentSecret = "";
|
pageState.currentSecret = "";
|
||||||
|
|
89
src/pages/NewEngines/NewKVEngine.ts
Normal file
89
src/pages/NewEngines/NewKVEngine.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import { Margin } from "../../elements/Margin";
|
||||||
|
import { Option } from "../../elements/Option";
|
||||||
|
import { Page } from "../../types/Page";
|
||||||
|
import { changePage, setErrorText, setPageContent } from "../../pageUtils";
|
||||||
|
import { makeElement } from "../../htmlUtils";
|
||||||
|
import { pageState } from "../../globalPageState";
|
||||||
|
import { reloadNavBar } from "../../elements/NavBar";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import {newMount} from "../../api/sys/newMount";
|
||||||
|
|
||||||
|
export class NewKVEnginePage extends Page {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
async render(): Promise<void> {
|
||||||
|
const newEngineForm = makeElement({
|
||||||
|
tag: "form",
|
||||||
|
children: [
|
||||||
|
Margin(
|
||||||
|
makeElement({
|
||||||
|
tag: "input",
|
||||||
|
class: ["uk-input", "uk-form-width-medium"],
|
||||||
|
attributes: {
|
||||||
|
required: "true",
|
||||||
|
type: "text",
|
||||||
|
placeholder: i18next.t("new_kv_engine_name_input"),
|
||||||
|
name: "name",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Margin(
|
||||||
|
makeElement({
|
||||||
|
tag: "select",
|
||||||
|
class: ["uk-select", "uk-form-width-medium"],
|
||||||
|
attributes: {
|
||||||
|
name: "version",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
Option(i18next.t("new_kv_engine_version_2"), "2"),
|
||||||
|
Option(i18next.t("new_kv_engine_version_1"), "1"),
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
makeElement({
|
||||||
|
tag: "p",
|
||||||
|
id: "errorText",
|
||||||
|
class: "uk-text-danger",
|
||||||
|
}),
|
||||||
|
makeElement({
|
||||||
|
tag: "button",
|
||||||
|
class: ["uk-button", "uk-button-primary"],
|
||||||
|
text: i18next.t("new_kv_engine_create_btn"),
|
||||||
|
attributes: {
|
||||||
|
type: "submit",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}) as HTMLFormElement;
|
||||||
|
|
||||||
|
setPageContent(newEngineForm);
|
||||||
|
|
||||||
|
newEngineForm.addEventListener("submit", async function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(newEngineForm);
|
||||||
|
|
||||||
|
const name = formData.get("name") as string;
|
||||||
|
const version = formData.get("version") as string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await newMount({
|
||||||
|
name: name,
|
||||||
|
type: "kv",
|
||||||
|
options: {
|
||||||
|
version: version
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pageState.currentMountType = "kv-v" + version;
|
||||||
|
pageState.currentBaseMount = name + "/";
|
||||||
|
await changePage("KEY_VALUE_VIEW");
|
||||||
|
} catch (e) {
|
||||||
|
const error = e as Error;
|
||||||
|
setErrorText(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
get name(): string {
|
||||||
|
return i18next.t("new_kv_engine_title");
|
||||||
|
}
|
||||||
|
}
|
34
src/pages/NewSecretsEngine.ts
Normal file
34
src/pages/NewSecretsEngine.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { Page } from "../types/Page";
|
||||||
|
import { Tile } from "../elements/Tile";
|
||||||
|
import { changePage, setPageContent, setTitleElement } from "../pageUtils";
|
||||||
|
import { makeElement } from "../htmlUtils";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
export class NewSecretsEnginePage extends Page {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async render(): Promise<void> {
|
||||||
|
setPageContent(
|
||||||
|
makeElement({
|
||||||
|
tag: "div",
|
||||||
|
class: "uk-child-width-1-1@s uk-child-width-1-2@m uk-grid-small uk-grid-match",
|
||||||
|
attributes: { "uk-grid": "" },
|
||||||
|
children: [
|
||||||
|
Tile({
|
||||||
|
title: i18next.t("new_secrets_engine_kv_title"),
|
||||||
|
description: i18next.t("new_secrets_engine_kv_description"),
|
||||||
|
onclick: () => {
|
||||||
|
void changePage("NEW_KV_ENGINE");
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return i18next.t("new_secrets_engine_title");
|
||||||
|
}
|
||||||
|
}
|
14
src/translations/en.js
vendored
14
src/translations/en.js
vendored
|
@ -37,6 +37,20 @@ module.exports = {
|
||||||
vaulturl_text: "Vault URL: {{text}}",
|
vaulturl_text: "Vault URL: {{text}}",
|
||||||
password_generator_btn: "Password Generator",
|
password_generator_btn: "Password Generator",
|
||||||
your_token_expires_in: "Your token expires in {{date, until_date}}",
|
your_token_expires_in: "Your token expires in {{date, until_date}}",
|
||||||
|
home_new_secrets_engine_button: "New Secrets Engine",
|
||||||
|
|
||||||
|
// New Secrets Engine Page
|
||||||
|
new_secrets_engine_title: "New Secrets Engine",
|
||||||
|
new_secrets_engine_kv_title: "Key/Value Storage",
|
||||||
|
new_secrets_engine_kv_description: "For storing key/value mapped secrets.",
|
||||||
|
|
||||||
|
// New KV Engine Page
|
||||||
|
new_kv_engine_title: "New Key/Value Engine",
|
||||||
|
new_kv_engine_name_input: "Name",
|
||||||
|
new_kv_engine_version_1: "Version 1",
|
||||||
|
new_kv_engine_version_2: "Version 2",
|
||||||
|
new_kv_engine_create_btn: "Create",
|
||||||
|
|
||||||
|
|
||||||
// Unseal Page
|
// Unseal Page
|
||||||
unseal_vault_text: "Unseal the Vault",
|
unseal_vault_text: "Unseal the Vault",
|
||||||
|
|
Loading…
Reference in a new issue