688 lines
19 KiB
TypeScript
688 lines
19 KiB
TypeScript
import { AuthListAPIType, AuthListType } from "./types/auth";
|
|
import { BaseAPIResponse } from "./types/api";
|
|
import { CapabilitiesType } from "./types/capabilities";
|
|
import {
|
|
DecryptionPayload,
|
|
DecryptionResult,
|
|
EncryptionPayload,
|
|
EncryptionResult,
|
|
RewrapPayload,
|
|
RewrapResult,
|
|
TransitKeyType,
|
|
} from "./types/transit";
|
|
import { DoesNotExistError } from "../types/internalErrors";
|
|
import { MountType, MountsType, NewMountParams } from "./types/mount";
|
|
import { NewTOTPData, NewTOTPResp } from "./types/totp";
|
|
import { SealStatusType } from "./types/seal";
|
|
import { SecretMetadataType } from "./types/secret";
|
|
import { Settings } from "../settings/Settings";
|
|
import { TokenInfo } from "./types/token";
|
|
import { UserType, UserTypeAPIResp } from "./types/user";
|
|
import { getObjectKeys, removeDoubleSlash } from "../utils";
|
|
|
|
async function checkResponse(resp: Response): Promise<void> {
|
|
if (resp.ok) return;
|
|
if (resp.status == 404) throw DoesNotExistError;
|
|
|
|
let json: BaseAPIResponse;
|
|
try {
|
|
json = (await resp.json()) as BaseAPIResponse;
|
|
} catch {
|
|
// Do Nothing
|
|
}
|
|
|
|
if (json?.errors?.length >= 1) {
|
|
throw new Error(json.errors[0]);
|
|
}
|
|
}
|
|
|
|
export class API {
|
|
private settings: Settings;
|
|
|
|
constructor(settings: Settings) {
|
|
this.settings = settings;
|
|
}
|
|
|
|
getHeaders(): Record<string, string> {
|
|
return {
|
|
"X-Vault-Token": this.settings.token,
|
|
};
|
|
}
|
|
appendAPIURL(url: string): string {
|
|
return this.settings.apiURL + url;
|
|
}
|
|
|
|
// List all supported auth methods
|
|
async listAuth(): Promise<AuthListType> {
|
|
const request = new Request(this.appendAPIURL(`/v1/sys/auth`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as AuthListAPIType;
|
|
return data.data;
|
|
}
|
|
|
|
// Tries to login with username and password, returns token
|
|
async usernameLogin(username: string, password: string): Promise<string> {
|
|
const request = new Request(this.appendAPIURL(`/v1/auth/userpass/login/${username}`), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ username: username, password: password }),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as {
|
|
auth: { client_token: string };
|
|
};
|
|
return data.auth.client_token;
|
|
}
|
|
|
|
async createOrUpdateUserPassUser(
|
|
path: string,
|
|
username: string,
|
|
data: Partial<UserType>,
|
|
): Promise<void> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/auth/${path}/users/${username}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(data, null, 0),
|
|
},
|
|
);
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async deleteUserPassUser(path: string, username: string): Promise<void> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/auth/${path}/users/${username}`)),
|
|
{
|
|
method: "DELETE",
|
|
headers: this.getHeaders(),
|
|
},
|
|
);
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getUserPassUser(path: string, username: string): Promise<UserType> {
|
|
const request = new Request(this.appendAPIURL(`/v1/auth/${path}/users/${username}`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as UserTypeAPIResp;
|
|
return data.data;
|
|
}
|
|
|
|
async listUserPassUsers(path: string): Promise<string[]> {
|
|
const request = new Request(this.appendAPIURL(`/v1/auth/${path}/users?list=true`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { keys: string[] } };
|
|
return data.data.keys;
|
|
}
|
|
|
|
async getMount(mountName: string): Promise<MountType> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/internal/ui/mounts/" + mountName), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: MountType };
|
|
return data.data;
|
|
}
|
|
|
|
async getMounts(): Promise<MountsType> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/internal/ui/mounts"), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { secret: MountsType } };
|
|
return data.data.secret;
|
|
}
|
|
|
|
async newMount(parms: NewMountParams): Promise<void> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/sys/mounts/${parms.name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(parms),
|
|
},
|
|
);
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async deleteMount(mountPath: string): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/mounts/" + mountPath), {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getCapabilitiesPath(path: string | string[]): Promise<CapabilitiesType> {
|
|
if (!Array.isArray(path)) {
|
|
path = [path];
|
|
}
|
|
|
|
const request = new Request(this.appendAPIURL("/v1/sys/capabilities-self"), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify({
|
|
paths: path,
|
|
}),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { capabilities: string[] };
|
|
return data;
|
|
}
|
|
|
|
async getCapsPath(path: string | string[]): Promise<string[]> {
|
|
return (await this.getCapabilitiesPath(path)).capabilities;
|
|
}
|
|
|
|
async getCapabilities(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
): Promise<CapabilitiesType> {
|
|
return await this.getCapabilitiesPath(
|
|
removeDoubleSlash(baseMount + secretPath.join("/") + "/" + name),
|
|
);
|
|
}
|
|
|
|
async sealVault(): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/seal"), {
|
|
method: "PUT",
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async submitUnsealKey(key: string): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/unseal"), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
key: key,
|
|
}),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getSealStatus(): Promise<SealStatusType> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/seal-status"));
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as SealStatusType;
|
|
return data;
|
|
}
|
|
|
|
async lookupSelf(): Promise<TokenInfo> {
|
|
const request = new Request(this.appendAPIURL("/v1/auth/token/lookup-self"), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: TokenInfo };
|
|
return data.data;
|
|
}
|
|
|
|
async renewSelf(): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/auth/token/renew-self"), {
|
|
method: "POST",
|
|
headers: {
|
|
...this.getHeaders(),
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({}),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async createOrUpdatePolicy(name: string, policy_data: string): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/policies/acl/" + name), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify({ policy: policy_data }, null, 0),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async deletePolicy(name: string): Promise<void> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/policies/acl/" + name), {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getPolicies(): Promise<string[]> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/policies/acl?list=true"), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { keys: string[] } };
|
|
return data.data.keys;
|
|
}
|
|
|
|
async getPolicy(name: string): Promise<string> {
|
|
const request = new Request(this.appendAPIURL("/v1/sys/policies/acl/" + name), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { policy: string } };
|
|
return data.data.policy;
|
|
}
|
|
|
|
async createOrUpdateSecret(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
data: Record<string, unknown>,
|
|
): Promise<void> {
|
|
let secretURL = "";
|
|
let APIData = {};
|
|
|
|
const mountInfo = await this.getMount(baseMount);
|
|
if (mountInfo.options.version == "2") {
|
|
secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
|
|
APIData = { data: data };
|
|
} else {
|
|
secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
|
|
APIData = data;
|
|
}
|
|
|
|
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
|
|
const request = new Request(this.appendAPIURL(secretURL), {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(APIData, null, 0),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async deleteSecret(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
version = "null",
|
|
): Promise<void> {
|
|
let secretURL = "";
|
|
|
|
let request: Request;
|
|
|
|
const mountInfo = await this.getMount(baseMount);
|
|
const mountVersion = mountInfo.options.version;
|
|
|
|
if (mountVersion == "2" && version != "null") {
|
|
secretURL = `/v1/${baseMount}/delete/${secretPath.join("/")}/${name}`;
|
|
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
|
|
request = new Request(this.appendAPIURL(secretURL), {
|
|
method: "POST",
|
|
headers: {
|
|
...this.getHeaders(),
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ versions: [version] }),
|
|
});
|
|
} else {
|
|
if (mountVersion == "2") {
|
|
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("/")}/${name}`;
|
|
} else {
|
|
secretURL = `/v1/${baseMount}/${secretPath.join("/")}/${name}`;
|
|
}
|
|
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
|
|
request = new Request(this.appendAPIURL(secretURL), {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(),
|
|
});
|
|
}
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async undeleteSecret(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
version = "null",
|
|
): Promise<void> {
|
|
let secretURL = `/v1/${baseMount}/undelete/${secretPath.join("/")}/${name}`;
|
|
secretURL = removeDoubleSlash(secretURL).replace(/\/$/, "");
|
|
if (version == "null") {
|
|
const meta = await this.getSecretMetadata(baseMount, secretPath, name);
|
|
const versions = getObjectKeys(meta.versions);
|
|
version = String(versions[versions.length - 1]);
|
|
}
|
|
|
|
const request = new Request(this.appendAPIURL(secretURL), {
|
|
method: "POST",
|
|
headers: {
|
|
...this.getHeaders(),
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ versions: [version] }),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getSecretKV1(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
): Promise<Record<string, unknown>> {
|
|
const request = new Request(
|
|
this.appendAPIURL(`/v1/${baseMount}/${secretPath.join("/")}/${name}`),
|
|
{
|
|
headers: this.getHeaders(),
|
|
},
|
|
);
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as unknown;
|
|
|
|
return (data as { data: Record<string, unknown> }).data;
|
|
}
|
|
|
|
async getSecretKV2(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
version = "null",
|
|
): Promise<Record<string, unknown>> {
|
|
let secretURL = "";
|
|
|
|
secretURL = `/v1/${baseMount}/data/${secretPath.join("/")}/${name}`;
|
|
if (version != "null") secretURL += `?version=${version}`;
|
|
|
|
const request = new Request(this.appendAPIURL(secretURL), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as unknown;
|
|
return (data as { data: { data: Record<string, unknown> } }).data.data;
|
|
}
|
|
|
|
async getSecret(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
version = "null",
|
|
): Promise<Record<string, unknown>> {
|
|
const mountInfo = await this.getMount(baseMount);
|
|
if (mountInfo.options.version == "2") {
|
|
return await this.getSecretKV2(baseMount, secretPath, name, version);
|
|
} else {
|
|
return await this.getSecretKV1(baseMount, secretPath, name);
|
|
}
|
|
}
|
|
|
|
async getSecretMetadata(
|
|
baseMount: string,
|
|
secretPath: string[],
|
|
name: string,
|
|
): Promise<SecretMetadataType> {
|
|
const request = new Request(
|
|
this.appendAPIURL(`/v1/${baseMount}/metadata/${secretPath.join("")}/${name}`),
|
|
{
|
|
headers: this.getHeaders(),
|
|
},
|
|
);
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: SecretMetadataType };
|
|
return data.data;
|
|
}
|
|
|
|
async getSecrets(baseMount: string, secretPath: string[]): Promise<string[]> {
|
|
let secretURL = "";
|
|
|
|
const mountInfo = await this.getMount(baseMount);
|
|
if (mountInfo.options.version == "2") {
|
|
secretURL = `/v1/${baseMount}/metadata/${secretPath.join("/")}?list=true`;
|
|
} else {
|
|
secretURL = `/v1/${baseMount}/${secretPath.join("/")}?list=true`;
|
|
}
|
|
const request = new Request(this.appendAPIURL(secretURL), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { keys: string[] } };
|
|
return data.data.keys;
|
|
}
|
|
|
|
async addNewTOTP(baseMount: string, parms: NewTOTPData): Promise<NewTOTPResp> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(parms),
|
|
},
|
|
);
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
const data = (await resp.json()) as { data: NewTOTPResp };
|
|
return data.data;
|
|
}
|
|
|
|
async deleteTOTP(baseMount: string, name: string): Promise<void> {
|
|
const request = new Request(this.appendAPIURL(`/v1/${baseMount}/keys/${name}`), {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(),
|
|
});
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async getTOTPCode(baseMount: string, name: string): Promise<string> {
|
|
const request = new Request(this.appendAPIURL(`/v1/${baseMount}/code/${name}`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { code: string } };
|
|
return data.data.code;
|
|
}
|
|
|
|
async getTOTPKeys(baseMount: string): Promise<string[]> {
|
|
const request = new Request(this.appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { keys: string[] } };
|
|
return data.data.keys;
|
|
}
|
|
|
|
async getTransitKey(baseMount: string, name: string): Promise<TransitKeyType> {
|
|
const request = new Request(this.appendAPIURL(`/v1/${baseMount}/keys/${name}`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: TransitKeyType };
|
|
return data.data;
|
|
}
|
|
|
|
async getTransitKeys(baseMount: string): Promise<string[]> {
|
|
const request = new Request(this.appendAPIURL(`/v1/${baseMount}/keys?list=true`), {
|
|
headers: this.getHeaders(),
|
|
});
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as { data: { keys: string[] } };
|
|
return data.data.keys;
|
|
}
|
|
|
|
async newTransitKey(baseMount: string, parms: { name: string; type: string }): Promise<void> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/keys/${parms.name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(parms),
|
|
},
|
|
);
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
}
|
|
|
|
async transitDecrypt(
|
|
baseMount: string,
|
|
name: string,
|
|
payload: DecryptionPayload,
|
|
): Promise<DecryptionResult> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/decrypt/${name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(payload),
|
|
},
|
|
);
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as {
|
|
data?: DecryptionResult;
|
|
};
|
|
|
|
return data.data;
|
|
}
|
|
|
|
async transitEncrypt(
|
|
baseMount: string,
|
|
name: string,
|
|
payload: EncryptionPayload,
|
|
): Promise<EncryptionResult> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/encrypt/${name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(payload),
|
|
},
|
|
);
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as {
|
|
data?: EncryptionResult;
|
|
};
|
|
|
|
return data.data;
|
|
}
|
|
|
|
async transitRewrap(
|
|
baseMount: string,
|
|
name: string,
|
|
payload: RewrapPayload,
|
|
): Promise<RewrapResult> {
|
|
const request = new Request(
|
|
this.appendAPIURL(removeDoubleSlash(`/v1/${baseMount}/rewrap/${name}`)),
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...this.getHeaders(),
|
|
},
|
|
body: JSON.stringify(payload),
|
|
},
|
|
);
|
|
|
|
const resp = await fetch(request);
|
|
await checkResponse(resp);
|
|
|
|
const data = (await resp.json()) as {
|
|
data?: RewrapResult;
|
|
};
|
|
|
|
return data.data;
|
|
}
|
|
}
|