1
0
Fork 0

add some more pages and page links better

This commit is contained in:
ChaotiCryptidz 2022-01-06 22:57:12 +00:00
parent c62cd89771
commit ff8a2995cb
19 changed files with 337 additions and 331 deletions

View file

@ -12,9 +12,9 @@ export async function deleteSecret(
const mountInfo = await getMount(baseMount);
if (mountInfo.options.version == "2") {
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`;
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("/")}/${name}`;
} else {
secretURL = `/v1/${baseMount}/${secretPath.join("")}/${name}`;
secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
}
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
request = new Request(appendAPIURL(secretURL), {

View file

@ -10,10 +10,10 @@ export async function getSecrets(
let secretMountType = "kv-v2"
if (secretMountType == "kv-v2") {
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("")}?list=true`;
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("/")}?list=true`;
} else {
// cubbyhole and v1 are identical
secretURL = `/v1/${baseMount}/${secretPath.join("")}?list=true`;
secretURL = `/v1/${baseMount}/${secretPath.join("/")}?list=true`;
}
const request = new Request(appendAPIURL(secretURL), {
headers: getHeaders(),

View file

@ -52,6 +52,9 @@ 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";
import { TransitView } from "./ui/pages/Secrets/Transit/TransitView";
import { TransitViewSecret } from "./ui/pages/Secrets/Transit/TransitViewSecret";
import { NewTransitKey } from "./ui/pages/Secrets/Transit/NewTransitKey";
async function onLoad(): Promise<void> {
const Main = () => (
@ -72,19 +75,25 @@ async function onLoad(): Promise<void> {
<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/:baseMount/:secretPath*?" state={pageState} />
<KeyValueSecret path="/secrets/kv/view/:item/:baseMount/:secretPath+" state={pageState} />
<KeyValueSecretEdit path="/secrets/kv/edit/:item/:baseMount/:secretPath+" state={pageState} />
<KeyValueDelete path="/secrets/kv/delete/:item/:baseMount/:secretPath+" state={pageState} />
<TOTPView path="/secrets/totp/list/:baseMount" state={pageState} />
<TOTPNew path="/secrets/totp/new/:baseMount" state={pageState} />
<TOTPDelete path="/secrets/totp/delete/:baseMount/:item" state={pageState} />
<TransitView path="/secrets/transit/list/:baseMount" state={pageState} />
<TransitViewSecret path="/secrets/transit/view/:baseMount/:secretItem" state={pageState} />
<NewTransitKey path="/secrets/transit/new/:baseMount" 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(
<>
<NavBar />

View file

@ -4,19 +4,22 @@ import { deleteSecret } from "../../../../api/kv/deleteSecret";
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 baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const item = this.props.matches["item"];
return (
<>
<SecretTitleElement type="kv" item={this.props.matches["item"]} mount={mount} suffix={i18next.t("kv_sec_edit_suffix")} />
<SecretTitleElement
type="kv"
baseMount={baseMount}
secretPath={secretPath}
item={item}
suffix={i18next.t("kv_sec_edit_suffix")}
/>
<div>
<h5>{i18next.t("kv_delete_text")}</h5>
<button
@ -24,7 +27,7 @@ export class KeyValueDelete extends Component<DefaultPageProps> {
onClick={async () => {
await deleteSecret(
baseMount,
secretPath.map((e) => e + "/"),
secretPath,
item,
);
window.history.back();

View file

@ -1,25 +1,29 @@
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { createOrUpdateSecret } from "../../../../api/kv/createOrUpdateSecret";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { kvViewURL } from "../../pageLinks";
export class KeyValueNewPage extends Page {
constructor() {
super();
}
export class KeyValueNewPage extends Component<DefaultPageProps> {
render(){
const baseMount = this.props.matches["baseMount"];
const secretPath = (this.props.matches["secretPath"] || "").split("/");
async goBack(): Promise<void> {
await this.router.changePage("KEY_VALUE_VIEW");
}
async render(): Promise<void> {
render(
return(
<>
<SecretTitleElement
type="kv"
baseMount={baseMount}
secretPath={secretPath}
suffix={i18next.t("kv_sec_edit_suffix")}
/>
<div>
<Form onSubmit={async (formData) => await this.newKVSecretHandleForm(formData)}>
<Form onSubmit={async (formData) => await this.newKVSecretHandleForm(formData, baseMount, secretPath)}>
<Margin>
<input
class="uk-input uk-form-width-medium"
@ -33,41 +37,28 @@ export class KeyValueNewPage extends Page {
{i18next.t("kv_new_create_btn")}
</button>
</Form>
</div>,
this.router.pageContentElement,
</div>
</>
);
}
async newKVSecretHandleForm(formData: FormData): Promise<void> {
async newKVSecretHandleForm(formData: FormData, baseMount: string, secretPath: string[]): Promise<void> {
const path = formData.get("path") as string;
let keyData = {};
if (["kv-v1", "cubbyhole"].includes(this.state.secretMountType)) {
keyData = { key: "value" };
}
// TODO: check only do this on kv v1
let keyData = { "key": "value" };
try {
await createOrUpdateSecret(
this.state.baseMount,
this.state.secretPath,
baseMount,
secretPath,
path,
keyData,
);
await this.router.changePage("KEY_VALUE_VIEW");
route(kvViewURL(baseMount, secretPath, path))
} catch (e: unknown) {
const error = e as Error;
setErrorText(error.message);
}
}
//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

@ -2,17 +2,14 @@ import { CodeBlock } from "../../../elements/CodeBlock";
import { Component, JSX, render } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { Grid, GridSizes } from "../../../elements/Grid";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getCapabilities } from "../../../../api/sys/getCapabilities";
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";
import { kvDeleteURL, kvEditURL } from "../../pageLinks";
export type KVSecretViewProps = {
kvData: Record<string, unknown>;
@ -55,10 +52,8 @@ type KeyValueSecretState = {
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 baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"];
const caps = (
@ -84,24 +79,25 @@ export class KeyValueSecret extends Component<DefaultPageProps, KeyValueSecretSt
}
render() {
if (!this.state.baseMount) return;
// TODO: maybe add some sort of version viewing
// no idea what gonna do for that
if (!this.state.baseMount) return;
return (
<>
<SecretTitleElement type="kv" item={this.props.matches["item"]} mount={this.props.matches["mount"]} suffix={i18next.t("kv_sec_edit_suffix")} />
<SecretTitleElement
type="kv"
item={this.props.matches["item"]}
baseMount={this.state.baseMount}
secretPath={this.state.secretPath}
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");
route(kvDeleteURL(this.state.baseMount, this.state.secretPath, this.state.secretItem))
}}
>
{i18next.t("kv_secret_delete_btn")}
@ -112,7 +108,7 @@ export class KeyValueSecret extends Component<DefaultPageProps, KeyValueSecretSt
<button
class="uk-button uk-button-primary"
onClick={async () => {
route("/secrets/kv/edit/" + this.state.secretItem + "/" + this.state.baseMount + "/" + this.state.secretPath.join("/"))
route(kvEditURL(this.state.baseMount, this.state.secretPath, this.state.secretItem))
}}
>
{i18next.t("kv_secret_edit_btn")}
@ -125,14 +121,5 @@ export class KeyValueSecret extends Component<DefaultPageProps, KeyValueSecretSt
</>
);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("kv_secret_title");
}
}

View file

@ -8,9 +8,6 @@ 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 = {
baseMount: string;
@ -109,28 +106,21 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
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 baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const item = this.props.matches["item"];
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} />
<SecretTitleElement
type="kv"
baseMount={baseMount}
secretPath={secretPath}
item={this.props.matches["item"]}
suffix={i18next.t("kv_sec_edit_suffix")}
/>
<KVEditor baseMount={baseMount} secretPath={secretPath} 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

@ -8,7 +8,8 @@ import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { splitKVMount } from "./splitKVMount";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
import { getMount } from "../../../../api/sys/getMounts";
export type KVKeysListProps = DefaultPageProps & {
@ -22,15 +23,16 @@ type KVKeysListState = {
searchQuery: string;
};
function SecretsList(baseMount: string, restOfMount: string[], secrets: string[]): JSX.Element[] {
function SecretsList(baseMount: string, secretPath: string[], secrets: string[]): JSX.Element[] {
return secrets.map((secret) => (
<li>
<a
onClick={async () => {
console.log(baseMount, secretPath, secret)
if (secret.endsWith("/")) {
route("/secrets/kv/list/" + baseMount + "/" + restOfMount.join("/") + "/" + secret)
route(kvListURL(baseMount, [...secretPath, secret.replace("/", "")].filter((e)=>e.length > 0)))
} else {
route("/secrets/kv/view/" + secret + "/" + baseMount + "/" + restOfMount.join("/"))
route(kvViewURL(baseMount, secretPath, secret))
}
}}
>
@ -54,7 +56,7 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
try {
const keys = await getSecrets(
this.props.baseMount,
this.props.secretPath.map((e) => e + "/"),
this.props.secretPath,
);
this.setState({
dataLoaded: true,
@ -118,7 +120,6 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
this.setState({
searchQuery: (this.searchBarRef.current as unknown as HTMLInputElement).value,
});
console.log("meow");
}}
/>
<br />
@ -140,60 +141,65 @@ export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
type KeyValueViewState = {
pathCaps: string[];
mountCaps: string[];
mountType: string;
}
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 baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const mountsPath = "/sys/mounts/" + baseMount;
const currentPath = baseMount + restOfMount.join();
const currentPath = baseMount + secretPath.join();
const caps = await getCapabilitiesPath([mountsPath, currentPath]);
const mount = await getMount(baseMount);
this.setState({
mountCaps: caps[mountsPath],
pathCaps: caps[currentPath],
mountType: mount.type,
});
}
render() {
if (!this.state.pathCaps) return;
const mount = this.props.matches["mount"];
const mountSplit = splitKVMount(mount);
const baseMount = mountSplit.baseMount;
const restOfMount = mountSplit.restOfMount;
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
return (
<>
<SecretTitleElement type="kv" mount={mount} item={this.props.matches["item"]} />
<SecretTitleElement
type="kv"
baseMount={baseMount}
secretPath={secretPath}
item={this.props.matches["item"]}
/>
<p>
{this.state.pathCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route("/secrets/kv/new/" + mount)
route(kvNewURL(baseMount, secretPath.length > 0 ? secretPath : null))
}}
>
{i18next.t("kv_view_new_btn")}
</button>
)}
{restOfMount.length == 0 && this.state.mountCaps.includes("delete") && (
{secretPath.length == 0 && this.state.mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route("/secrets/delete_engine/" + mount)
route(delSecretsEngineURL(baseMount))
}}
>
{i18next.t("kv_view_delete_btn")}
</button>
)}
</p>
{//"TODO: " == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
}
<KVKeysList baseMount={baseMount} secretPath={restOfMount} state={this.props.state} />
{this.state.mountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
<KVKeysList baseMount={baseMount} secretPath={secretPath} state={this.props.state} />
</>
);
}

View file

@ -1,10 +0,0 @@
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

@ -8,6 +8,7 @@ import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { PageTitle } from "../../../elements/PageTitle";
import { route } from "preact-router";
import { kvListURL } from "../../pageLinks";
export class NewKVEngine extends Component {
render() {
@ -57,7 +58,7 @@ export class NewKVEngine extends Component {
version: version,
},
});
route("/secrets/kv/list/" + name + "/")
route(kvListURL(name, []));
} catch (e) {
const error = e as Error;
setErrorText(error.message);

View file

@ -8,6 +8,7 @@ import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { route } from "preact-router";
import { PageTitle } from "../../../elements/PageTitle";
import { totpListURL } from "../../pageLinks";
export class NewTOTPEngine extends Component {
render() {
@ -43,7 +44,7 @@ export class NewTOTPEngine extends Component {
name: name,
type: "totp",
});
route("/secrets/totp/list/" + name + "/")
route(totpListURL(name));
} catch (e) {
const error = e as Error;
setErrorText(error.message);

View file

@ -1,29 +1,28 @@
import { route } from "preact-router";
import { JSX } from "preact/jsx-runtime";
import { Page } from "../../../types/Page";
import { kvListURL } from "../pageLinks";
type SecretTitleElementProps = {
type: string;
mount: string;
baseMount: string;
secretPath?: string[];
item?: string;
suffix?: string;
};
export function SecretTitleElement(props: SecretTitleElementProps): JSX.Element {
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();
const baseMount = props.baseMount;
const secretPath = props.secretPath || [];
return (
<h3 class="uk-card-title" id="pageTitle">
<div>
<a
test-data="secrets-title-first-slash"
onClick={async () => {
route("/secrets");
}}
@ -31,22 +30,25 @@ export function SecretTitleElement(props: SecretTitleElementProps): JSX.Element
{"/ "}
</a>
<a href={"/secrets/" + type + "/list/" + baseMount + "/"}>
{baseMount + " "}
<a href={"/secrets/" + type + "/list/" + baseMount + "/"} test-data="secrets-title-baseMount">
{baseMount + "/ "}
</a>
{...restOfMount.map((secretPath, index, secretPaths) => (
<a
{...secretPath.map((secretPath, index, secretPaths) => {
// TODO: find where a '' is returned so dont need this
if (secretPath.length < 1) return;
return <a test-data="secrets-title-secretPath"
onClick={async () => {
if (type == "kv") {
let secretPath = secretPaths.slice(0, index + 1);
route("/secrets/kv/list/" + baseMount + "/" + secretPath.join("/"))
route(kvListURL(baseMount, secretPath))
}
}}
>
{secretPath + "/" + " "}
</a>
))}
})}
{item.length != 0 && <span>{item}</span>}
{suffix.length != 0 && <span>{suffix}</span>}
</div>

View file

@ -5,35 +5,34 @@ import { Component, render } from "preact";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { totpListURL } from "../../pageLinks";
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 () => {
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>
<>
<SecretTitleElement
type="totp"
baseMount={this.props.matches["baseMount"]}
item={this.props.matches["item"]}
suffix={i18next.t("totp_delete_suffix")}
/>
<div>
<h5>{i18next.t("totp_delete_text")}</h5>
<button
class="uk-button uk-button-danger"
onClick={async () => {
let baseMount = this.props.matches["baseMount"];
let item = this.props.matches["item"];
await deleteTOTP(baseMount, item);
route(totpListURL(baseMount));
}}
>
{i18next.t("kv_delete_btn")}
</button>
</div>
</>
);
}
//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

@ -21,7 +21,7 @@ function removeDashSpaces(str: string): string {
return str;
}
export class TOTPNewForm extends Component<{ mount: string }, { qrMode: boolean }> {
export class TOTPNewForm extends Component<{ baseMount: string }, { qrMode: boolean }> {
constructor() {
super();
this.state = {
@ -40,8 +40,8 @@ export class TOTPNewForm extends Component<{ mount: string }, { qrMode: boolean
};
try {
await addNewTOTP(this.props.mount, parms);
route("/secrets/totp/list/" + this.props.mount)
await addNewTOTP(this.props.baseMount, parms);
route("/secrets/totp/list/" + this.props.baseMount)
} catch (e: unknown) {
const error = e as Error;
setErrorText(`API Error: ${error.message}`);
@ -121,23 +121,12 @@ export class TOTPNewForm extends Component<{ mount: string }, { qrMode: boolean
export class TOTPNew extends Component<DefaultPageProps> {
render() {
let mount = this.props.matches["mount"];
const baseMount = this.props.matches["baseMount"];
return (
<>
<SecretTitleElement type="totp" mount={mount} suffix={i18next.t("totp_new_suffix")} />
<TOTPNewForm mount={this.props.matches["mount"]} />
<SecretTitleElement type="totp" baseMount={baseMount} suffix={i18next.t("totp_new_suffix")} />
<TOTPNewForm baseMount={baseMount} />
</>
);
}
//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

@ -13,9 +13,10 @@ import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { delSecretsEngineURL, totpNewURL } from "../../pageLinks";
type TOTPGridItemProps = {
mount: string; totpKey: string; canDelete: boolean;
baseMount: string; totpKey: string; canDelete: boolean;
}
export class RefreshingTOTPGridItem extends Component<
@ -29,7 +30,7 @@ export class RefreshingTOTPGridItem extends Component<
timer: unknown;
updateTOTPCode(): void {
void getTOTPCode(this.props.mount, this.props.totpKey).then((code) => {
void getTOTPCode(this.props.baseMount, this.props.totpKey).then((code) => {
this.setState({ totpValue: code });
});
}
@ -56,7 +57,7 @@ export class RefreshingTOTPGridItem extends Component<
<button
class="uk-button uk-button-danger"
onClick={async () => {
route("/secrets/totp/delete/" + this.props.mount + "/" + this.props.totpKey);
route("/secrets/totp/delete/" + this.props.baseMount + "/" + this.props.totpKey);
}}
>
{i18next.t("totp_view_secret_delete_btn")}
@ -69,15 +70,12 @@ export class RefreshingTOTPGridItem extends Component<
}
}
type TOTPViewProps = DefaultPageProps & {
}
type TOTPViewState = {
capabilities?: CapabilitiesType;
totpItems: TOTPGridItemProps[];
}
export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
export class TOTPView extends Component<DefaultPageProps, TOTPViewState> {
constructor() {
super();
this.refresher = undefined;
@ -87,22 +85,22 @@ export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
refresher: number;
async componentDidMount() {
var mount = this.props.matches["mount"];
const mountsPath = "/sys/mounts/" + mount;
const caps = await getCapabilitiesPath([mountsPath, mount]);
var baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const caps = await getCapabilitiesPath([mountsPath, baseMount]);
let totpItems: TOTPGridItemProps[] = [];
// TODO: tidy this up i guess
try {
totpItems = await Promise.all(
Array.from(await getTOTPKeys(mount)).map(async (key) => {
Array.from(await getTOTPKeys(baseMount)).map(async (key) => {
const totpCaps = await getCapsPath(
removeDoubleSlash(mount + "code/" + key),
removeDoubleSlash(baseMount + "/code/" + key),
);
if (totpCaps.includes("read")) {
return {
mount: mount,
baseMount: baseMount,
totpKey: key,
canDelete: totpCaps.includes("delete"),
};
@ -124,22 +122,22 @@ export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
render() {
if (!this.state.capabilities) return;
var mount = this.props.matches["mount"];
var baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + mount;
const mountsPath = "/sys/mounts/" + baseMount;
const mountCaps = this.state.capabilities[mountsPath];
const totpCaps = this.state.capabilities[mount];
const totpCaps = this.state.capabilities[baseMount];
return (
<>
<SecretTitleElement type="totp" mount={mount} />
<SecretTitleElement type="totp" baseMount={baseMount} />
<div>
<p>
{totpCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route("/secrets/totp/new/" + mount)
route(totpNewURL(baseMount))
}}
>
{i18next.t("totp_view_new_btn")}
@ -149,7 +147,7 @@ export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
<button
class="uk-button uk-button-danger"
onClick={async () => {
route("/secrets/delete_engine/" + mount)
route(delSecretsEngineURL(baseMount))
}}
>
{i18next.t("totp_view_delete_btn")}
@ -164,7 +162,7 @@ export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
} else {
return this.state.totpItems.map((totpItem) => {
return <RefreshingTOTPGridItem
mount={totpItem.mount}
baseMount={totpItem.baseMount}
totpKey={totpItem.totpKey}
canDelete={totpItem.canDelete}
/>
@ -178,8 +176,4 @@ export class TOTPView extends Component<TOTPViewProps, TOTPViewState> {
);
}
get name(): string {
return i18next.t("totp_view_title");
}
}

View file

@ -1,89 +1,89 @@
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { newTransitKey } from "../../../../api/transit/newTransitKey";
import { render } from "preact";
import { Component } from "preact";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { route } from "preact-router";
import { transitViewSecretURL } from "../../pageLinks";
export class NewTransitKeyPage extends Page {
export class NewTransitKey extends Component<DefaultPageProps> {
constructor() {
super();
}
async render(): Promise<void> {
render(
<Form
onSubmit={async (data) => {
await this.onSubmit(data);
}}
>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
placeholder={i18next.t("transit_new_key_name_input")}
type="text"
required
/>
</Margin>
<Margin>
<select class="uk-select uk-form-width-medium" name="type">
{[
"aes128-gcm96",
"aes256-gcm96",
"chacha20-poly1305",
"ed25519",
"ecdsa-p256",
"ecdsa-p384",
"ecdsa-p521",
"rsa-2048",
"rsa-3072",
"rsa-4096",
].map((type) => (
<option label={type} value={type}>
{type}
</option>
))}
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_new_key_create_btn")}
</button>
</MarginInline>
</Form>,
this.router.pageContentElement,
render() {
const baseMount = this.props.matches["baseMount"];
return (
<>
<SecretTitleElement
type="transit"
baseMount={baseMount}
suffix={i18next.t("transit_new_key_suffix")}
/>
<Form
onSubmit={async (data) => {
await this.onSubmit(data);
}}
>
<Margin>
<input
class="uk-input uk-form-width-medium"
name="name"
placeholder={i18next.t("transit_new_key_name_input")}
type="text"
required
/>
</Margin>
<Margin>
<select class="uk-select uk-form-width-medium" name="type">
{[
"aes128-gcm96",
"aes256-gcm96",
"chacha20-poly1305",
"ed25519",
"ecdsa-p256",
"ecdsa-p384",
"ecdsa-p521",
"rsa-2048",
"rsa-3072",
"rsa-4096",
].map((type) => (
<option label={type} value={type}>
{type}
</option>
))}
</select>
</Margin>
<p class="uk-text-danger" id="errorText" />
<MarginInline>
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_new_key_create_btn")}
</button>
</MarginInline>
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
const baseMount = this.props.matches["baseMount"];
const name = data.get("name") as string;
const type = data.get("type") as string;
try {
await newTransitKey(this.state.baseMount, {
await newTransitKey(baseMount, {
name: name,
type: type,
});
this.state.secretItem = name;
await this.router.changePage("TRANSIT_VIEW_SECRET");
route(transitViewSecretURL(baseMount, name))
} catch (e) {
const error = e as Error;
setErrorText(error.message);
}
}
//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

@ -1,16 +1,18 @@
import { Component, JSX, render } from "preact";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { getTransitKeys } from "../../../../api/transit/getTransitKeys";
import i18next from "i18next";
import { route } from "preact-router";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks";
type TransitViewListState = {
contentLoaded: boolean;
transitKeysList: string[];
};
export class TransitViewListItem extends Component<{ page: Page }, TransitViewListState> {
export class TransitViewListItem extends Component<{ baseMount: string }, TransitViewListState> {
constructor() {
super();
this.state = {
@ -22,7 +24,7 @@ export class TransitViewListItem extends Component<{ page: Page }, TransitViewLi
timer: unknown;
getTransitKeys(): void {
void getTransitKeys(this.props.page.state.baseMount)
void getTransitKeys(this.props.baseMount)
.then((keys) => {
this.setState({
contentLoaded: true,
@ -56,8 +58,7 @@ export class TransitViewListItem extends Component<{ page: Page }, TransitViewLi
<li>
<a
onClick={async () => {
this.props.page.state.secretItem = key;
await this.props.page.router.changePage("TRANSIT_VIEW_SECRET");
route(transitViewSecretURL(this.props.baseMount, key))
}}
>
{key}
@ -69,31 +70,32 @@ export class TransitViewListItem extends Component<{ page: Page }, TransitViewLi
}
}
export class TransitViewPage extends Page {
constructor() {
super();
export class TransitView extends Component<DefaultPageProps, {caps: CapabilitiesType}> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const caps = await getCapabilitiesPath([mountsPath, baseMount]);
this.setState({caps})
}
async goBack(): Promise<void> {
await this.router.changePage("SECRETS_HOME");
}
render() {
if (!this.state.caps) return;
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const mountCaps = this.state.caps[mountsPath];
const transitCaps = this.state.caps[baseMount];
async render(): Promise<void> {
this.state.secretItem = "";
const mountsPath = "/sys/mounts/" + this.state.baseMount;
const caps = await getCapabilitiesPath([mountsPath, this.state.baseMount]);
const mountCaps = caps[mountsPath];
const transitCaps = caps[this.state.baseMount];
render(
return (
<>
<SecretTitleElement type="transit" baseMount={baseMount} />
<p>
{transitCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
await this.router.changePage("TRANSIT_NEW_KEY");
route(transitNewSecretURL(baseMount));
}}
>
{i18next.t("transit_view_new_btn")}
@ -103,24 +105,15 @@ export class TransitViewPage extends Page {
<button
class="uk-button uk-button-danger"
onClick={async () => {
await this.router.changePage("DELETE_SECRET_ENGINE");
route(delSecretsEngineURL(baseMount))
}}
>
{i18next.t("transit_view_delete_btn")}
</button>
)}
</p>
<TransitViewListItem page={this} />
</>,
this.router.pageContentElement,
<TransitViewListItem baseMount={baseMount} />
</>
);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("transit_view_title");
}
}

View file

@ -3,22 +3,27 @@ import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { Tile } from "../../../elements/Tile";
import { getTransitKey } from "../../../../api/transit/getTransitKey";
import { render } from "preact";
import { Component, render } from "preact";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { TransitKeyType } from "../../../../api/types/transit";
export class TransitViewSecretPage extends Page {
constructor() {
super();
export class TransitViewSecret extends Component<DefaultPageProps, {transitKey: TransitKeyType}> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const transitKey = await getTransitKey(baseMount, secretItem);
this.setState({transitKey});
}
async goBack(): Promise<void> {
await this.router.changePage("TRANSIT_VIEW");
}
render() {
if (!this.state.transitKey) return;
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const transitKey = this.state.transitKey;
async render(): Promise<void> {
const transitKey = await getTransitKey(this.state.baseMount, this.state.secretItem);
render(
return (
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
{transitKey.supports_encryption && (
<Tile
@ -26,7 +31,7 @@ export class TransitViewSecretPage extends Page {
description={i18next.t("transit_view_encrypt_description")}
icon="lock"
iconText={i18next.t("transit_view_encrypt_icon_text")}
onclick={async () => await this.router.changePage("TRANSIT_ENCRYPT")}
onclick={async () => {} /*await this.router.changePage("TRANSIT_ENCRYPT")*/}
/>
)}
{transitKey.supports_decryption && (
@ -35,7 +40,7 @@ export class TransitViewSecretPage extends Page {
description={i18next.t("transit_view_decrypt_description")}
icon="mail"
iconText={i18next.t("transit_view_decrypt_icon_text")}
onclick={async () => await this.router.changePage("TRANSIT_DECRYPT")}
onclick={async () => {} /*await this.router.changePage("TRANSIT_DECRYPT")*/}
/>
)}
{transitKey.supports_decryption && (
@ -44,19 +49,10 @@ export class TransitViewSecretPage extends Page {
description={i18next.t("transit_view_rewrap_description")}
icon="code"
iconText={i18next.t("transit_view_rewrap_icon_text")}
onclick={async () => await this.router.changePage("TRANSIT_REWRAP")}
onclick={async () => {} /*await this.router.changePage("TRANSIT_REWRAP")*/}
/>
)}
</Grid>,
this.router.pageContentElement,
</Grid>
);
}
//async renderPageTitle(): Promise<void> {
// render(<SecretTitleElement page={this} />, this.router.pageTitleElement);
//}
get name(): string {
return i18next.t("transit_view_secret_title");
}
}

View file

@ -0,0 +1,55 @@
// Delete Secret Engine
export function delSecretsEngineURL(baseMount: string): string {
return `/secrets/delete_engine/${baseMount}`;
}
// Secrets / Key Value
export function kvNewURL(baseMount: string, secretPath?: string[]): string {
return `/secrets/kv/new/${baseMount}` + secretPath ? `/${secretPath.join("/")}` : "";
}
export function kvDeleteURL(baseMount: string, secretPath: string[], secret: string): string {
return `/secrets/kv/delete/${secret}/${baseMount}/${secretPath.join("/")}`;
}
export function kvEditURL(baseMount: string, secretPath: string[], secret: string): string {
return `/secrets/kv/edit/${secret}/${baseMount}/${secretPath.join("/")}`;
}
export function kvViewURL(baseMount: string, secretPath: string[], secret: string): string {
return `/secrets/kv/view/${secret}/${baseMount}/${secretPath.join("/")}`;
}
export function kvListURL(baseMount: string, secretPath: string[]): string {
console.log(baseMount, secretPath);
return `/secrets/kv/list/${baseMount}/${secretPath.join("/")}`;
}
// Secrets / TOTP
export function totpNewURL(baseMount: string): string {
return `/secrets/totp/new/${baseMount}`;
}
export function totpListURL(baseMount: string): string {
return `/secrets/totp/list/${baseMount}`;
}
export function totpDeleteURL(baseMount: string, secret: string): string {
return `/secrets/totp/delete/${baseMount}/${secret}`;
}
// Secrets / Transit
export function transitNewSecretURL(baseMount: string): string {
return `/secrets/transit/new/${baseMount}`;
}
export function transitViewSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/view/${baseMount}/${secret}`;
}
//