1
0
Fork 0

get all secrets urls working

This commit is contained in:
ChaotiCryptidz 2022-01-07 12:05:24 +00:00
parent 6131a13331
commit ee93d882c8
15 changed files with 630 additions and 610 deletions

View file

@ -30,9 +30,9 @@ import { Component, render } from "preact";
import { DeleteSecretsEngine } from "./ui/pages/Secrets/DeleteSecretsEngine";
import { Home } from "./ui/pages/Home";
import { KeyValueDelete } from "./ui/pages/Secrets/KeyValue/KeyValueDelete";
import { KeyValueSecret } from "./ui/pages/Secrets/KeyValue/KeyValueSecret";
import { KeyValueSecretEdit } from "./ui/pages/Secrets/KeyValue/KeyValueSecretsEdit";
import { KeyValueView } from "./ui/pages/Secrets/KeyValue/KeyValueView";
import { KeyValueEdit } from "./ui/pages/Secrets/KeyValue/KeyValueEdit";
import { KeyValueList } from "./ui/pages/Secrets/KeyValue/KeyValueList";
import { Login } from "./ui/pages/Login";
import { Me } from "./ui/pages/Me";
import { NavBar } from "./ui/elements/NavBar";
@ -40,21 +40,25 @@ import { NewKVEngine } from "./ui/pages/Secrets/NewEngines/NewKVEngine";
import { NewSecretsEngine } from "./ui/pages/Secrets/NewSecretsEngine";
import { NewTOTPEngine } from "./ui/pages/Secrets/NewEngines/NewTOTPEngine";
import { NewTransitEngine } from "./ui/pages/Secrets/NewEngines/NewTransitEngine";
import { NewTransitKey } from "./ui/pages/Secrets/Transit/NewTransitKey";
import { TransitNew } from "./ui/pages/Secrets/Transit/TransitNew";
import { PasswordGenerator } from "./ui/pages/PwGen";
import { Secrets } from "./ui/pages/Secrets/SecretsHome";
import { SetLanguage } from "./ui/pages/SetLanguage";
import { SetVaultURL } from "./ui/pages/SetVaultURL";
import { TOTPDelete } from "./ui/pages/Secrets/TOTP/TOTPDelete";
import { TOTPNew } from "./ui/pages/Secrets/TOTP/TOTPNew";
import { TOTPView } from "./ui/pages/Secrets/TOTP/TOTPView";
import { TOTPList } from "./ui/pages/Secrets/TOTP/TOTPList";
import { TransitList } from "./ui/pages/Secrets/Transit/TransitList";
import { TransitView } from "./ui/pages/Secrets/Transit/TransitView";
import { TransitViewSecret } from "./ui/pages/Secrets/Transit/TransitViewSecret";
import { Unseal } from "./ui/pages/Unseal";
import { pageState } from "./globalPageState";
import { playground } from "./playground";
import Router from "preact-router";
import i18next from "i18next";
import { KeyValueNew } from "./ui/pages/Secrets/KeyValue/KeyValueNew";
import { TransitDecrypt } from "./ui/pages/Secrets/Transit/TransitDecrypt";
import { TransitEncrypt } from "./ui/pages/Secrets/Transit/TransitEncrypt";
import { TransitRewrap } from "./ui/pages/Secrets/Transit/TransitRewrap";
async function onLoad(): Promise<void> {
const Main = () => (
@ -75,18 +79,23 @@ async function onLoad(): Promise<void> {
<NewTOTPEngine path="/secrets/new_secrets_engine/totp" />
<NewTransitEngine path="/secrets/new_secrets_engine/trasit" />
<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} />
<KeyValueNew path="/secrets/kv/new/:baseMount/:secretPath*?" state={pageState} />
<KeyValueList path="/secrets/kv/list/:baseMount/:secretPath*?" state={pageState} />
<KeyValueView path="/secrets/kv/view/:item/:baseMount/:secretPath*?" state={pageState} />
<KeyValueEdit 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} />
<TOTPList 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} />
<TransitNew path="/secrets/transit/new/:baseMount" state={pageState} />
<TransitList path="/secrets/transit/list/:baseMount" state={pageState} />
<TransitView path="/secrets/transit/view/:baseMount/:secretItem" state={pageState} />
<TransitEncrypt path="/secrets/transit/encrypt/:baseMount/:secretItem" state={pageState} />
<TransitDecrypt path="/secrets/transit/decrypt/:baseMount/:secretItem" state={pageState} />
<TransitRewrap path="/secrets/transit/rewrap/:baseMount/:secretItem" state={pageState} />
<div default>
<p>PAGE NOT YET IMPLEMENTED</p>

