1
0
Fork 0
VaultUI/src/ui/pages/Secrets/KeyValue/KeyValueView.tsx
2022-01-06 23:02:34 +00:00

211 lines
5.8 KiB
TypeScript

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 KeyValueViewState = {
pathCaps: string[];
mountCaps: string[];
mountType: string;
};
export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState> {
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={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} />
</>
);
}
get name(): string {
return i18next.t("kv_view_title");
}
}