1
0
Fork 0

initial work on moving to a real page router

This commit is contained in:
ChaotiCryptidz 2022-01-06 18:53:38 +00:00
parent da163c931a
commit c62cd89771
48 changed files with 1201 additions and 1209 deletions

View file

@ -17,6 +17,7 @@
"codejar": "^3.5.0",
"core-js": "^3.20.2",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.3.1",
"date-fns": "^2.28.0",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
@ -41,7 +42,9 @@
"uikit": "^3.9.4",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.7.2",
"css-minimizer-webpack-plugin": "^3.3.1"
"webpack-dev-server": "^4.7.2"
},
"dependencies": {
"preact-router": "^4.0.0"
}
}

View file

@ -1,121 +1,121 @@
import { PageType } from "./pagerouter/PageType";
import { AccessHomePage } from "./ui/pages/Access/AccessHome";
import { AuthHomePage } from "./ui/pages/Access/Auth/AuthHome";
import { AuthViewConfigPage } from "./ui/pages/Access/Auth/AuthViewConfig";
import { DeleteSecretsEnginePage } from "./ui/pages/Secrets/DeleteSecretsEngine";
import { HomePage } from "./ui/pages/Home";
import { KeyValueDeletePage } from "./ui/pages/Secrets/KeyValue/KeyValueDelete";
import { KeyValueNewPage } from "./ui/pages/Secrets/KeyValue/KeyValueNew";
import { KeyValueSecretEditPage } from "./ui/pages/Secrets/KeyValue/KeyValueSecretsEdit";
import { KeyValueSecretPage } from "./ui/pages/Secrets/KeyValue/KeyValueSecret";
import { KeyValueVersionsPage } from "./ui/pages/Secrets/KeyValue/KeyValueVersions";
import { KeyValueViewPage } from "./ui/pages/Secrets/KeyValue/KeyValueView";
import { LoginPage } from "./ui/pages/Login";
import { MePage } from "./ui/pages/Me";
import { NewKVEnginePage } from "./ui/pages/Secrets/NewEngines/NewKVEngine";
import { NewSecretsEnginePage } from "./ui/pages/Secrets/NewSecretsEngine";
import { NewTOTPEnginePage } from "./ui/pages/Secrets/NewEngines/NewTOTPEngine";
import { NewTransitEnginePage } from "./ui/pages/Secrets/NewEngines/NewTransitEngine";
import { NewTransitKeyPage } from "./ui/pages/Secrets/Transit/NewTransitKey";
import { Page } from "./types/Page";
import { PoliciesHomePage } from "./ui/pages/Policies/PoliciesHome";
import { PolicyDeletePage } from "./ui/pages/Policies/PolicyDelete";
import { PolicyEditPage } from "./ui/pages/Policies/PolicyEdit";
import { PolicyNewPage } from "./ui/pages/Policies/PolicyNew";
import { PolicyViewPage } from "./ui/pages/Policies/PolicyView";
import { PwGenPage } from "./ui/pages/PwGen";
import { SecretsHomePage } from "./ui/pages/Secrets/SecretsHome";
import { SetLanguagePage } from "./ui/pages/SetLanguage";
import { SetVaultURLPage } from "./ui/pages/SetVaultURL";
import { TOTPDeletePage } from "./ui/pages/Secrets/TOTP/TOTPDelete";
import { TOTPNewPage } from "./ui/pages/Secrets/TOTP/TOTPNew";
import { TOTPViewPage } from "./ui/pages/Secrets/TOTP/TOTPView";
import { TransitDecryptPage } from "./ui/pages/Secrets/Transit/TransitDecrypt";
import { TransitEncryptPage } from "./ui/pages/Secrets/Transit/TransitEncrypt";
import { TransitRewrapPage } from "./ui/pages/Secrets/Transit/TransitRewrap";
import { TransitViewPage } from "./ui/pages/Secrets/Transit/TransitView";
import { TransitViewSecretPage } from "./ui/pages/Secrets/Transit/TransitViewSecret";
import { UnsealPage } from "./ui/pages/Unseal";
import { UserPassUserDeletePage } from "./ui/pages/Access/Auth/userpass/UserPassUserDelete";
import { UserPassUserEditPage } from "./ui/pages/Access/Auth/userpass/UserPassUserEdit";
import { UserPassUserNewPage } from "./ui/pages/Access/Auth/userpass/UserPassUserNew";
import { UserPassUserViewPage } from "./ui/pages/Access/Auth/userpass/UserPassUserView";
import { UserPassUsersListPage } from "./ui/pages/Access/Auth/userpass/UserPassUsersList";
import { getObjectKeys } from "./utils";
type pagesList = {
[key: string]: Page;
};
export const allPages: pagesList = {
HOME: new HomePage(),
LOGIN: new LoginPage(),
SET_VAULT_URL: new SetVaultURLPage(),
UNSEAL: new UnsealPage(),
SET_LANGUAGE: new SetLanguagePage(),
ME: new MePage(),
PW_GEN: new PwGenPage(),
POLICIES_HOME: new PoliciesHomePage(),
POLICY_VIEW: new PolicyViewPage(),
POLICY_NEW: new PolicyNewPage(),
POLICY_EDIT: new PolicyEditPage(),
POLICY_DELETE: new PolicyDeletePage(),
ACCESS_HOME: new AccessHomePage(),
AUTH_HOME: new AuthHomePage(),
AUTH_VIEW_CONFIG: new AuthViewConfigPage(),
USERPASS_USERS_LIST: new UserPassUsersListPage(),
USERPASS_USER_VIEW: new UserPassUserViewPage(),
USERPASS_USER_EDIT: new UserPassUserEditPage(),
USERPASS_USER_NEW: new UserPassUserNewPage(),
USERPASS_USER_DELETE: new UserPassUserDeletePage(),
SECRETS_HOME: new SecretsHomePage(),
TOTP_VIEW: new TOTPViewPage(),
TOTP_NEW: new TOTPNewPage(),
TOTP_DELETE: new TOTPDeletePage(),
TRANSIT_VIEW: new TransitViewPage(),
TRANSIT_NEW_KEY: new NewTransitKeyPage(),
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(),
KEY_VALUE_NEW_SECRET: new KeyValueNewPage(),
KEY_VALUE_DELETE: new KeyValueDeletePage(),
KEY_VALUE_SECRET_EDIT: new KeyValueSecretEditPage(),
DELETE_SECRET_ENGINE: new DeleteSecretsEnginePage(),
NEW_SECRETS_ENGINE: new NewSecretsEnginePage(),
NEW_KV_ENGINE: new NewKVEnginePage(),
NEW_TOTP_ENGINE: new NewTOTPEnginePage(),
NEW_TRANSIT_ENGINE: new NewTransitEnginePage(),
};
// This should implement all o PageListType
class PageList {
constructor(pages: pagesList) {
this.pages = pages;
}
private pages: pagesList;
async getPageIDs(): Promise<string[]> {
return getObjectKeys(this.pages);
}
async getPage(pageID: string): Promise<PageType> {
return this.pages[pageID];
}
}
export const pageList = new PageList(allPages);
//import { PageType } from "./pagerouter/PageType";
//
//import { AccessHomePage } from "./ui/pages/Access/AccessHome";
//import { AuthHomePage } from "./ui/pages/Access/Auth/AuthHome";
//import { AuthViewConfigPage } from "./ui/pages/Access/Auth/AuthViewConfig";
//import { DeleteSecretsEnginePage } from "./ui/pages/Secrets/DeleteSecretsEngine";
//import { HomePage } from "./ui/pages/Home";
//import { KeyValueDeletePage } from "./ui/pages/Secrets/KeyValue/KeyValueDelete";
//import { KeyValueNewPage } from "./ui/pages/Secrets/KeyValue/KeyValueNew";
//import { KeyValueSecretEditPage } from "./ui/pages/Secrets/KeyValue/KeyValueSecretsEdit";
//import { KeyValueSecretPage } from "./ui/pages/Secrets/KeyValue/KeyValueSecret";
//import { KeyValueVersionsPage } from "./ui/pages/Secrets/KeyValue/KeyValueVersions";
//import { KeyValueViewPage } from "./ui/pages/Secrets/KeyValue/KeyValueView";
//import { LoginPage } from "./ui/pages/Login";
//import { MePage } from "./ui/pages/Me";
//import { NewKVEnginePage } from "./ui/pages/Secrets/NewEngines/NewKVEngine";
//import { NewSecretsEnginePage } from "./ui/pages/Secrets/NewSecretsEngine";
//import { NewTOTPEnginePage } from "./ui/pages/Secrets/NewEngines/NewTOTPEngine";
//import { NewTransitEnginePage } from "./ui/pages/Secrets/NewEngines/NewTransitEngine";
//import { NewTransitKeyPage } from "./ui/pages/Secrets/Transit/NewTransitKey";
//import { Page } from "./types/Page";
//import { PoliciesHomePage } from "./ui/pages/Policies/PoliciesHome";
//import { PolicyDeletePage } from "./ui/pages/Policies/PolicyDelete";
//import { PolicyEditPage } from "./ui/pages/Policies/PolicyEdit";
//import { PolicyNewPage } from "./ui/pages/Policies/PolicyNew";
//import { PolicyViewPage } from "./ui/pages/Policies/PolicyView";
//import { PwGenPage } from "./ui/pages/PwGen";
//import { SecretsHomePage } from "./ui/pages/Secrets/SecretsHome";
//import { SetLanguagePage } from "./ui/pages/SetLanguage";
//import { SetVaultURLPage } from "./ui/pages/SetVaultURL";
//import { TOTPDeletePage } from "./ui/pages/Secrets/TOTP/TOTPDelete";
//import { TOTPNewPage } from "./ui/pages/Secrets/TOTP/TOTPNew";
//import { TOTPViewPage } from "./ui/pages/Secrets/TOTP/TOTPView";
//import { TransitDecryptPage } from "./ui/pages/Secrets/Transit/TransitDecrypt";
//import { TransitEncryptPage } from "./ui/pages/Secrets/Transit/TransitEncrypt";
//import { TransitRewrapPage } from "./ui/pages/Secrets/Transit/TransitRewrap";
//import { TransitViewPage } from "./ui/pages/Secrets/Transit/TransitView";
//import { TransitViewSecretPage } from "./ui/pages/Secrets/Transit/TransitViewSecret";
//import { UnsealPage } from "./ui/pages/Unseal";
//import { UserPassUserDeletePage } from "./ui/pages/Access/Auth/userpass/UserPassUserDelete";
//import { UserPassUserEditPage } from "./ui/pages/Access/Auth/userpass/UserPassUserEdit";
//import { UserPassUserNewPage } from "./ui/pages/Access/Auth/userpass/UserPassUserNew";
//import { UserPassUserViewPage } from "./ui/pages/Access/Auth/userpass/UserPassUserView";
//import { UserPassUsersListPage } from "./ui/pages/Access/Auth/userpass/UserPassUsersList";
//import { getObjectKeys } from "./utils";
//
//type pagesList = {
// [key: string]: Page;
//};
//
//export const allPages: pagesList = {
// HOME: new HomePage(),
// LOGIN: new LoginPage(),
// SET_VAULT_URL: new SetVaultURLPage(),
// UNSEAL: new UnsealPage(),
// SET_LANGUAGE: new SetLanguagePage(),
// ME: new MePage(),
// PW_GEN: new PwGenPage(),
//
// POLICIES_HOME: new PoliciesHomePage(),
// POLICY_VIEW: new PolicyViewPage(),
// POLICY_NEW: new PolicyNewPage(),
// POLICY_EDIT: new PolicyEditPage(),
// POLICY_DELETE: new PolicyDeletePage(),
//
// ACCESS_HOME: new AccessHomePage(),
//
// AUTH_HOME: new AuthHomePage(),
// AUTH_VIEW_CONFIG: new AuthViewConfigPage(),
//
// USERPASS_USERS_LIST: new UserPassUsersListPage(),
// USERPASS_USER_VIEW: new UserPassUserViewPage(),
// USERPASS_USER_EDIT: new UserPassUserEditPage(),
// USERPASS_USER_NEW: new UserPassUserNewPage(),
// USERPASS_USER_DELETE: new UserPassUserDeletePage(),
//
// SECRETS_HOME: new SecretsHomePage(),
//
// TOTP_VIEW: new TOTPViewPage(),
// TOTP_NEW: new TOTPNewPage(),
// TOTP_DELETE: new TOTPDeletePage(),
//
// TRANSIT_VIEW: new TransitViewPage(),
// TRANSIT_NEW_KEY: new NewTransitKeyPage(),
// 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(),
// KEY_VALUE_NEW_SECRET: new KeyValueNewPage(),
// KEY_VALUE_DELETE: new KeyValueDeletePage(),
// KEY_VALUE_SECRET_EDIT: new KeyValueSecretEditPage(),
//
// DELETE_SECRET_ENGINE: new DeleteSecretsEnginePage(),
//
// NEW_SECRETS_ENGINE: new NewSecretsEnginePage(),
// NEW_KV_ENGINE: new NewKVEnginePage(),
// NEW_TOTP_ENGINE: new NewTOTPEnginePage(),
// NEW_TRANSIT_ENGINE: new NewTransitEnginePage(),
//};
//
//// This should implement all o PageListType
//class PageList {
// constructor(pages: pagesList) {
// this.pages = pages;
// }
//
// private pages: pagesList;
//
// async getPageIDs(): Promise<string[]> {
// return getObjectKeys(this.pages);
// }
// async getPage(pageID: string): Promise<PageType> {
// return this.pages[pageID];
// }
//}
//
//export const pageList = new PageList(allPages);