View file

@ -104,7 +104,7 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
}
}
export class KeyValueSecretEdit extends Component<DefaultPageProps> {
export class KeyValueEdit extends Component<DefaultPageProps> {
render() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");

View file

@ -0,0 +1,209 @@
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { Component, JSX, createRef, render } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
import { getMount } from "../../../../api/sys/getMounts";
import { getSecrets } from "../../../../api/kv/getSecrets";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export type KVKeysListProps = DefaultPageProps & {
baseMount: string;
secretPath: string[];
};
type KVKeysListState = {
dataLoaded: boolean;
keys: string[];
searchQuery: string;
};
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(
kvListURL(
baseMount,
[...secretPath, secret.replace("/", "")].filter((e) => e.length > 0),
),
);
} else {
route(kvViewURL(baseMount, secretPath, secret));
}
}}
>
{secret}
</a>
</li>
));
}
export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
constructor() {
super();
this.state = {
dataLoaded: false,
keys: [],
searchQuery: "",
};
}
async loadData(): Promise<void> {
try {
const keys = await getSecrets(this.props.baseMount, this.props.secretPath);
this.setState({
dataLoaded: true,
keys: keys,
});
return;
} catch (e: unknown) {
const error = e as Error;
if (error == DoesNotExistError) {
// getSecrets also 404's on no keys so dont go all the way back.
if (this.props.secretPath.length != 0) {
window.history.back();
return;
}
} else {
setErrorText(error.message);
}
this.setState({
dataLoaded: true,
keys: null,
});
}
}
componentDidUpdate(prevProps: KVKeysListProps): void {
if (
prevProps.baseMount !== this.props.baseMount ||
prevProps.secretPath !== this.props.secretPath
) {
this.setState({
dataLoaded: false,
});
void this.loadData();
}
}
componentDidMount(): void {
void this.loadData();
}
searchBarRef = createRef();
render(): JSX.Element {
if (!this.state.dataLoaded) {
return <p>{i18next.t("content_loading")}</p>;
}
if (this.state.keys == null) {
return <p>{i18next.t("kv_view_none_here_text")}</p>;
}
return (
<>
<input
ref={this.searchBarRef}
class="uk-input uk-form-width-medium uk-margin-bottom"
name="path"
placeholder={i18next.t("kv_view_search_input_text")}
onInput={async () => {
this.setState({
searchQuery: (this.searchBarRef.current as unknown as HTMLInputElement).value,
});
}}
/>
<br />
<ul class="uk-nav uk-nav-default">
{...((): JSX.Element[] => {
let secrets: string[] = this.state.keys;
if (this.state.searchQuery.length > 0) {
secrets = secrets.filter((secret) => secret.includes(this.state.searchQuery));
}
return SecretsList(this.props.baseMount, this.props.secretPath, secrets);
})()}
</ul>
</>
);
}
}
type KeyValueListState = {
pathCaps: string[];
mountCaps: string[];
mountType: string;
};
export class KeyValueList extends Component<DefaultPageProps, KeyValueListState> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const mountsPath = "/sys/mounts/" + baseMount;
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 baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
return (
<>
<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={() => {
console.log(kvNewURL(baseMount, secretPath));
route(kvNewURL(baseMount, secretPath));
}}
>
{i18next.t("kv_view_new_btn")}
</button>
)}
{secretPath.length == 0 && this.state.mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(delSecretsEngineURL(baseMount));
}}
>
{i18next.t("kv_view_delete_btn")}
</button>
)}
</p>
{this.state.mountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
<KVKeysList baseMount={baseMount} secretPath={secretPath} state={this.props.state} />
</>
);
}
}

View file