View file

@ -1,9 +1,9 @@
import { appendAPIURL, checkResponse, getHeaders } from "../apiUtils";
import { removeDoubleSlash } from "../../utils";
import { getMount } from "../sys/getMounts";
export async function createOrUpdateSecret(
baseMount: string,
secretMountType: string,
secretPath: string[],
name: string,
data: Record<string, unknown>,
@ -11,7 +11,8 @@ export async function createOrUpdateSecret(
let secretURL = "";
let APIData = {};
if (secretMountType == "kv-v2") {
const mountInfo = await getMount(baseMount);
if (mountInfo.options.version == "2") {
secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
APIData = { data: data };
} else {

View file

@ -1,40 +1,26 @@
import { appendAPIURL, checkResponse, getHeaders } from "../apiUtils";
import { removeDoubleSlash } from "../../utils";
import { getMount } from "../sys/getMounts";
export async function deleteSecret(
baseMount: string,
secretMountType: string,
secretPath: string[],
name: string,
version: string | null = null,
): Promise<void> {
let secretURL = "";
let request;
if (secretMountType == "kv-v2" && version != null) {
secretURL = `/v1/${baseMount}/delete/${secretPath.join("")}/${name}`;
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
request = new Request(appendAPIURL(secretURL), {
method: "POST",
headers: {
...getHeaders(),
"Content-Type": "application/json",
},
body: version != null ? JSON.stringify({ versions: [version] }) : "{}",
});
const mountInfo = await getMount(baseMount);
if (mountInfo.options.version == "2") {
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`;
} else {
if (secretMountType == "kv-v2") {
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`;
} else {
secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
}
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
request = new Request(appendAPIURL(secretURL), {
method: "DELETE",
headers: getHeaders(),
});
secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
}
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
request = new Request(appendAPIURL(secretURL), {
method: "DELETE",
headers: getHeaders(),
});
const resp = await fetch(request);
await checkResponse(resp);
}

View file

@ -1,4 +1,5 @@
import { appendAPIURL, checkResponse, getHeaders } from "../apiUtils";
import { getMount } from "../sys/getMounts";
export async function getSecretKV1(
baseMount: string,
@ -21,12 +22,10 @@ export async function getSecretKV2(
baseMount: string,
secretPath: string[],
name: string,
version: string | null = null,
): Promise<Record<string, unknown>> {
let secretURL = "";
secretURL = `/v1/${baseMount}/data/${secretPath.join("")}/${name}`;
if (version != null) secretURL += `?version=${version}`;
const request = new Request(appendAPIURL(secretURL), {
headers: getHeaders(),
@ -41,13 +40,12 @@ export async function getSecretKV2(
export async function getSecret(
baseMount: string,
secretMountType: string,
secretPath: string[],
name: string,
version: string | null = null,
): Promise<Record<string, unknown>> {
if (secretMountType == "kv-v2") {
return await getSecretKV2(baseMount, secretPath, name, version);
const mountInfo = await getMount(baseMount);
if (mountInfo.options.version == "2") {
return await getSecretKV2(baseMount, secretPath, name);
} else {
return await getSecretKV1(baseMount, secretPath, name);
}

View file

@ -2,10 +2,13 @@ import { appendAPIURL, checkResponse, getHeaders } from "../apiUtils";
export async function getSecrets(
baseMount: string,
secretMountType: string,
secretPath: string[],
): Promise<string[]> {
let secretURL = "";
// TODO: FIX THIS
let secretMountType = "kv-v2"
if (secretMountType == "kv-v2") {
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}?list=true`;
} else {

View file

@ -21,3 +21,15 @@ export async function getMounts(): Promise<MountsType> {
const data = (await resp.json()) as { data: { secret: MountsType } };
return data.data.secret;
}
export async function getMount(mountName: string): Promise<MountType> {
const request = new Request(appendAPIURL("/v1/sys/internal/ui/mounts/" + mountName), {
headers: getHeaders(),
});
const resp = await fetch(request);
await checkResponse(resp);
const data = (await resp.json()) as { data: MountType };
return data.data;
}

View file

@ -25,59 +25,110 @@ import translations from "./translations/index.mjs";
import { PageRouter } from "./pagerouter/PageRouter";
import { formatDistance } from "./formatDistance";
import { getSealStatus } from "./api/sys/getSealStatus";
import { pageList } from "./allPages";
//import { pageList } from "./allPages";
import { pageState } from "./globalPageState";
import { playground } from "./playground";
import { reloadNavBar } from "./ui/elements/NavBar";
import { render } from "preact";
import { NavBar } from "./ui/elements/NavBar";
import { render, Component } from "preact";
import i18next from "i18next";
import Router from 'preact-router';
import { Home } from "./ui/pages/Home";
import { Me } from "./ui/pages/Me";
import { Login } from "./ui/pages/Login";
import { PasswordGenerator } from "./ui/pages/PwGen";
import { SetVaultURL } from "./ui/pages/SetVaultURL";
import { Unseal } from "./ui/pages/Unseal";
import { SetLanguage } from "./ui/pages/SetLanguage";
import { Secrets } from "./ui/pages/Secrets/SecretsHome";
import { TOTPView } from "./ui/pages/Secrets/TOTP/TOTPView";
import { TOTPNew } from "./ui/pages/Secrets/TOTP/TOTPNew";
import { TOTPDelete } from "./ui/pages/Secrets/TOTP/TOTPDelete";
import { NewSecretsEngine } from "./ui/pages/Secrets/NewSecretsEngine";
import { NewKVEngine } from "./ui/pages/Secrets/NewEngines/NewKVEngine";
import { NewTOTPEngine } from "./ui/pages/Secrets/NewEngines/NewTOTPEngine";
import { NewTransitEngine } from "./ui/pages/Secrets/NewEngines/NewTransitEngine";
import { DeleteSecretsEngine } from "./ui/pages/Secrets/DeleteSecretsEngine";
import { KeyValueView } from "./ui/pages/Secrets/KeyValue/KeyValueView";
import { KeyValueSecret } from "./ui/pages/Secrets/KeyValue/KeyValueSecret";
import { KeyValueSecretEdit } from "./ui/pages/Secrets/KeyValue/KeyValueSecretsEdit";
import { KeyValueDelete } from "./ui/pages/Secrets/KeyValue/KeyValueDelete";
async function onLoad(): Promise<void> {
const Main = () => (
<Router>
<Home path="/" state={pageState} />
<Me path="/me" state={pageState} />
<Login path="/login" state={pageState} />
<PasswordGenerator path="/pw_gen" />
<SetVaultURL path="/set_vault_url" state={pageState} />
<Unseal path="/unseal" state={pageState} />
<SetLanguage path="/set_language" state={pageState} />
<Secrets path="/secrets" state={pageState} />
<DeleteSecretsEngine path="/secrets/delete_engine/:mount" state={pageState} />
<NewSecretsEngine path="/secrets/new_secrets_engine" />
<NewKVEngine path="/secrets/new_secrets_engine/kv" />
<NewTOTPEngine path="/secrets/new_secrets_engine/totp" />
<NewTransitEngine path="/secrets/new_secrets_engine/trasit" />
<TOTPView path="/secrets/totp/list/:mount" state={pageState} />
<TOTPNew path="/secrets/totp/new/:mount" state={pageState} />
<TOTPDelete path="/secrets/totp/delete/:mount/:item" state={pageState} />
<KeyValueView path="/secrets/kv/list/:mount+" state={pageState} />
<KeyValueSecret path="/secrets/kv/view/:item/:mount+" state={pageState} />
<KeyValueSecretEdit path="/secrets/kv/edit/:item/:mount+" state={pageState} />
<KeyValueDelete path="/secrets/kv/delete/:item/:mount+" state={pageState} />
<div default><p>PAGE NOT YET IMPLEMENTED</p></div>
</Router>
);
render(
<>
<div id="navBarBox" />
<NavBar />
<div class="uk-container uk-container-medium uk-align-center">
<div class="uk-card uk-card-body">
<h3 class="uk-card-title" id="pageTitle" />
<div id="pageContent" />
<Main />
</div>
</div>
</>,
document.body,
);
const pageRouter = new PageRouter({
pageList: pageList,
state: pageState,
pageTitleElement: document.querySelector("#pageTitle"),
pageContentElement: document.querySelector("#pageContent"),
resetElementContent: !true,
onPageChange: async function () {
pageState.currentPage = await pageRouter.getCurrentPageID();
document.documentElement.dir = pageState.pageDirection;
},
});
reloadNavBar(pageRouter);
//const pageRouter = new PageRouter({
// pageList: pageList,
// state: pageState,
// pageTitleElement: document.querySelector("#pageTitle"),
// pageContentElement: document.querySelector("#pageContent"),
// resetElementContent: !true,
// onPageChange: async function () {
// pageState.currentPage = await pageRouter.getCurrentPageID();
// document.documentElement.dir = pageState.pageDirection;
// },
//});
//
//reloadNavBar(pageRouter);
if (process.env.NODE_ENV == "development") {
await playground(pageRouter);
// await playground(pageRouter);
}
await pageRouter.changePage(pageState.currentPage);
//await pageRouter.changePage(pageState.currentPage);
setInterval(async () => {
if ((await pageRouter.getCurrentPageID()) != "UNSEAL") {
if (pageState.apiURL.length != 0) {
return;
}
const sealStatus = await getSealStatus();
if (sealStatus.sealed) {
await pageRouter.changePage("UNSEAL");
return;
}
}
}, 5000);
//setInterval(async () => {
// if ((await pageRouter.getCurrentPageID()) != "UNSEAL") {
// if (pageState.apiURL.length != 0) {
// return;
// }
// const sealStatus = await getSealStatus();
// if (sealStatus.sealed) {
// await pageRouter.changePage("UNSEAL");
// return;
// }
// }
//}, 5000);
}
document.addEventListener(

View file

@ -5,35 +5,36 @@ import { lookupSelf } from "./api/sys/lookupSelf";
import ClipboardJS from "clipboard";
import UIkit from "uikit";
import i18next from "i18next";
import { route } from "preact-router";
async function prePageChecksReal(router: PageRouter) {
const state = router.state as PageState;
async function prePageChecksReal(state: PageState) {
if (state.language.length == 0) {
await router.changePage("SET_LANGUAGE");
route("/set_language", true);
throw new Error("Language Not Set");
}
if (!state.apiURL) {
await router.changePage("SET_VAULT_URL");
route("/set_vault_url", true);
throw new Error("Vault URL Not Set");
}
const sealStatus = await getSealStatus();
if (sealStatus.sealed) {
await router.changePage("UNSEAL");
route("/unseal", true);
throw new Error("Vault Sealed");
}
try {
await lookupSelf();
} catch (e) {
await router.changePage("LOGIN");
route("/login", true);
throw e;
}
}
export async function prePageChecks(router: PageRouter): Promise<boolean> {
export async function prePageChecks(state: PageState): Promise<boolean> {
try {
await prePageChecksReal(router);
await prePageChecksReal(state);
} catch (e) {
console.log("OHNO", e);
return false;

View file

@ -35,6 +35,7 @@ module.exports = {
me_copy_token_btn: "Copy Token",
me_renew_lease_btn: "Renew Token Lease",
me_change_language_btn: "Change Language",
me_set_vault_url_btn: "Set Vault URL",
// Home Page
home_page_title: "Home",

View file

@ -0,0 +1,6 @@
import { PageState } from "../state/PageState";
export type DefaultPageProps = {
state: PageState;
matches?: {[key: string]: string}
};

View file

@ -1,29 +1,24 @@
import { JSX, render } from "preact";
import { PageRouter } from "../../pagerouter/PageRouter";
import i18next from "i18next";
import { getCurrentUrl, route } from "preact-router";
export type NavBarProps = {
router: PageRouter;
};
export function NavBar(props: NavBarProps): JSX.Element {
export function NavBar(): JSX.Element {
return (
<nav class="uk-navbar uk-navbar-container">
<div class="uk-navbar-left">
<ul class="uk-navbar-nav">
<li>
<a
onClick={async () => {
await props.router.changePage("HOME");
}}
>
<a href="/">
{i18next.t("home_btn")}
</a>
</li>
<li>
<a
onClick={async () => {
await props.router.goBack();
window.history.back()
// maybe???
}}
>
{i18next.t("back_btn")}
@ -32,7 +27,7 @@ export function NavBar(props: NavBarProps): JSX.Element {
<li>
<a
onClick={async () => {
await props.router.refresh();
route(getCurrentUrl(), false);
}}
>
{i18next.t("refresh_btn")}
@ -45,7 +40,7 @@ export function NavBar(props: NavBarProps): JSX.Element {
<li>
<a
onClick={async () => {
await props.router.changePage("ME");
route("/me", true);
}}
>
{i18next.t("me_btn")}
@ -56,7 +51,3 @@ export function NavBar(props: NavBarProps): JSX.Element {
</nav>
);
}
export function reloadNavBar(router: PageRouter): void {
render(<NavBar router={router} />, document.querySelector("#navBarBox"));
}

View file

@ -0,0 +1,9 @@
import { JSX } from "preact";
export type PageTitleProps = {
title: string;
};
export function PageTitle(props: PageTitleProps): JSX.Element {
return <h3 class="uk-card-title" id="pageTitle">{props.title}</h3>;
}

View file

@ -4,6 +4,7 @@ import { Tile } from "../../elements/Tile";
import { notImplemented, prePageChecks } from "../../../pageUtils";
import { render } from "preact";
import i18next from "i18next";
import { PageState } from "../../../state/PageState";
export class AccessHomePage extends Page {
constructor() {
@ -14,7 +15,7 @@ export class AccessHomePage extends Page {
}
async render(): Promise<void> {
this.router.pageContentElement.innerHTML = "";
if (!(await prePageChecks(this.router))) return;
if (!(await prePageChecks(this.router.state as PageState))) return;
render(
<Grid size={GridSizes.MATCHING_TWO_ROWS}>

View file

@ -6,21 +6,25 @@ import { TokenInfo } from "../../api/types/token";
import { getCapabilitiesPath } from "../../api/sys/getCapabilities";
import { lookupSelf } from "../../api/sys/lookupSelf";
import { prePageChecks, setErrorText } from "../../pageUtils";
import { render } from "preact";
import { Component, JSX, render } from "preact";
import i18next from "i18next";
import { PageState } from "../../state/PageState";
import { route } from "preact-router";
import { PageTitle } from "../elements/PageTitle";
export class HomePage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
await this.router.setPageContent("");
if (!(await prePageChecks(this.router))) return;
type HomeProps = {
state: PageState;
}
this.state.baseMount = "";
this.state.secretPath = [];
this.state.secretItem = "";
this.state.secretVersion = null;
type HomeState = {
selfTokenInfo: TokenInfo;
authCaps: string[];
policiesCaps: string[];
}
export class Home extends Component<HomeProps, HomeState> {
async componentDidMount() {
if (!(await prePageChecks(this.props.state))) return;
let selfTokenInfo: TokenInfo;
try {
@ -29,8 +33,9 @@ export class HomePage extends Page {
const error = e as Error;
setErrorText(error.message);
if (error.message == "permission denied") {
this.state.token = "";
await this.router.changePage("LOGIN");
this.props.state.token = "";
route('/login', true);
return;
}
}
@ -38,61 +43,67 @@ export class HomePage extends Page {
const authCaps = caps["sys/auth"];
const policiesCaps = caps["sys/policies"];
render(
<div>
<ul id="textList" class="uk-nav">
<li>
<span>{i18next.t("home_vaulturl_text", { text: this.state.apiURL })}</span>
</li>
<li>
<a
onClick={async () => {
await this.router.changePage("PW_GEN");
}}
>
{i18next.t("home_password_generator_btn")}
</a>
</li>
<li>
<span>
{i18next.t("home_your_token_expires_in", {
date: new Date(selfTokenInfo.expire_time),
})}
</span>
</li>
</ul>
<Margin>
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
<Tile
title={i18next.t("home_secrets_title")}
description={i18next.t("home_secrets_description")}
icon="file-edit"
onclick={async () => {
await this.router.changePage("SECRETS_HOME");
}}
/>
<Tile
title={i18next.t("home_access_title")}
description={i18next.t("home_access_description")}
icon="users"
disabled={!authCaps.includes("read")}
onclick={async () => {
await this.router.changePage("ACCESS_HOME");
}}
/>
<Tile
title={i18next.t("home_policies_title")}
description={i18next.t("home_policies_description")}
icon="pencil"
disabled={!policiesCaps.includes("read")}
onclick={async () => {
await this.router.changePage("POLICIES_HOME");
}}
/>
</Grid>
</Margin>
</div>,
this.router.pageContentElement,
this.setState({
selfTokenInfo: selfTokenInfo,
authCaps: authCaps,
policiesCaps: policiesCaps,
})
}
render(): JSX.Element {
return this.state.selfTokenInfo && (
<>
<PageTitle title={this.name} />
<div>
<ul id="textList" class="uk-nav">
<li>
<span>{i18next.t("home_vaulturl_text", { text: this.props.state.apiURL })}</span>
</li>
<li>
<a href="/pw_gen">
{i18next.t("home_password_generator_btn")}
</a>
</li>
<li>
<span>
{i18next.t("home_your_token_expires_in", {
date: new Date(this.state.selfTokenInfo.expire_time),
})}
</span>
</li>
</ul>
<Margin>
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
<Tile
title={i18next.t("home_secrets_title")}
description={i18next.t("home_secrets_description")}
icon="file-edit"
onclick={async () => {
route("/secrets");
}}
/>
<Tile
title={i18next.t("home_access_title")}
description={i18next.t("home_access_description")}
icon="users"
disabled={!this.state.authCaps.includes("read")}
onclick={async () => {
route("/access");
}}
/>
<Tile
title={i18next.t("home_policies_title")}
description={i18next.t("home_policies_description")}
icon="pencil"
disabled={!this.state.policiesCaps.includes("read")}
onclick={async () => {
route("/policies");
}}
/>
</Grid>
</Margin>
</div>
</>
);
}

View file

@ -7,8 +7,12 @@ import { lookupSelf } from "../../api/sys/lookupSelf";
import { setErrorText } from "../../pageUtils";
import { usernameLogin } from "../../api/auth/usernameLogin";
import i18next from "i18next";
import { route } from "preact-router";
import { PageTitle } from "../elements/PageTitle";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { PageState } from "../../state/PageState";
export class TokenLoginForm extends Component<{ page: Page }, unknown> {
export class TokenLoginForm extends Component<{state: PageState}, unknown> {
constructor() {
super();
}
@ -36,13 +40,12 @@ export class TokenLoginForm extends Component<{ page: Page }, unknown> {
}
async onSubmit(data: FormData): Promise<void> {
const page = this.props.page;
const token = data.get("token");
page.state.token = token as string;
this.props.state.token = token as string;
try {
await lookupSelf();
await page.router.changePage("HOME");
route("/");
} catch (e: unknown) {
const error = e as Error;
document.querySelector("#tokenInput").classList.add("uk-form-danger");
@ -55,7 +58,7 @@ export class TokenLoginForm extends Component<{ page: Page }, unknown> {
}
}
export class UsernameLoginForm extends Component<{ page: Page }, unknown> {
export class UsernameLoginForm extends Component<{state: PageState}, unknown> {
constructor() {
super();
}
@ -93,15 +96,13 @@ export class UsernameLoginForm extends Component<{ page: Page }, unknown> {
}
async onSubmit(data: FormData): Promise<void> {
const page = this.props.page;
try {
const res = await usernameLogin(
data.get("username") as string,
data.get("password") as string,
);
page.state.token = res;
await page.router.changePage("HOME");
this.props.state.token = res;
route("/");
} catch (e: unknown) {
const error = e as Error;
document.querySelector("#usernameInput").classList.add("uk-form-danger");
@ -111,32 +112,31 @@ export class UsernameLoginForm extends Component<{ page: Page }, unknown> {
}
}
export class LoginPage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
render(
<div>
<ul class="uk-subnav uk-subnav-pill" uk-switcher=".switcher-container">
<li>
<a>{i18next.t("log_in_with_token")}</a>
</li>
<li>
<a>{i18next.t("log_in_with_username")}</a>
</li>
</ul>
<p id="errorText" class="uk-text-danger" />
<ul class="uk-switcher uk-margin switcher-container">
<li>
<TokenLoginForm page={this} />
</li>
<li>
<UsernameLoginForm page={this} />
</li>
</ul>
</div>,
this.router.pageContentElement,
export class Login extends Component<DefaultPageProps> {
render(): JSX.Element {
return (
<>
<PageTitle title={this.name} />
<div>
<ul class="uk-subnav uk-subnav-pill" uk-switcher=".switcher-container">
<li>
<a>{i18next.t("log_in_with_token")}</a>
</li>
<li>
<a>{i18next.t("log_in_with_username")}</a>
</li>
</ul>
<p id="errorText" class="uk-text-danger" />
<ul class="uk-switcher uk-margin switcher-container">
<li>
<TokenLoginForm state={this.props.state} />
</li>
<li>
<UsernameLoginForm state={this.props.state} />
</li>
</ul>
</div>
</>
);
}

View file

@ -1,11 +1,13 @@
import { Component, JSX, createRef, render } from "preact";
import { Page } from "../../types/Page";
import { Component, JSX, createRef } from "preact";
import { addClipboardNotifications, prePageChecks, setErrorText } from "../../pageUtils";
import { getCapsPath } from "../../api/sys/getCapabilities";
import { renewSelf } from "../../api/sys/renewSelf";
import { sealVault } from "../../api/sys/sealVault";
import ClipboardJS from "clipboard";
import i18next from "i18next";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { route } from "preact-router";
import { PageTitle } from "../elements/PageTitle";
export class CopyLink extends Component<{ text: string; data: string }, unknown> {
linkRef = createRef();
@ -24,14 +26,23 @@ export class CopyLink extends Component<{ text: string; data: string }, unknown>
}
}
export class MePage extends Page {
type MeState = {
loaded: boolean;
canSealVault: boolean;
}
export class Me extends Component<DefaultPageProps, MeState> {
defaultState = { loaded: false, canSealVault: false }
constructor() {
super();
this.state = this.defaultState;
}
async render(): Promise<void> {
if (!(await prePageChecks(this.router))) return;
componentWillUnmount() {
this.setState(this.defaultState);
}
async componentDidMount() {
let canSealVault = false;
try {
const caps = await getCapsPath("sys/seal");
@ -40,59 +51,69 @@ export class MePage extends Page {
canSealVault = false;
}
render(
<ul class="uk-nav">
<li>
<a
onClick={async () => {
this.state.token = "";
await this.router.changePage("HOME");
}}
>
{i18next.t("me_log_out_btn")}
</a>
</li>
<li>
<CopyLink text={i18next.t("me_copy_token_btn")} data={this.state.token} />
</li>
<li>
<a
onClick={async () => {
try {
await renewSelf();
await this.router.changePage("HOME");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}}
>
{i18next.t("me_renew_lease_btn")}
</a>
</li>
{canSealVault && (
this.setState({
loaded: true,
canSealVault: canSealVault,
});
}
render(): JSX.Element {
return this.state.loaded && (
<>
<PageTitle title={this.name} />
<ul class="uk-nav">
<li>
<a
onClick={async () => {
await sealVault();
await this.router.changePage("UNSEAL");
this.props.state.token = "";
route("/");
}}
>
{i18next.t("me_seal_vault_btn")}
{i18next.t("me_log_out_btn")}
</a>
</li>
)}
<li>
<a
onClick={async () => {
await this.router.changePage("SET_LANGUAGE");
}}
>
{i18next.t("me_change_language_btn")}
</a>
</li>
</ul>,
this.router.pageContentElement,
<li>
<CopyLink text={i18next.t("me_copy_token_btn")} data={this.props.state.token} />
</li>
<li>
<a
onClick={async () => {
try {
await renewSelf();
route("/");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}}
>
{i18next.t("me_renew_lease_btn")}
</a>
</li>
{this.state.canSealVault && (
<li>
<a
onClick={async () => {
await sealVault();
route("/unseal", true);
}}
>
{i18next.t("me_seal_vault_btn")}
</a>
</li>
)}
<li>
<a href="/set_language">
{i18next.t("me_change_language_btn")}
</a>
</li>
<li>
<a href="/set_vault_url">
{i18next.t("me_set_vault_url_btn")}
</a>
</li>
</ul>
</>
);
}

View file

@ -4,6 +4,7 @@ import { getPolicies } from "../../../api/sys/policies/getPolicies";
import { prePageChecks } from "../../../pageUtils";
import { render } from "preact";
import i18next from "i18next";
import { PageState } from "../../../state/PageState";
export class PoliciesHomePage extends Page {
constructor() {
@ -14,7 +15,7 @@ export class PoliciesHomePage extends Page {
}
async render(): Promise<void> {
await this.router.setPageContent("");
if (!(await prePageChecks(this.router))) return;
if (!(await prePageChecks(this.router.state as PageState))) return;
let policies = await getPolicies();
policies = policies.sort();

View file

@ -5,6 +5,7 @@ import { getPolicy } from "../../../api/sys/policies/getPolicy";
import { prePageChecks } from "../../../pageUtils";
import { render } from "preact";
import i18next from "i18next";
import { PageState } from "../../../state/PageState";
export class PolicyViewPage extends Page {
constructor() {
@ -15,7 +16,7 @@ export class PolicyViewPage extends Page {
}
async render(): Promise<void> {
await this.router.setPageContent("");
if (!(await prePageChecks(this.router))) return;
if (!(await prePageChecks(this.router.state as PageState))) return;
const policy = await getPolicy(this.state.policyItem);

View file

@ -1,5 +1,6 @@
import { Component, JSX, createRef, render } from "preact";
import { CopyableInputBox } from "../elements/CopyableInputBox";
import { PageTitle } from "../elements/PageTitle";
import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin";
import { Page } from "../../types/Page";
@ -51,7 +52,7 @@ type PasswordGeneratorState = {
alphabet: string;
};
export class PasswordGenerator extends Component<unknown, PasswordGeneratorState> {
export class PasswordGenerator extends Component<{}, PasswordGeneratorState> {
constructor() {
super();
this.state = {
@ -89,65 +90,54 @@ export class PasswordGenerator extends Component<unknown, PasswordGeneratorState
// createRef
render(): JSX.Element {
return (
<Form onSubmit={() => this.onSubmit()}>
<Margin>
<h4>{this.getPasswordLengthText(this.state.length)}</h4>
</Margin>
<Margin>
<input
class="uk-range uk-width-1-2"
name="length"
type="range"
value={this.state.length}
max={passwordLengthMax.toString()}
min={passwordLengthMin.toString()}
ref={this.passwordLengthSlider}
onInput={() => {
this.updateLength();
}}
/>
</Margin>
<Margin>
<select
class="uk-select uk-width-1-2"
ref={this.alphabetSelector}
onInput={() => {
this.updateAlphabet();
}}
>
<option value={alphabets.SECURE}>a-z a-Z 0-9 specials</option>
<option value={alphabets.SMOL}>a-z 0-9</option>
<option value={alphabets.HEX}>A-F 1-9</option>
</select>
</Margin>
<>
<PageTitle title={i18next.t("password_generator_title")} />
<Form onSubmit={() => this.onSubmit()}>
<Margin>
<h4>{this.getPasswordLengthText(this.state.length)}</h4>
</Margin>
<Margin>
<input
class="uk-range uk-width-1-2"
name="length"
type="range"
value={this.state.length}
max={passwordLengthMax.toString()}
min={passwordLengthMin.toString()}
ref={this.passwordLengthSlider}
onInput={() => {
this.updateLength();
}}
/>
</Margin>
<Margin>
<select
class="uk-select uk-width-1-2"
ref={this.alphabetSelector}
onInput={() => {
this.updateAlphabet();
}}
>
<option value={alphabets.SECURE}>a-z a-Z 0-9 specials</option>
<option value={alphabets.SMOL}>a-z 0-9</option>
<option value={alphabets.HEX}>A-F 1-9</option>
</select>
</Margin>
<CopyableInputBox
text={genPassword({
length: this.state.length,
alphabet: this.state.alphabet,
})}
copyable
/>
<Margin>
<button class="uk-button uk-button-primary uk-margin-bottom" type="submit">
{i18next.t("password_generator_generate_btn")}
</button>
</Margin>
</Form>
<CopyableInputBox
text={genPassword({
length: this.state.length,
alphabet: this.state.alphabet,
})}
copyable
/>
<Margin>
<button class="uk-button uk-button-primary uk-margin-bottom" type="submit">
{i18next.t("password_generator_generate_btn")}
</button>
</Margin>
</Form>
</>
);
}
}
export class PwGenPage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
render(<PasswordGenerator />, this.router.pageContentElement);
}
get name(): string {
return i18next.t("password_generator_title");
}
}

View file

@ -2,47 +2,45 @@ import { Form } from "../../elements/Form";
import { MarginInline } from "../../elements/MarginInline";
import { Page } from "../../../types/Page";
import { deleteMount } from "../../../api/sys/deleteMount";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { PageTitle } from "../../elements/PageTitle";
import { route } from "preact-router";
export class DeleteSecretsEnginePage extends Page {
constructor() {
super();
}
export class DeleteSecretsEngine extends Component<DefaultPageProps> {
render() {
async render(): Promise<void> {
render(
<Form
onSubmit={async () => {
await this.onSubmit();
}}
>
<p>{i18next.t("delete_secrets_engine_message")}</p>
return (
<>
<PageTitle title={i18next.t("delete_secrets_engine_title", { mount: this.props.matches["mount"] })}/>
<Form
onSubmit={async () => {
await this.onSubmit();
}}
>
<p>{i18next.t("delete_secrets_engine_message")}</p>
<p class="uk-text-danger" id="errorText" />
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-danger" type="submit">
{i18next.t("delete_secrets_engine_delete_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
<MarginInline>
<button class="uk-button uk-button-danger" type="submit">
{i18next.t("delete_secrets_engine_delete_btn")}
</button>
</MarginInline>
</Form>
</>
);
}
async onSubmit(): Promise<void> {
try {
await deleteMount(this.state.baseMount);
await this.router.changePage("SECRETS_HOME");
await deleteMount(this.props.matches["mount"]);
route("/secrets");
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}
get name(): string {
return i18next.t("delete_secrets_engine_title", { mount: this.state.baseMount });
}
}

View file

@ -1,54 +1,40 @@
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { deleteSecret } from "../../../../api/kv/deleteSecret";
import { render } from "preact";
import { Component, render } from "preact";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { splitKVMount } from "./splitKVMount";
export class KeyValueDelete extends Component<DefaultPageProps> {
render() {
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const secretPath = mountSplit.restOfMount;
const item = this.props.matches["item"];
return (
<>
<SecretTitleElement type="kv" item={this.props.matches["item"]} mount={mount} suffix={i18next.t("kv_sec_edit_suffix")} />
<div>
<h5>{i18next.t("kv_delete_text")}</h5>
<button
class="uk-button uk-button-danger"
onClick={async () => {
await deleteSecret(
baseMount,
secretPath.map((e) => e + "/"),
item,
);
window.history.back();
}}
>
{i18next.t("kv_delete_btn")}
</button>
</div>
</>
export class KeyValueDeletePage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
if (this.state.secretVersion != null) {
this.state.secretVersion = null;
await this.router.changePage("KEY_VALUE_SECRET");
} else {
this.state.secretItem = "";
await this.router.changePage("KEY_VALUE_VIEW");
}
}
async render(): Promise<void> {
render(
<div>
<h5>{i18next.t("kv_delete_text")}</h5>
<button
class="uk-button uk-button-danger"
onClick={async () => {
await deleteSecret(
this.state.baseMount,
this.state.secretMountType,
this.state.secretPath,
this.state.secretItem,
this.state.secretVersion,
);
await this.goBack();
}}
>
{i18next.t("kv_delete_btn")}
</button>
</div>,
this.router.pageContentElement,
);
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("kv_delete_suffix")} />,
this.router.pageTitleElement,
);
}
get name(): string {
return i18next.t("kv_delete_title");
}
}

View file

@ -49,7 +49,6 @@ export class KeyValueNewPage extends Page {
try {
await createOrUpdateSecret(
this.state.baseMount,
this.state.secretMountType,
this.state.secretPath,
path,
keyData,
@ -61,12 +60,12 @@ export class KeyValueNewPage extends Page {
}
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("kv_new_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("kv_new_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("kv_new_title");

View file

@ -9,6 +9,10 @@ import { getSecret } from "../../../../api/kv/getSecret";
import { sortedObjectMap } from "../../../../utils";
import { undeleteSecret } from "../../../../api/kv/undeleteSecret";
import i18next from "i18next";
import { splitKVMount } from "./splitKVMount";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { getMount } from "../../../../api/sys/getMounts";
import { route } from "preact-router";
export type KVSecretViewProps = {
kvData: Record<string, unknown>;
@ -41,119 +45,92 @@ export class KVSecretVew extends Component<KVSecretViewProps, unknown> {
}
}
export class KeyValueSecretPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
if (this.state.secretVersion != null) {
this.state.secretVersion = null;
await this.router.changePage("KEY_VALUE_VERSIONS");
} else {
this.state.secretItem = "";
await this.router.changePage("KEY_VALUE_VIEW");
}
}
async render(): Promise<void> {
type KeyValueSecretState = {
baseMount: string;
secretPath: string[];
secretItem: string;
caps: string[];
secretInfo: Record<string, unknown>;
}
export class KeyValueSecret extends Component<DefaultPageProps, KeyValueSecretState> {
async componentDidMount() {
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const secretPath = mountSplit.restOfMount;
const secretItem = this.props.matches["item"];
const caps = (
await getCapabilities(this.state.baseMount, this.state.secretPath, this.state.secretItem)
await getCapabilities(baseMount, secretPath, secretItem)
).capabilities;
let secretPathAPI = secretPath.map((e) => e + "/");
// TODO: this is a big hacky, fix when redo how api arguments work
secretPathAPI[secretPathAPI.length - 1] = String(secretPathAPI[secretPathAPI.length - 1]).replace("/", "").toString();
const secretInfo = await getSecret(
this.state.baseMount,
this.state.secretMountType,
this.state.secretPath,
this.state.secretItem,
this.state.secretVersion,
baseMount,
secretPathAPI,
secretItem,
);
this.setState({
baseMount,
secretPath,
secretItem,
caps,
secretInfo
})
// On kv-v2, secrets can be deleted temporarily with the ability to restore
// Do not show any buttons when the secret is deleted.
const secretIsDeleted = secretInfo == null && this.state.secretMountType == "kv-v2";
}
render() {
if (!this.state.baseMount) return;
render(
<div>
<p id="buttonsBlock">
{
// Delete Button
!secretIsDeleted && caps.includes("delete") && (
// TODO: maybe add some sort of version viewing
// no idea what gonna do for that
return (
<>
<SecretTitleElement type="kv" item={this.props.matches["item"]} mount={this.props.matches["mount"]} suffix={i18next.t("kv_sec_edit_suffix")} />
<div>
<p id="buttonsBlock">
{
// Delete Button
this.state.caps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route("/secrets/kv/delete/" + this.state.secretItem + "/" + this.state.baseMount + "/" + this.state.secretPath.join("/"))
//await this.router.changePage("KEY_VALUE_DELETE");
}}
>
{i18next.t("kv_secret_delete_btn")}
</button>
)
}
{this.state.caps.includes("update") && (
<button
class="uk-button uk-button-danger"
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("KEY_VALUE_DELETE");
route("/secrets/kv/edit/" + this.state.secretItem + "/" + this.state.baseMount + "/" + this.state.secretPath.join("/"))
}}
>
{((): string => {
// Delete Secret on kv-v1
let deleteButtonText = i18next.t("kv_secret_delete_btn");
if (this.state.secretMountType == "kv-v2" && this.state.secretVersion == null) {
// Delete All
deleteButtonText = i18next.t("kv_secret_delete_all_btn");
} else if (
this.state.secretMountType == "kv-v2" &&
this.state.secretVersion != null
) {
// Delete Version X
deleteButtonText = i18next.t("kv_secret_delete_version_btn", {
version: this.state.secretVersion,
});
}
return deleteButtonText;
})()}
{i18next.t("kv_secret_edit_btn")}
</button>
)
}
{!secretIsDeleted && caps.includes("update") && this.state.secretVersion == null && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("KEY_VALUE_SECRET_EDIT");
}}
>
{i18next.t("kv_secret_edit_btn")}
</button>
)}
{!secretIsDeleted && this.state.secretMountType == "kv-v2" && (
<button
class="uk-button uk-button-secondary"
onClick={async () => {
await this.router.changePage("KEY_VALUE_VERSIONS");
}}
>
{i18next.t("kv_secret_versions_btn")}
</button>
)}
</p>
)}
</p>
{!secretIsDeleted && <KVSecretVew kvData={secretInfo} />}
{<KVSecretVew kvData={this.state.secretInfo} />}
</div>
</>
{secretIsDeleted && (
<>
<p>{i18next.t("kv_secret_deleted_text")}</p>
<button
class="uk-button uk-button-primary"
onClick={async () => {
await undeleteSecret(
this.state.baseMount,
this.state.secretPath,
this.state.secretItem,
this.state.secretVersion,
);
await this.router.refresh();
}}
>
{i18next.t("kv_secret_restore_btn")}
</button>
</>
)}
</div>,
this.router.pageContentElement,
);
}
async renderPageTitle(): Promise<void> {
render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("kv_secret_title");

View file

@ -7,21 +7,26 @@ import { getSecret } from "../../../../api/kv/getSecret";
import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap, verifyJSONString } from "../../../../utils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { splitKVMount } from "./splitKVMount";
import { route } from "preact-router";
//import { highlightElement } from "prismjs";
export type KVEditProps = {
page: Page;
baseMount: string;
secretPath: string[];
secretItem: string;
};
type KVEditState =
| {
dataLoaded: false;
}
dataLoaded: false;
}
| {
dataLoaded: true;
kvData: Record<string, unknown>;
code: string;
};
dataLoaded: true;
kvData: Record<string, unknown>;
code: string;
};
export class KVEditor extends Component<KVEditProps, KVEditState> {
constructor() {
@ -41,13 +46,12 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
}
await createOrUpdateSecret(
this.props.page.state.baseMount,
this.props.page.state.secretMountType,
this.props.page.state.secretPath,
this.props.page.state.secretItem,
this.props.baseMount,
this.props.secretPath.map((e) => e + "/"),
this.props.secretItem,
JSON.parse(editorContent),
);
await this.props.page.router.changePage("KEY_VALUE_SECRET");
window.history.back();
}
onCodeUpdate(code: string): void {
@ -58,10 +62,9 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
loadData(): void {
void getSecret(
this.props.page.state.baseMount,
this.props.page.state.secretMountType,
this.props.page.state.secretPath,
this.props.page.state.secretItem,
this.props.baseMount,
this.props.secretPath.map((e) => e + "/"),
this.props.secretItem,
).then((kvData) => {
this.setState({
dataLoaded: true,
@ -104,24 +107,29 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
}
}
export class KeyValueSecretEditPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("KEY_VALUE_SECRET");
}
async render(): Promise<void> {
render(<KVEditor page={this} />, this.router.pageContentElement);
}
export class KeyValueSecretEdit extends Component<DefaultPageProps> {
render() {
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const restOfMount = mountSplit.restOfMount;
const item = this.props.matches["item"];
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("kv_sec_edit_suffix")} />,
this.router.pageTitleElement,
return (
<>
<SecretTitleElement type="kv" mount={mount} item={this.props.matches["item"]} suffix={i18next.t("kv_sec_edit_suffix")} />
<KVEditor baseMount={baseMount} secretPath={restOfMount} secretItem={item} />
</>
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("kv_sec_edit_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("kv_sec_edit_title");
}

View file

@ -1,55 +0,0 @@
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getSecretMetadata } from "../../../../api/kv/getSecretMetadata";
import { objectToMap } from "../../../../utils";
import { render } from "preact";
import i18next from "i18next";
export class KeyValueVersionsPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
if (this.state.secretVersion != null) {
this.state.secretVersion = null;
}
await this.router.changePage("KEY_VALUE_SECRET");
}
async render(): Promise<void> {
const metadata = await getSecretMetadata(
this.state.baseMount,
this.state.secretPath,
this.state.secretItem,
);
const versions = Array.from(objectToMap(metadata.versions).keys());
render(
<ul class="uk-nav uk-nav-default">
{versions.map((ver) => (
<li>
<a
onClick={async () => {
this.state.secretVersion = ver;
await this.router.changePage("KEY_VALUE_SECRET");
}}
>
{`v${ver}`}
</a>
</li>
))}
</ul>,
this.router.pageContentElement,
);
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("kv_sec_versions_suffix")} />,
this.router.pageTitleElement,
);
}
get name(): string {
return i18next.t("kv_sec_versions_title");
}
}

View file

@ -2,15 +2,17 @@ import { Component, createRef, JSX, render } from "preact";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { getSecrets } from "../../../../api/kv/getSecrets";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { splitKVMount } from "./splitKVMount";
export type KVKeysListProps = {
page: Page;
export type KVKeysListProps = DefaultPageProps & {
baseMount: string;
secretMountType: string;
secretPath: string[];
};
@ -20,17 +22,15 @@ type KVKeysListState = {
searchQuery: string;
};
function SecretsList(secrets: string[], page: Page): JSX.Element[] {
function SecretsList(baseMount: string, restOfMount: string[], secrets: string[]): JSX.Element[] {
return secrets.map((secret) => (
<li>
<a
onClick={async () => {
if (secret.endsWith("/")) {
page.state.pushSecretPath(secret);
await page.router.changePage("KEY_VALUE_VIEW");
route("/secrets/kv/list/" + baseMount + "/" + restOfMount.join("/") + "/" + secret)
} else {
page.state.secretItem = secret;
await page.router.changePage("KEY_VALUE_SECRET");
route("/secrets/kv/view/" + secret + "/" + baseMount + "/" + restOfMount.join("/"))
}
}}
>
@ -51,12 +51,10 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
}
async loadData(): Promise<void> {
const page = this.props.page;
try {
const keys = await getSecrets(
this.props.baseMount,
this.props.secretMountType,
this.props.secretPath,
this.props.secretPath.map((e) => e + "/"),
);
this.setState({
dataLoaded: true,
@ -68,12 +66,13 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
if (error == DoesNotExistError) {
// getSecrets also 404's on no keys so dont go all the way back.
if (this.props.secretPath.length != 0) {
await page.goBack();
window.history.back();
return;
}
} else {
setErrorText(error.message);
}
this.setState({
dataLoaded: true,
keys: null,
@ -84,7 +83,6 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
componentDidUpdate(prevProps: KVKeysListProps): void {
if (
prevProps.baseMount !== this.props.baseMount ||
prevProps.secretMountType !== this.props.secretMountType ||
prevProps.secretPath !== this.props.secretPath
) {
this.setState({
@ -131,7 +129,7 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
if (this.state.searchQuery.length > 0) {
secrets = secrets.filter((secret) => secret.includes(this.state.searchQuery));
}
return SecretsList(secrets, this.props.page);
return SecretsList(this.props.baseMount, this.props.secretPath, secrets);
})()}
</ul>
</>
@ -139,65 +137,66 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
}
}
export class KeyValueViewPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
if (this.state.secretPath.length != 0) {
this.state.popSecretPath();
await this.router.changePage("KEY_VALUE_VIEW");
} else {
await this.router.changePage("SECRETS_HOME");
}
}
async render(): Promise<void> {
const mountsPath = "/sys/mounts/" + this.state.baseMount;
const currentPath = this.state.baseMount + this.state.secretPath.join("/");
const caps = await getCapabilitiesPath([mountsPath, currentPath]);
const mountCaps = caps[mountsPath];
const pathCaps = caps[currentPath];
type KeyValueViewState = {
pathCaps: string[];
mountCaps: string[];
}
render(
export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState> {
async componentDidMount() {
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const restOfMount = mountSplit.restOfMount;
const mountsPath = "/sys/mounts/" + baseMount;
const currentPath = baseMount + restOfMount.join();
const caps = await getCapabilitiesPath([mountsPath, currentPath]);
this.setState({
mountCaps: caps[mountsPath],
pathCaps: caps[currentPath],
});
}
render() {
if (!this.state.pathCaps) return;
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const restOfMount = mountSplit.restOfMount;
return (
<>
<SecretTitleElement type="kv" mount={mount} item={this.props.matches["item"]} />
<p>
{pathCaps.includes("create") && (
{this.state.pathCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("KEY_VALUE_NEW_SECRET");
route("/secrets/kv/new/" + mount)
}}
>
{i18next.t("kv_view_new_btn")}
</button>
)}
{this.state.secretPath.length == 0 && mountCaps.includes("delete") && (
{restOfMount.length == 0 && this.state.mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
await this.router.changePage("DELETE_SECRET_ENGINE");
route("/secrets/delete_engine/" + mount)
}}
>
{i18next.t("kv_view_delete_btn")}
</button>
)}
</p>
{this.state.secretMountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
<KVKeysList
page={this}
baseMount={this.state.baseMount}
secretMountType={this.state.secretMountType}
secretPath={this.state.secretPath}
/>
</>,
this.router.pageContentElement,
{//"TODO: " == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
}
<KVKeysList baseMount={baseMount} secretPath={restOfMount} state={this.props.state} />
</>
);
}
async renderPageTitle(): Promise<void> {
render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
}
get name(): string {
return i18next.t("kv_view_title");
}

View file

@ -0,0 +1,10 @@
export function splitKVMount(mount: string): { baseMount: string, restOfMount: string[] } {
const baseMount = mount.split("/")[0];
const restOfMount = mount.split("/");
restOfMount.shift();
return {
baseMount,
restOfMount
};
}

View file

@ -3,47 +3,45 @@ import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { newMount } from "../../../../api/sys/newMount";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router";
export class NewKVEnginePage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("SECRETS_HOME");
}
async render(): Promise<void> {
render(
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_kv_engine_name_input")}
required
/>
</Margin>
<Margin>
<select class="uk-input uk-form-width-medium" name="version">
<option label={i18next.t("new_kv_engine_version_2")} value="2">
{i18next.t("new_kv_engine_version_2")}
</option>
<option label={i18next.t("new_kv_engine_version_1")} value="1">
{i18next.t("new_kv_engine_version_1")}
</option>
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_kv_engine_create_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
export class NewKVEngine extends Component {
render() {
return (
<>
<PageTitle title={i18next.t("new_kv_engine_title")} />
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_kv_engine_name_input")}
required
/>
</Margin>
<Margin>
<select class="uk-input uk-form-width-medium" name="version">
<option label={i18next.t("new_kv_engine_version_2")} value="2">
{i18next.t("new_kv_engine_version_2")}
</option>
<option label={i18next.t("new_kv_engine_version_1")} value="1">
{i18next.t("new_kv_engine_version_1")}
</option>
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_kv_engine_create_btn")}
</button>
</MarginInline>
</Form>
</>
);
}
@ -59,16 +57,10 @@ export class NewKVEnginePage extends Page {
version: version,
},
});
this.state.secretMountType = "kv-v" + version;
this.state.baseMount = name + "/";
await this.router.changePage("KEY_VALUE_VIEW");
route("/secrets/kv/list/" + name + "/")
} catch (e) {
const error = e as Error;
setErrorText(error.message);
}
}
get name(): string {
return i18next.t("new_kv_engine_title");
}
}

View file

@ -3,37 +3,35 @@ import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { newMount } from "../../../../api/sys/newMount";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { route } from "preact-router";
import { PageTitle } from "../../../elements/PageTitle";
export class NewTOTPEnginePage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("SECRETS_HOME");
}
async render(): Promise<void> {
render(
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_totp_engine_name_input")}
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_totp_engine_create_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
export class NewTOTPEngine extends Component {
render() {
return (
<>
<PageTitle title={i18next.t("new_totp_engine_title")} />
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_totp_engine_name_input")}
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_totp_engine_create_btn")}
</button>
</MarginInline>
</Form>,
</>
);
}
@ -45,9 +43,7 @@ export class NewTOTPEnginePage extends Page {
name: name,
type: "totp",
});
this.state.secretMountType = "totp";
this.state.baseMount = name + "/";
await this.router.changePage("TOTP_VIEW");
route("/secrets/totp/list/" + name + "/")
} catch (e) {
const error = e as Error;
setErrorText(error.message);

View file

@ -3,37 +3,35 @@ import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { newMount } from "../../../../api/sys/newMount";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router";
export class NewTransitEnginePage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("SECRETS_HOME");
}
async render(): Promise<void> {
render(
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_transit_engine_name_input")}
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_transit_engine_create_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
export class NewTransitEngine extends Component {
render() {
return (
<>
<PageTitle title={i18next.t("new_totp_engine_title")} />
<Form onSubmit={(data) => this.submit(data)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
type="text"
placeholder={i18next.t("new_transit_engine_name_input")}
required
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("new_transit_engine_create_btn")}
</button>
</MarginInline>
</Form>,
</>
);
}
@ -45,9 +43,7 @@ export class NewTransitEnginePage extends Page {
name: name,
type: "transit",
});
this.state.secretMountType = "transit";
this.state.baseMount = name + "/";
await this.router.changePage("TRANSIT_VIEW");
route("/secrets/transit/list/" + name + "/")
} catch (e) {
const error = e as Error;
setErrorText(error.message);

View file

@ -1,44 +1,40 @@
import { Grid, GridSizes } from "../../elements/Grid";
import { Page } from "../../../types/Page";
import { Tile } from "../../elements/Tile";
import { render } from "preact";
import { Component, render } from "preact";
import i18next from "i18next";
import { route } from "preact-router";
import { PageTitle } from "../../elements/PageTitle";
export class NewSecretsEnginePage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
render(
export class NewSecretsEngine extends Component {
render() {
return (
<>
<PageTitle title={i18next.t("new_secrets_engine_title")} />
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
<Tile
title={i18next.t("new_secrets_engine_kv_title")}
description={i18next.t("new_secrets_engine_kv_description")}
onclick={async () => {
await this.router.changePage("NEW_KV_ENGINE");
route("/secrets/new_secrets_engine/kv")
}}
/>
<Tile
title={i18next.t("new_secrets_engine_totp_title")}
description={i18next.t("new_secrets_engine_totp_description")}
onclick={async () => {
await this.router.changePage("NEW_TOTP_ENGINE");
route("/secrets/new_secrets_engine/totp")
}}
/>
<Tile
title={i18next.t("new_secrets_engine_transit_title")}
description={i18next.t("new_secrets_engine_transit_description")}
onclick={async () => {
await this.router.changePage("NEW_TRANSIT_ENGINE");
route("/secrets/new_secrets_engine/transit")
}}
/>
</Grid>,
this.router.pageContentElement,
</Grid>
</>
);
}
get name(): string {
return i18next.t("new_secrets_engine_title");
}
}

View file

@ -1,69 +1,55 @@
import { route } from "preact-router";
import { JSX } from "preact/jsx-runtime";
import { Page } from "../../../types/Page";
import { CopyStateLinkButton } from "../../elements/CopyStateLinkButton";
function currentTitleSecretText(page: Page): string {
let secretItemText = page.state.secretItem;
if (page.state.secretVersion !== null) secretItemText += ` (v${page.state.secretVersion})`;
return secretItemText;
}
type SecretTitleElementProps = {
page: Page;
type: string;
mount: string;
item?: string;
suffix?: string;
};
export function SecretTitleElement(props: SecretTitleElementProps): JSX.Element {
const page = props.page;
const type = props.type;
const mount = props.mount;
const item = props.item || "";
const suffix = props.suffix || "";
const baseMount = mount.split("/")[0];
const restOfMount = mount.split("/");
restOfMount.shift();
return (
<div>
<a
onClick={async () => {
await page.router.changePage("SECRETS_HOME");
}}
>
{"/ "}
</a>
<h3 class="uk-card-title" id="pageTitle">
<a
onClick={async () => {
page.state.secretPath = [];
page.state.secretItem = "";
page.state.secretVersion = null;
if (
page.state.secretMountType.startsWith("kv") ||
page.state.secretMountType == "cubbyhole"
) {
await page.router.changePage("KEY_VALUE_VIEW");
} else if (page.state.secretMountType == "totp") {
await page.router.changePage("TOTP_VIEW");
} else if (page.state.secretMountType == "transit") {
await page.router.changePage("TRANSIT_VIEW");
}
}}
>
{page.state.baseMount + " "}
</a>
{...page.state.secretPath.map((secretPath, index, secretPaths) => (
<div>
<a
onClick={async () => {
page.state.secretVersion = null;
if (page.state.secretMountType.startsWith("kv")) {
page.state.secretPath = secretPaths.slice(0, index + 1);
await page.router.changePage("KEY_VALUE_VIEW");
}
route("/secrets");
}}
>
{secretPath + " "}
{"/ "}
</a>
))}
{page.state.secretItem.length != 0 && <span>{currentTitleSecretText(page)}</span>}
{suffix.length != 0 && <span>{suffix}</span>}
<CopyStateLinkButton state={page.state} />
</div>
<a href={"/secrets/" + type + "/list/" + baseMount + "/"}>
{baseMount + " "}
</a>
{...restOfMount.map((secretPath, index, secretPaths) => (
<a
onClick={async () => {
if (type == "kv") {
let secretPath = secretPaths.slice(0, index + 1);
route("/secrets/kv/list/" + baseMount + "/" + secretPath.join("/"))
}
}}
>
{secretPath + "/" + " "}
</a>
))}
{item.length != 0 && <span>{item}</span>}
{suffix.length != 0 && <span>{suffix}</span>}
</div>
</h3>
);
}

View file

@ -1,16 +1,14 @@
import { JSX, render } from "preact";
import { Component, JSX, render } from "preact";
import { MountType, getMounts } from "../../../api/sys/getMounts";
import { Page } from "../../../types/Page";
import { getCapsPath } from "../../../api/sys/getCapabilities";
import { prePageChecks } from "../../../pageUtils";
import { sortedObjectMap } from "../../../utils";
import i18next from "i18next";
export type MountLinkProps = {
page: Page;
mount: MountType;
baseMount: string;
};
import { PageState } from "../../../state/PageState";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { route } from "preact-router";
import { PageTitle } from "../../elements/PageTitle";
const supportedMountTypes = ["kv", "totp", "transit", "cubbyhole"];
@ -22,97 +20,96 @@ export function isSupportedMount(mount: MountType): boolean {
return true;
}
export type MountLinkProps = {
state: PageState;
mount: MountType;
baseMount: string;
};
function MountLink(props: MountLinkProps): JSX.Element {
const mount = props.mount;
const baseMount = props.baseMount;
const page = props.page;
const secretMountType = mount.type == "kv" ? "kv-v" + String(mount.options.version) : mount.type;
let linkText = "";
let linkPage: string;
let mountPathType: string;
if (mount.type == "kv") {
linkText = `K/V (v${mount.options.version}) - ${baseMount}`;
linkPage = "KEY_VALUE_VIEW";
mountPathType = "kv";
} else if (mount.type == "totp") {
linkText = `TOTP - ${baseMount}`;
linkPage = "TOTP_VIEW";
mountPathType = "totp";
} else if (mount.type == "transit") {
linkText = `Transit - ${baseMount}`;
linkPage = "TRANSIT_VIEW";
mountPathType = "transit";
} else if (mount.type == "cubbyhole") {
linkText = `Cubbyhole - ${baseMount}`;
linkPage = "KEY_VALUE_VIEW";
mountPathType = "kv";
}
let link = "/secrets/" + mountPathType + "/list/" + baseMount;
return (
<li>
<a
onClick={async () => {
page.state.baseMount = baseMount;
page.state.secretMountType = secretMountType;
await page.router.changePage(linkPage);
}}
>
<a href={link}>
{linkText}
</a>
</li>
);
}
export class SecretsHomePage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("HOME");
}
async render(): Promise<void> {
if (!(await prePageChecks(this.router))) return;
type SecretsState = {
mountsMap: Map<String, MountType>;
capabilities: string[];
}
this.state.baseMount = "";
this.state.secretPath = [];
this.state.secretItem = "";
this.state.secretVersion = null;
export class Secrets extends Component<DefaultPageProps, SecretsState> {
async componentDidMount() {
if (!(await prePageChecks(this.props.state))) return;
const mountsCapabilities = await getCapsPath("/sys/mounts");
const mounts = await getMounts();
// sort it by secretPath so it's in alphabetical order consistantly.
const mountsMap = sortedObjectMap(mounts);
this.setState({
capabilities: mountsCapabilities,
mountsMap: mountsMap as Map<String, MountType>
})
}
render(
<div>
render() {
return this.state.mountsMap && (
<>
<PageTitle title={i18next.t("secrets_home_page_title")} />
<div>
<p>
{mountsCapabilities.includes("sudo") && mountsCapabilities.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("NEW_SECRETS_ENGINE");
}}
>
{i18next.t("secrets_home_new_secrets_engine_button")}
</button>
)}
</p>
<div>
<p>
{this.state.capabilities.includes("sudo") && this.state.capabilities.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={() => {
route("/secrets/new_secrets_engine")
}}
>
{i18next.t("secrets_home_new_secrets_engine_button")}
</button>
)}
</p>
</div>
<div class="uk-margin-top">
<ul class="uk-nav uk-nav-default">
{Array.from(this.state.mountsMap).map((args: [string, MountType]) => {
const baseMount = args[0];
const mount = args[1];
console.log(baseMount, mount)
if (isSupportedMount(mount)) {
return <MountLink state={this.props.state} mount={mount} baseMount={baseMount} />;
}
})}
</ul>
</div>
</div>
<div class="uk-margin-top">
<ul class="uk-nav uk-nav-default">
{Array.from(mountsMap as Map<string, MountType>).map((args: [string, MountType]) => {
const baseMount = args[0];
const mount = args[1];
if (isSupportedMount(mount)) {
return <MountLink page={this} mount={mount} baseMount={baseMount} />;
}
})}
</ul>
</div>
</div>,
this.router.pageContentElement,
</>
);
}
get name(): string {
return i18next.t("secrets_home_page_title");
}
}

View file

@ -1,46 +1,37 @@
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { deleteTOTP } from "../../../../api/totp/deleteTOTP";
import { render } from "preact";
import { Component, render } from "preact";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
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_VIEW");
}
async render(): Promise<void> {
render(
export class TOTPDelete extends Component<DefaultPageProps> {
render() {
return (
<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();
let mount = this.props.matches["mount"];
let item = this.props.matches["item"];
await deleteTOTP(mount, item);
route("/secrets/totp/list/" + mount)
}}
>
{i18next.t("kv_delete_btn")}
</button>
</div>,
this.router.pageContentElement,
</div>
);
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("totp_delete_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("totp_delete_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("totp_delete_title");

View file

@ -8,6 +8,8 @@ import { SecretTitleElement } from "../SecretTitleElement";
import { addNewTOTP } from "../../../../api/totp/addNewTOTP";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { route } from "preact-router";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
function replaceAll(str: string, replace: string, replaceWith: string): string {
return str.replace(new RegExp(replace, "g"), replaceWith);
@ -19,7 +21,7 @@ function removeDashSpaces(str: string): string {
return str;
}
export class TOTPNewForm extends Component<{ page: Page }, { qrMode: boolean }> {
export class TOTPNewForm extends Component<{ mount: string }, { qrMode: boolean }> {
constructor() {
super();
this.state = {
@ -30,7 +32,6 @@ export class TOTPNewForm extends Component<{ page: Page }, { qrMode: boolean }>
uriInputRef = createRef<HTMLInputElement>();
async onSubmit(data: FormData): Promise<void> {
const page = this.props.page;
const parms = {
url: data.get("uri") as string,
key: removeDashSpaces(data.get("key") as string).toUpperCase(),
@ -39,8 +40,8 @@ export class TOTPNewForm extends Component<{ page: Page }, { qrMode: boolean }>
};
try {
await addNewTOTP(page.state.baseMount, parms);
await page.router.changePage("TOTP_VIEW");
await addNewTOTP(this.props.mount, parms);
route("/secrets/totp/list/" + this.props.mount)
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
@ -118,24 +119,24 @@ export class TOTPNewForm extends Component<{ page: Page }, { qrMode: boolean }>
}
}
export class TOTPNewPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("TOTP_VIEW");
}
async render(): Promise<void> {
render(<TOTPNewForm page={this} />, this.router.pageContentElement);
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("totp_new_suffix")} />,
this.router.pageTitleElement,
export class TOTPNew extends Component<DefaultPageProps> {
render() {
let mount = this.props.matches["mount"];
return (
<>
<SecretTitleElement type="totp" mount={mount} suffix={i18next.t("totp_new_suffix")} />
<TOTPNewForm mount={this.props.matches["mount"]} />
</>
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("totp_new_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("totp_new_title");
}

View file

@ -5,15 +5,21 @@ import { Grid, GridSizes } from "../../../elements/Grid";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getCapabilitiesPath, getCapsPath } from "../../../../api/sys/getCapabilities";
import { CapabilitiesType, getCapabilitiesPath, getCapsPath } from "../../../../api/sys/getCapabilities";
import { getTOTPCode } from "../../../../api/totp/getTOTPCode";
import { getTOTPKeys } from "../../../../api/totp/getTOTPKeys";
import { removeDoubleSlash } from "../../../../utils";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
type TOTPGridItemProps = {
mount: string; totpKey: string; canDelete: boolean;
}
export class RefreshingTOTPGridItem extends Component<
{ baseMount: string; totpKey: string; page: Page; canDelete: boolean },
TOTPGridItemProps,
{ totpValue: string }
> {
constructor() {
@ -23,7 +29,7 @@ export class RefreshingTOTPGridItem extends Component<
timer: unknown;
updateTOTPCode(): void {
void getTOTPCode(this.props.baseMount, this.props.totpKey).then((code) => {
void getTOTPCode(this.props.mount, this.props.totpKey).then((code) => {
this.setState({ totpValue: code });
});
}
@ -50,9 +56,7 @@ export class RefreshingTOTPGridItem extends Component<
<button
class="uk-button uk-button-danger"
onClick={async () => {
const page = this.props.page;
page.state.secretItem = this.props.totpKey;
await page.router.changePage("TOTP_DELETE");
route("/secrets/totp/delete/" + this.props.mount + "/" + this.props.totpKey);
}}
>
{i18next.t("totp_view_secret_delete_btn")}
@ -65,34 +69,77 @@ export class RefreshingTOTPGridItem extends Component<
}
}
export class TOTPViewPage extends Page {
type TOTPViewProps = DefaultPageProps & {
}
type TOTPViewState = {
capabilities?: CapabilitiesType;
totpItems: TOTPGridItemProps[];
}
export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
constructor() {
super();
this.refresher = undefined;
this.state = { capabilities: null, totpItems: [] };
}
refresher: number;
async goBack(): Promise<void> {
await this.router.changePage("SECRETS_HOME");
async componentDidMount() {
var mount = this.props.matches["mount"];
const mountsPath = "/sys/mounts/" + mount;
const caps = await getCapabilitiesPath([mountsPath, mount]);
let totpItems: TOTPGridItemProps[] = [];
// TODO: tidy this up i guess
try {
totpItems = await Promise.all(
Array.from(await getTOTPKeys(mount)).map(async (key) => {
const totpCaps = await getCapsPath(
removeDoubleSlash(mount + "code/" + key),
);
if (totpCaps.includes("read")) {
return {
mount: mount,
totpKey: key,
canDelete: totpCaps.includes("delete"),
};
}
}),
);
} catch (e: unknown) {
const error = e as Error;
if (error != DoesNotExistError) {
setErrorText(error.message);
}
}
this.setState({
capabilities: caps,
totpItems: totpItems
});
}
async render(): Promise<void> {
this.state.secretItem = "";
render() {
if (!this.state.capabilities) return;
var mount = this.props.matches["mount"];
const mountsPath = "/sys/mounts/" + this.state.baseMount;
const caps = await getCapabilitiesPath([mountsPath, this.state.baseMount]);
const mountCaps = caps[mountsPath];
const totpCaps = caps[this.state.baseMount];
const mountsPath = "/sys/mounts/" + mount;
const mountCaps = this.state.capabilities[mountsPath];
const totpCaps = this.state.capabilities[mount];
render(
return (
<>
<SecretTitleElement type="totp" mount={mount} />
<div>
<p>
{totpCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("TOTP_NEW");
route("/secrets/totp/new/" + mount)
}}
>
{i18next.t("totp_view_new_btn")}
@ -102,7 +149,7 @@ export class TOTPViewPage extends Page {
<button
class="uk-button uk-button-danger"
onClick={async () => {
await this.router.changePage("DELETE_SECRET_ENGINE");
route("/secrets/delete_engine/" + mount)
}}
>
{i18next.t("totp_view_delete_btn")}
@ -111,44 +158,25 @@ export class TOTPViewPage extends Page {
</p>
<div id="totpList">
{
await (async () => {
try {
const elem = await Promise.all(
Array.from(await getTOTPKeys(this.state.baseMount)).map(async (key) => {
const caps = await getCapsPath(
removeDoubleSlash(this.state.baseMount + "code/" + key),
);
if (caps.includes("read")) {
return (
<RefreshingTOTPGridItem
baseMount={this.state.baseMount}
totpKey={key}
page={this}
canDelete={caps.includes("delete")}
/>
);
}
}),
);
return elem;
} catch (e: unknown) {
const error = e as Error;
if (error == DoesNotExistError) {
return <p>{i18next.t("totp_view_empty")}</p>;
} else {
setErrorText(error.message);
}
(() => {
if (this.state.totpItems.length == 0) {
return <p>{i18next.t("totp_view_empty")}</p>;
} else {
return this.state.totpItems.map((totpItem) => {
return <RefreshingTOTPGridItem
mount={totpItem.mount}
totpKey={totpItem.totpKey}
canDelete={totpItem.canDelete}
/>
})
}
})()
}
</div>
</div>,
this.router.pageContentElement,
);
}
</div>
</>
async renderPageTitle(): Promise<void> {
render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
);
}
get name(): string {

View file

@ -76,12 +76,12 @@ export class NewTransitKeyPage extends Page {
}
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("transit_new_key_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("transit_new_key_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("transit_new_key_title");

View file

@ -82,12 +82,12 @@ export class TransitDecryptPage extends Page {
}
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("transit_decrypt_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("transit_decrypt_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("transit_decrypt_title");

View file

@ -79,12 +79,12 @@ export class TransitEncryptPage extends Page {
}
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("transit_encrypt_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("transit_encrypt_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("transit_encrypt_title");

View file

@ -94,12 +94,12 @@ export class TransitRewrapPage extends Page {
}
}
async renderPageTitle(): Promise<void> {
render(
<SecretTitleElement page={this} suffix={i18next.t("transit_rewrap_suffix")} />,
this.router.pageTitleElement,
);
}
//async renderPageTitle(): Promise<void> {
// render(
// <SecretTitleElement page={this} suffix={i18next.t("transit_rewrap_suffix")} />,
// this.router.pageTitleElement,
// );
//}
get name(): string {
return i18next.t("transit_rewrap_title");

View file

@ -116,9 +116,9 @@ export class TransitViewPage extends Page {
);
}
async renderPageTitle(): Promise<void> {
render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("transit_view_title");

View file

@ -52,9 +52,9 @@ export class TransitViewSecretPage extends Page {
);
}
async renderPageTitle(): Promise<void> {
render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("transit_view_secret_title");

View file

@ -5,51 +5,52 @@ import translations from "../../translations/index.mjs";
import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin";
import { MarginInline } from "../elements/MarginInline";
import { Page } from "../../types/Page";
import { reloadNavBar } from "../elements/NavBar";
import { render } from "preact";
import { Component } from "preact";
import i18next from "i18next";
import { route } from "preact-router";
import { PageTitle } from "../elements/PageTitle";
import { DefaultPageProps } from "../../types/DefaultPageProps.js";
const languageIDs = Object.getOwnPropertyNames(translations);
export class SetLanguagePage extends Page {
export class SetLanguage extends Component<DefaultPageProps> {
constructor() {
super();
}
async render(): Promise<void> {
render(
<Form onSubmit={(data) => this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-form-width-large" name="language">
{languageIDs.map((languageID) => (
<option value={languageID}>
{i18next.getFixedT(languageID, null)("language_name")}
</option>
))}
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("set_language_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
render() {
return (
<>
<PageTitle title={i18next.t("set_language_title")} />
<Form onSubmit={(data) => this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-form-width-large" name="language">
{languageIDs.map((languageID) => (
<option value={languageID}>
{i18next.getFixedT(languageID, null)("language_name")}
</option>
))}
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("set_language_btn")}
</button>
</MarginInline>
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
const language = data.get("language") as string;
this.state.language = language;
this.props.state.language = language;
const t = await i18next.changeLanguage(language);
this.state.pageDirection = t("language_direction");
reloadNavBar(this.router);
await this.router.changePage("HOME");
}
get name(): string {
return i18next.t("set_language_title");
this.props.state.pageDirection = t("language_direction");
// TODO: make navbar somethingy
//reloadNavBar(this.router);
route("/");
}
}

View file

@ -1,14 +1,16 @@
import { Form } from "../elements/Form";
import { Margin } from "../elements/Margin";
import { Page } from "../../types/Page";
import { render } from "preact";
import { Component, render } from "preact";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { route } from "preact-router";
import { PageTitle } from "../elements/PageTitle";
export class SetVaultURLPage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
render(
export class SetVaultURL extends Component<DefaultPageProps> {
render() {
return (
<>
<PageTitle title={this.name} />
<Form onSubmit={(data) => this.onSubmit(data)}>
<Margin>
<input
@ -25,18 +27,19 @@ export class SetVaultURLPage extends Page {
Set
</button>
</Margin>
</Form>,
this.router.pageContentElement,
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
this.state.apiURL = data.get("vaultURL") as string;
await this.router.changePage("HOME");
// TODO: check if vault is actually working here.
this.props.state.apiURL = data.get("vaultURL") as string;
route("/")
}
get name(): string {
// TODO:
return "Set Vault URL";
}
}

View file

@ -9,6 +9,9 @@ import { setErrorText } from "../../pageUtils";
import { submitUnsealKey } from "../../api/sys/submitUnsealKey";
import { toStr } from "../../utils";
import i18next from "i18next";
import { DefaultPageProps } from "../../types/DefaultPageProps";
import { PageTitle } from "../elements/PageTitle";
import { route } from "preact-router";
const UnsealInputModes = {
FORM_INPUT: "FORM_INPUT",
@ -50,7 +53,7 @@ type UnsealPageState = {
keys_needed: number;
};
export class UnsealPageElement extends Component<{ page: Page }, UnsealPageState> {
export class Unseal extends Component<DefaultPageProps, UnsealPageState> {
constructor() {
super();
this.state = {
@ -79,7 +82,7 @@ export class UnsealPageElement extends Component<{ page: Page }, UnsealPageState
keys_needed: data.t,
});
if (!data.sealed) {
void this.props.page.router.changePage("HOME");
route("/");
}
});
}
@ -97,61 +100,50 @@ export class UnsealPageElement extends Component<{ page: Page }, UnsealPageState
render(): JSX.Element {
return (
<div>
<progress
class="uk-progress"
value={this.state.keys_submitted}
max={this.state.keys_needed}
/>
<>
<PageTitle title={i18next.t("unseal_vault_text")} />
<div>
<progress
class="uk-progress"
value={this.state.keys_submitted}
max={this.state.keys_needed}
/>
<p id="errorText" class="uk-text-danger uk-margin-top" />
<p id="errorText" class="uk-text-danger uk-margin-top" />
<p>
{i18next.t("unseal_keys_progress", {
progress: toStr(this.state.keys_submitted),
keys_needed: toStr(this.state.keys_needed),
})}
</p>
<p>
{i18next.t("unseal_keys_progress", {
progress: toStr(this.state.keys_submitted),
keys_needed: toStr(this.state.keys_needed),
})}
</p>
{this.state.mode == UnsealInputModes.FORM_INPUT && (
<UnsealFormInput onSubmit={(code) => this.submitKey(code)} />
)}
{this.state.mode == UnsealInputModes.FORM_INPUT && (
<UnsealFormInput onSubmit={(code) => this.submitKey(code)} />
)}
{this.state.mode == UnsealInputModes.QR_INPUT && (
<QRScanner onScan={(code) => this.submitKey(code)} />
)}
{this.state.mode == UnsealInputModes.QR_INPUT && (
<QRScanner onScan={(code) => this.submitKey(code)} />
)}
<button
class="uk-button uk-button-primary"
onClick={async () => {
let newMethod: string;
if (this.state.mode == UnsealInputModes.FORM_INPUT) {
newMethod = UnsealInputModes.QR_INPUT;
} else {
newMethod = UnsealInputModes.FORM_INPUT;
}
this.setState({ mode: newMethod });
}}
>
{this.state.mode == UnsealInputModes.QR_INPUT
? i18next.t("unseal_input_btn")
: i18next.t("unseal_qr_btn")}
</button>
</div>
<button
class="uk-button uk-button-primary"
onClick={async () => {
let newMethod: string;
if (this.state.mode == UnsealInputModes.FORM_INPUT) {
newMethod = UnsealInputModes.QR_INPUT;
} else {
newMethod = UnsealInputModes.FORM_INPUT;
}
this.setState({ mode: newMethod });
}}
>
{this.state.mode == UnsealInputModes.QR_INPUT
? i18next.t("unseal_input_btn")
: i18next.t("unseal_qr_btn")}
</button>
</div>
</>
);
}
}
export class UnsealPage extends Page {
constructor() {
super();
}
async render(): Promise<void> {
render(<UnsealPageElement page={this} />, this.router.pageContentElement);
}
get name(): string {
return i18next.t("unseal_vault_text");
}
}

View file

@ -16,6 +16,7 @@ module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/',
},
stats: {
colors: true,
@ -31,6 +32,7 @@ module.exports = {
],
devServer: {
open: process.env.BROWSER,
historyApiFallback: true,
},
resolve: {
modules: ['node_modules'],

View file

@ -41,6 +41,7 @@ module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/',
},
stats: {
colors: true,
@ -56,6 +57,7 @@ module.exports = {
],
devServer: {
open: process.env.BROWSER || "microsoft-edge-dev",
historyApiFallback: true,
},
resolve: {
modules: ['node_modules'],