@ -9,7 +9,7 @@ import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import i18next from "i18next";
export class KeyValueNewPage extends Component<DefaultPageProps> {
export class KeyValueNew extends Component<DefaultPageProps> {
render() {
const baseMount = this.props.matches["baseMount"];
const secretPath = (this.props.matches["secretPath"] || "").split("/");

View file

@ -1,121 +0,0 @@
import { CodeBlock } from "../../../elements/CodeBlock";
import { Component, JSX, render } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { Grid, GridSizes } from "../../../elements/Grid";
import { SecretTitleElement } from "../SecretTitleElement";
import { getCapabilities } from "../../../../api/sys/getCapabilities";
import { getSecret } from "../../../../api/kv/getSecret";
import { kvDeleteURL, kvEditURL } from "../../pageLinks";
import { route } from "preact-router";
import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next";
export type KVSecretViewProps = {
kvData: Record<string, unknown>;
};
export class KVSecretVew extends Component<KVSecretViewProps, unknown> {
render(): JSX.Element {
const secretsMap = sortedObjectMap(this.props.kvData);
let isMultiLevelJSON = false;
for (const value of secretsMap.values()) {
if (typeof value == "object") isMultiLevelJSON = true;
}
if (isMultiLevelJSON) {
const jsonText = JSON.stringify(Object.fromEntries(secretsMap), null, 4);
return <CodeBlock language="json" code={jsonText} />;
} else {
return (
<>
{Array.from(secretsMap).map((data: [string, string]) => (
<Grid size={GridSizes.NORMAL}>
<CopyableInputBox text={data[0]} copyable />
<CopyableInputBox text={data[1]} copyable />
</Grid>
))}
</>
);
}
}
}
type KeyValueSecretState = {
baseMount: string;
secretPath: string[];
secretItem: string;
caps: string[];
secretInfo: Record<string, unknown>;
};
export class KeyValueSecret extends Component<DefaultPageProps, KeyValueSecretState> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"];
const caps = (await getCapabilities(baseMount, secretPath, secretItem)).capabilities;
const 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(baseMount, secretPathAPI, secretItem);
this.setState({
baseMount,
secretPath,
secretItem,
caps,
secretInfo,
});
}
render() {
if (!this.state.baseMount) return;
return (
<>
<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">
{this.state.caps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(
kvDeleteURL(this.state.baseMount, this.state.secretPath, this.state.secretItem),
);
}}
>
{i18next.t("kv_secret_delete_btn")}
</button>
)}
{this.state.caps.includes("update") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route(
kvEditURL(this.state.baseMount, this.state.secretPath, this.state.secretItem),
);
}}
>
{i18next.t("kv_secret_edit_btn")}
</button>
)}
</p>
{<KVSecretVew kvData={this.state.secretInfo} />}
</div>
</>
);
}
}

View file

@ -1,210 +1,121 @@
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { Component, JSX, createRef, render } from "preact";
import { CodeBlock } from "../../../elements/CodeBlock";
import { Component, JSX, render } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Page } from "../../../../types/Page";
import { Grid, GridSizes } from "../../../elements/Grid";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
import { getMount } from "../../../../api/sys/getMounts";
import { getSecrets } from "../../../../api/kv/getSecrets";
import { getCapabilities } from "../../../../api/sys/getCapabilities";
import { getSecret } from "../../../../api/kv/getSecret";
import { kvDeleteURL, kvEditURL } from "../../pageLinks";
import { route } from "preact-router";
import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next";
export type KVKeysListProps = DefaultPageProps & {
baseMount: string;
secretPath: string[];
export type KVSecretViewProps = {
kvData: Record<string, unknown>;
};
type KVKeysListState = {
dataLoaded: boolean;
keys: string[];
searchQuery: string;
};
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(
kvListURL(
baseMount,
[...secretPath, secret.replace("/", "")].filter((e) => e.length > 0),
),
);
} else {
route(kvViewURL(baseMount, secretPath, secret));
}
}}
>
{secret}
</a>
</li>
));
}
export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
constructor() {
super();
this.state = {
dataLoaded: false,
keys: [],
searchQuery: "",
};
}
async loadData(): Promise<void> {
try {
const keys = await getSecrets(this.props.baseMount, this.props.secretPath);
this.setState({
dataLoaded: true,
keys: keys,
});
return;
} catch (e: unknown) {
const error = e as Error;
if (error == DoesNotExistError) {
// getSecrets also 404's on no keys so dont go all the way back.
if (this.props.secretPath.length != 0) {
window.history.back();
return;
}
} else {
setErrorText(error.message);
}
this.setState({
dataLoaded: true,
keys: null,
});
}
}
componentDidUpdate(prevProps: KVKeysListProps): void {
if (
prevProps.baseMount !== this.props.baseMount ||
prevProps.secretPath !== this.props.secretPath
) {
this.setState({
dataLoaded: false,
});
void this.loadData();
}
}
componentDidMount(): void {
void this.loadData();
}
searchBarRef = createRef();
export class KVSecretVew extends Component<KVSecretViewProps, unknown> {
render(): JSX.Element {
if (!this.state.dataLoaded) {
return <p>{i18next.t("content_loading")}</p>;
const secretsMap = sortedObjectMap(this.props.kvData);
let isMultiLevelJSON = false;
for (const value of secretsMap.values()) {
if (typeof value == "object") isMultiLevelJSON = true;
}
if (this.state.keys == null) {
return <p>{i18next.t("kv_view_none_here_text")}</p>;
if (isMultiLevelJSON) {
const jsonText = JSON.stringify(Object.fromEntries(secretsMap), null, 4);
return <CodeBlock language="json" code={jsonText} />;
} else {
return (
<>
{Array.from(secretsMap).map((data: [string, string]) => (
<Grid size={GridSizes.NORMAL}>
<CopyableInputBox text={data[0]} copyable />
<CopyableInputBox text={data[1]} copyable />
</Grid>
))}
</>
);
}
return (
<>
<input
ref={this.searchBarRef}
class="uk-input uk-form-width-medium uk-margin-bottom"
name="path"
placeholder={i18next.t("kv_view_search_input_text")}
onInput={async () => {
this.setState({
searchQuery: (this.searchBarRef.current as unknown as HTMLInputElement).value,
});
}}
/>
<br />
<ul class="uk-nav uk-nav-default">
{...((): JSX.Element[] => {
let secrets: string[] = this.state.keys;
if (this.state.searchQuery.length > 0) {
secrets = secrets.filter((secret) => secret.includes(this.state.searchQuery));
}
return SecretsList(this.props.baseMount, this.props.secretPath, secrets);
})()}
</ul>
</>
);
}
}
type KeyValueViewState = {
pathCaps: string[];
mountCaps: string[];
mountType: string;
baseMount: string;
secretPath: string[];
secretItem: string;
caps: string[];
secretInfo: Record<string, unknown>;
};
export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
const secretItem = this.props.matches["item"];
const mountsPath = "/sys/mounts/" + baseMount;
const currentPath = baseMount + secretPath.join();
const caps = await getCapabilitiesPath([mountsPath, currentPath]);
const caps = (await getCapabilities(baseMount, secretPath, secretItem)).capabilities;
const mount = await getMount(baseMount);
const 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(baseMount, secretPathAPI, secretItem);
this.setState({
mountCaps: caps[mountsPath],
pathCaps: caps[currentPath],
mountType: mount.type,
baseMount,
secretPath,
secretItem,
caps,
secretInfo,
});
}
render() {
if (!this.state.pathCaps) return;
const baseMount = this.props.matches["baseMount"];
const secretPath = this.props.matches["secretPath"].split("/");
if (!this.state.baseMount) return;
return (
<>
<SecretTitleElement
type="kv"
baseMount={baseMount}
secretPath={secretPath}
item={this.props.matches["item"]}
baseMount={this.state.baseMount}
secretPath={this.state.secretPath}
suffix={i18next.t("kv_sec_edit_suffix")}
/>
<p>
{this.state.pathCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route(kvNewURL(baseMount, secretPath.length > 0 ? secretPath : null));
}}
>
{i18next.t("kv_view_new_btn")}
</button>
)}
{secretPath.length == 0 && this.state.mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(delSecretsEngineURL(baseMount));
}}
>
{i18next.t("kv_view_delete_btn")}
</button>
)}
</p>
{this.state.mountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
<KVKeysList baseMount={baseMount} secretPath={secretPath} state={this.props.state} />
<div>
<p id="buttonsBlock">
{this.state.caps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(
kvDeleteURL(this.state.baseMount, this.state.secretPath, this.state.secretItem),
);
}}
>
{i18next.t("kv_secret_delete_btn")}
</button>
)}
{this.state.caps.includes("update") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route(
kvEditURL(this.state.baseMount, this.state.secretPath, this.state.secretItem),
);
}}
>
{i18next.t("kv_secret_edit_btn")}
</button>
)}
</p>
{<KVSecretVew kvData={this.state.secretInfo} />}
</div>
</>
);
}
get name(): string {
return i18next.t("kv_view_title");
}
}

View file

@ -3,13 +3,12 @@ import {
getCapabilitiesPath,
getCapsPath,
} from "../../../../api/sys/getCapabilities";
import { Component, JSX, render } from "preact";
import { Component, JSX } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Grid, GridSizes } from "../../../elements/Grid";
import { MarginInline } from "../../../elements/MarginInline";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, totpNewURL } from "../../pageLinks";
import { getTOTPCode } from "../../../../api/totp/getTOTPCode";
@ -73,12 +72,12 @@ export class RefreshingTOTPGridItem extends Component<TOTPGridItemProps, { totpV
}
}
type TOTPViewState = {
type TOTPListState = {
capabilities?: CapabilitiesType;
totpItems: TOTPGridItemProps[];
};
export class TOTPView extends Component<DefaultPageProps, TOTPViewState> {
export class TOTPList extends Component<DefaultPageProps, TOTPListState> {
constructor() {
super();
this.refresher = undefined;

View file

@ -3,51 +3,55 @@ import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import { transitDecrypt } from "../../../../api/transit/transitDecrypt";
import UIkit from "uikit";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
export class TransitDecryptPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("TRANSIT_VIEW_SECRET");
}
async render(): Promise<void> {
render(
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="ciphertext"
placeholder={i18next.t("transit_decrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="ciphertext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_decrypt_decrypt_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>,
this.router.pageContentElement,
export class TransitDecrypt extends Component<DefaultPageProps> {
render() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
return (
<>
<SecretTitleElement
type="transit"
baseMount={baseMount}
item={secretItem}
suffix={i18next.t("transit_decrypt_suffix")}
/>
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="ciphertext"
placeholder={i18next.t("transit_decrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="ciphertext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_decrypt_decode_checkbox")}>
<input class="uk-checkbox" name="decodeBase64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_decrypt_decrypt_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const decodeBase64 = data.get("decodeBase64Checkbox") as string;
let ciphertext = data.get("ciphertext") as string;
@ -60,7 +64,7 @@ export class TransitDecryptPage extends Page {
}
try {
const res = await transitDecrypt(this.state.baseMount, this.state.secretItem, {
const res = await transitDecrypt(baseMount, secretItem, {
ciphertext: ciphertext,
});
let plaintext = res.plaintext;
@ -81,15 +85,4 @@ export class TransitDecryptPage extends Page {
setErrorText(`API Error: ${error.message}`);
}
}
//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

@ -3,51 +3,55 @@ import { FileUploadInput } from "../../../elements/FileUploadInput";
import { Form } from "../../../elements/Form";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { Margin } from "../../../elements/Margin";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { fileToBase64 } from "../../../../htmlUtils";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import { transitEncrypt } from "../../../../api/transit/transitEncrypt";
import UIkit from "uikit";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
export class TransitEncryptPage extends Page {
constructor() {
super();
}
async goBack(): Promise<void> {
await this.router.changePage("TRANSIT_VIEW_SECRET");
}
async render(): Promise<void> {
render(
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="plaintext"
placeholder={i18next.t("transit_encrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="plaintext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_encrypt_encrypt_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>,
this.router.pageContentElement,
export class TransitEncrypt extends Component<DefaultPageProps> {
render() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
return (
<>
<SecretTitleElement
type="transit"
baseMount={baseMount}
item={secretItem}
suffix={i18next.t("transit_encrypt_suffix")}
/>
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<textarea
class="uk-textarea uk-form-width-medium"
name="plaintext"
placeholder={i18next.t("transit_encrypt_input_placeholder")}
/>
</Margin>
<Margin>
<FileUploadInput name="plaintext_file" />
</Margin>
<InputWithTitle title={i18next.t("transit_encrypt_already_encoded_checkbox")}>
<input class="uk-checkbox" name="base64Checkbox" type="checkbox" />
</InputWithTitle>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_encrypt_encrypt_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const base64Checkbox = data.get("base64Checkbox") as string;
let plaintext = data.get("plaintext") as string;
@ -61,7 +65,7 @@ export class TransitEncryptPage extends Page {
}
try {
const res = await transitEncrypt(this.state.baseMount, this.state.secretItem, {
const res = await transitEncrypt(baseMount, secretItem, {
plaintext: plaintext,
});
render(
@ -78,15 +82,4 @@ export class TransitEncryptPage extends Page {
setErrorText(`API Error: ${error.message}`);
}
}
//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

@ -0,0 +1,119 @@
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { Component, JSX, render } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks";
import { getTransitKeys } from "../../../../api/transit/getTransitKeys";
import { route } from "preact-router";
import i18next from "i18next";
type TransitViewListState = {
contentLoaded: boolean;
transitKeysList: string[];
};
export class TransitViewListItem extends Component<{ baseMount: string }, TransitViewListState> {
constructor() {
super();
this.state = {
contentLoaded: false,
transitKeysList: [],
};
}
timer: unknown;
getTransitKeys(): void {
void getTransitKeys(this.props.baseMount)
.then((keys) => {
this.setState({
contentLoaded: true,
transitKeysList: keys,
});
})
.catch((_) => {
this.setState({
contentLoaded: true,
transitKeysList: [],
});
});
}
componentDidMount(): void {
this.getTransitKeys();
}
render(): JSX.Element {
if (!this.state.contentLoaded) {
return <p>{i18next.t("content_loading")}</p>;
}
if (this.state.transitKeysList.length == 0) {
return <p>{i18next.t("transit_view_none_here_text")}</p>;
}
return (
<ul class="uk-nav uk-nav-default">
{...this.state.transitKeysList.map((key) => (
<li>
<a
onClick={async () => {
route(transitViewSecretURL(this.props.baseMount, key));
}}
>
{key}
</a>
</li>
))}
</ul>
);
}
}
export class TransitList 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 });
}
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];
return (
<>
<SecretTitleElement type="transit" baseMount={baseMount} />
<p>
{transitCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route(transitNewSecretURL(baseMount));
}}
>
{i18next.t("transit_view_new_btn")}
</button>
)}
{mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(delSecretsEngineURL(baseMount));
}}
>
{i18next.t("transit_view_delete_btn")}
</button>
)}
</p>
<TransitViewListItem baseMount={baseMount} />
</>
);
}
}

View file

@ -10,7 +10,7 @@ import { setErrorText } from "../../../../pageUtils";
import { transitViewSecretURL } from "../../pageLinks";
import i18next from "i18next";
export class NewTransitKey extends Component<DefaultPageProps> {
export class TransitNew extends Component<DefaultPageProps> {
constructor() {
super();
}

View file

@ -1,33 +1,38 @@
import { CopyableModal } from "../../../elements/CopyableModal";
import { Form } from "../../../elements/Form";
import { Margin } from "../../../elements/Margin";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { getTransitKey } from "../../../../api/transit/getTransitKey";
import { objectToMap } from "../../../../utils";
import { render } from "preact";
import { Component, render } from "preact";
import { setErrorText } from "../../../../pageUtils";
import { transitRewrap } from "../../../../api/transit/transitRewrap";
import UIkit from "uikit";
import i18next from "i18next";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { TransitKeyType } from "../../../../api/types/transit";
type versionOption = { version: string; label: string };
export class TransitRewrapPage extends Page {
constructor() {
super();
export class TransitRewrap extends Component<DefaultPageProps, { transitKey: TransitKeyType }> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
this.setState({
transitKey: await getTransitKey(baseMount, secretItem)
})
}
async goBack(): Promise<void> {
await this.router.changePage("TRANSIT_VIEW_SECRET");
}
render() {
if (!this.state.transitKey) return;
async render(): Promise<void> {
const transitKey = await getTransitKey(this.state.baseMount, this.state.secretItem);
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const stringVersions = Array.from(
objectToMap(transitKey.keys).keys(),
objectToMap(this.state.transitKey.keys).keys(),
).reverse() as unknown as string[];
const versions = stringVersions.map((val): number => parseInt(val, 10));
// get the selectable version options in the same
@ -45,37 +50,46 @@ export class TransitRewrapPage extends Page {
};
});
render(
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-width-1-2" name="version">
{options.map((option) => (
<option label={option.label} value={option.version}>
{option.label}
</option>
))}
</select>
</Margin>
<Margin>
<textarea
class="uk-textarea uk-width-1-2"
name="ciphertext"
placeholder={i18next.t("transit_rewrap_input_placeholder")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_rewrap_rewrap_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>,
this.router.pageContentElement,
return (
<>
<SecretTitleElement
type="transit"
baseMount={baseMount}
item={secretItem}
suffix={i18next.t("transit_rewrap_suffix")}
/>
<Form onSubmit={async (data) => await this.onSubmit(data)}>
<Margin>
<select class="uk-select uk-width-1-2" name="version">
{options.map((option) => (
<option label={option.label} value={option.version}>
{option.label}
</option>
))}
</select>
</Margin>
<Margin>
<textarea
class="uk-textarea uk-width-1-2"
name="ciphertext"
placeholder={i18next.t("transit_rewrap_input_placeholder")}
/>
</Margin>
<p class="uk-text-danger" id="errorText" />
<button class="uk-button uk-button-primary" type="submit">
{i18next.t("transit_rewrap_rewrap_btn")}
</button>
<div id="modalAttachmentPoint" />
</Form>
</>
);
}
async onSubmit(data: FormData): Promise<void> {
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
try {
const res = await transitRewrap(this.state.baseMount, this.state.secretItem, {
const res = await transitRewrap(baseMount, secretItem, {
ciphertext: data.get("ciphertext") as string,
key_version: parseInt(data.get("version") as string, 10),
});
@ -93,15 +107,4 @@ export class TransitRewrapPage extends Page {
setErrorText(`API Error: ${error.message}`);
}
}
//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

@ -1,118 +1,64 @@
import { CapabilitiesType, getCapabilitiesPath } from "../../../../api/sys/getCapabilities";
import { Component, JSX, render } from "preact";
import { Component, render } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { Grid, GridSizes } from "../../../elements/Grid";
import { SecretTitleElement } from "../SecretTitleElement";
import { delSecretsEngineURL, transitNewSecretURL, transitViewSecretURL } from "../../pageLinks";
import { getTransitKeys } from "../../../../api/transit/getTransitKeys";
import { route } from "preact-router";
import { Tile } from "../../../elements/Tile";
import { TransitKeyType } from "../../../../api/types/transit";
import { getTransitKey } from "../../../../api/transit/getTransitKey";
import i18next from "i18next";
import { route } from "preact-router";
import { transitDecryptSecretURL, transitEncryptSecretURL, transitRewrapSecretURL } from "../../pageLinks";
type TransitViewListState = {
contentLoaded: boolean;
transitKeysList: string[];
};
export class TransitViewListItem extends Component<{ baseMount: string }, TransitViewListState> {
constructor() {
super();
this.state = {
contentLoaded: false,
transitKeysList: [],
};
}
timer: unknown;
getTransitKeys(): void {
void getTransitKeys(this.props.baseMount)
.then((keys) => {
this.setState({
contentLoaded: true,
transitKeysList: keys,
});
})
.catch((_) => {
this.setState({
contentLoaded: true,
transitKeysList: [],
});
});
}
componentDidMount(): void {
this.getTransitKeys();
}
render(): JSX.Element {
if (!this.state.contentLoaded) {
return <p>{i18next.t("content_loading")}</p>;
}
if (this.state.transitKeysList.length == 0) {
return <p>{i18next.t("transit_view_none_here_text")}</p>;
}
return (
<ul class="uk-nav uk-nav-default">
{...this.state.transitKeysList.map((key) => (
<li>
<a
onClick={async () => {
route(transitViewSecretURL(this.props.baseMount, key));
}}
>
{key}
</a>
</li>
))}
</ul>
);
}
}
export class TransitView extends Component<DefaultPageProps, { caps: CapabilitiesType }> {
export class TransitView extends Component<DefaultPageProps, { transitKey: TransitKeyType }> {
async componentDidMount() {
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const caps = await getCapabilitiesPath([mountsPath, baseMount]);
this.setState({ caps });
const secretItem = this.props.matches["secretItem"];
const transitKey = await getTransitKey(baseMount, secretItem);
this.setState({ transitKey });
}
render() {
if (!this.state.caps) return;
if (!this.state.transitKey) return;
const baseMount = this.props.matches["baseMount"];
const mountsPath = "/sys/mounts/" + baseMount;
const mountCaps = this.state.caps[mountsPath];
const transitCaps = this.state.caps[baseMount];
const secretItem = this.props.matches["secretItem"];
const transitKey = this.state.transitKey;
return (
<>
<SecretTitleElement type="transit" baseMount={baseMount} />
<p>
{transitCaps.includes("create") && (
<button
class="uk-button uk-button-primary"
onClick={async () => {
route(transitNewSecretURL(baseMount));
}}
>
{i18next.t("transit_view_new_btn")}
</button>
<SecretTitleElement
type="transit"
baseMount={baseMount}
item={secretItem}
/>
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
{transitKey.supports_encryption && (
<Tile
title={i18next.t("transit_view_encrypt_text")}
description={i18next.t("transit_view_encrypt_description")}
icon="lock"
iconText={i18next.t("transit_view_encrypt_icon_text")}
onclick={async () => route(transitEncryptSecretURL(baseMount, secretItem))}
/>
)}
{mountCaps.includes("delete") && (
<button
class="uk-button uk-button-danger"
onClick={async () => {
route(delSecretsEngineURL(baseMount));
}}
>
{i18next.t("transit_view_delete_btn")}
</button>
{transitKey.supports_decryption && (
<Tile
title={i18next.t("transit_view_decrypt_text")}
description={i18next.t("transit_view_decrypt_description")}
icon="mail"
iconText={i18next.t("transit_view_decrypt_icon_text")}
onclick={() => route(transitDecryptSecretURL(baseMount, secretItem))}
/>
)}
</p>
<TransitViewListItem baseMount={baseMount} />
{transitKey.supports_decryption && (
<Tile
title={i18next.t("transit_view_rewrap_text")}
description={i18next.t("transit_view_rewrap_description")}
icon="code"
iconText={i18next.t("transit_view_rewrap_icon_text")}
onclick={async () => route(transitRewrapSecretURL(baseMount, secretItem))}
/>
)}
</Grid>
</>
);
}

View file

@ -1,57 +0,0 @@
import { Component, render } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { Grid, GridSizes } from "../../../elements/Grid";
import { Page } from "../../../../types/Page";
import { SecretTitleElement } from "../SecretTitleElement";
import { Tile } from "../../../elements/Tile";
import { TransitKeyType } from "../../../../api/types/transit";
import { getTransitKey } from "../../../../api/transit/getTransitKey";
import i18next from "i18next";
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 });
}
render() {
if (!this.state.transitKey) return;
const baseMount = this.props.matches["baseMount"];
const secretItem = this.props.matches["secretItem"];
const transitKey = this.state.transitKey;
return (
<Grid size={GridSizes.MATCHING_TWO_ROWS}>
{transitKey.supports_encryption && (
<Tile
title={i18next.t("transit_view_encrypt_text")}
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")*/}
/>
)}
{transitKey.supports_decryption && (
<Tile
title={i18next.t("transit_view_decrypt_text")}
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")*/}
/>
)}
{transitKey.supports_decryption && (
<Tile
title={i18next.t("transit_view_rewrap_text")}
description={i18next.t("transit_view_rewrap_description")}
icon="code"
iconText={i18next.t("transit_view_rewrap_icon_text")}
onclick={async () => {} /*await this.router.changePage("TRANSIT_REWRAP")*/}
/>
)}
</Grid>
);
}
}

View file

@ -7,7 +7,7 @@ export function delSecretsEngineURL(baseMount: string): string {
// Secrets / Key Value
export function kvNewURL(baseMount: string, secretPath?: string[]): string {
return `/secrets/kv/new/${baseMount}` + secretPath ? `/${secretPath.join("/")}` : "";
return `/secrets/kv/new/${baseMount}` + (secretPath ? `/${secretPath.join("/")}` : "");
}
export function kvDeleteURL(baseMount: string, secretPath: string[], secret: string): string {
@ -47,8 +47,24 @@ export function transitNewSecretURL(baseMount: string): string {
return `/secrets/transit/new/${baseMount}`;
}
export function transitListSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/list/${baseMount}/${secret}`;
}
export function transitViewSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/view/${baseMount}/${secret}`;
}
export function transitEncryptSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/encrypt/${baseMount}/${secret}`;
}
export function transitDecryptSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/decrypt/${baseMount}/${secret}`;
}
export function transitRewrapSecretURL(baseMount: string, secret: string): string {
return `/secrets/transit/rewrap/${baseMount}/${secret}`;
}
//