Merge pull request #12080 from ivangonzalezacuna/feature/vault-plugin-improvements
[Vault Plugin] Add requested improvements
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-vault': patch
|
||||
---
|
||||
|
||||
Export missing parameters and added them to the api-report. Also adapted the API to the expected response from the backend
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-vault-backend': minor
|
||||
---
|
||||
|
||||
Throw exceptions instead of swallow them, remove some exported types from the `api-report`, small changes in the API responses & expose the vault `baseUrl` to the frontend as well
|
||||
@@ -12,13 +12,6 @@ import { TaskRunner } from '@backstage/backend-tasks';
|
||||
// @public
|
||||
export function createRouter(options: RouterOptions): express.Router;
|
||||
|
||||
// @public
|
||||
export type RenewTokenResponse = {
|
||||
auth: {
|
||||
client_token: string;
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface RouterOptions {
|
||||
// (undocumented)
|
||||
@@ -33,7 +26,7 @@ export interface RouterOptions {
|
||||
export interface VaultApi {
|
||||
getFrontendSecretsUrl(): string;
|
||||
listSecrets(secretPath: string): Promise<VaultSecret[]>;
|
||||
renewToken?(): Promise<boolean>;
|
||||
renewToken?(): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -45,7 +38,6 @@ export class VaultBuilder {
|
||||
enableTokenRenew(schedule?: TaskRunner): Promise<this>;
|
||||
// (undocumented)
|
||||
protected readonly env: VaultEnvironment;
|
||||
protected renewToken(vaultClient: VaultClient): Promise<void>;
|
||||
setVaultClient(vaultClient: VaultClient): this;
|
||||
}
|
||||
|
||||
@@ -62,7 +54,7 @@ export class VaultClient implements VaultApi {
|
||||
// (undocumented)
|
||||
listSecrets(secretPath: string): Promise<VaultSecret[]>;
|
||||
// (undocumented)
|
||||
renewToken(): Promise<boolean>;
|
||||
renewToken(): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -82,12 +74,5 @@ export type VaultSecret = {
|
||||
editUrl: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type VaultSecretList = {
|
||||
data: {
|
||||
keys: string[];
|
||||
};
|
||||
};
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
Vendored
+1
@@ -19,6 +19,7 @@ export interface Config {
|
||||
vault?: {
|
||||
/**
|
||||
* The baseUrl for your Vault instance.
|
||||
* @visibility frontend
|
||||
*/
|
||||
baseUrl: string;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"helmet": "^5.0.2",
|
||||
"p-limit": "^3.1.0",
|
||||
"winston": "^3.7.2",
|
||||
"yn": "^5.0.0"
|
||||
},
|
||||
@@ -54,7 +55,7 @@
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"msw": "^0.42.0",
|
||||
"supertest": "^4.0.2"
|
||||
"supertest": "^6.1.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -45,9 +45,6 @@ export type VaultBuilderReturn = {
|
||||
* @public
|
||||
*/
|
||||
export class VaultBuilder {
|
||||
// private vaultTokenRefreshInterval: Duration = Duration.fromObject({
|
||||
// minutes: 60,
|
||||
// });
|
||||
private vaultClient?: VaultClient;
|
||||
|
||||
/**
|
||||
@@ -118,26 +115,12 @@ export class VaultBuilder {
|
||||
fn: async () => {
|
||||
this.env.logger.info('Renewing Vault token');
|
||||
const vaultClient = this.vaultClient ?? new VaultClient(this.env);
|
||||
await this.renewToken(vaultClient);
|
||||
await vaultClient.renewToken();
|
||||
},
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renews the token for vault using a defined client.
|
||||
*
|
||||
* @param vaultClient - The vault client used to renew the token
|
||||
*/
|
||||
protected async renewToken(vaultClient: VaultClient) {
|
||||
const result = await vaultClient.renewToken();
|
||||
if (!result) {
|
||||
this.env.logger.warn('Error renewing vault token');
|
||||
} else {
|
||||
this.env.logger.info('Vault token renewed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the backend routes for Vault.
|
||||
*
|
||||
@@ -159,7 +142,7 @@ export class VaultBuilder {
|
||||
}
|
||||
|
||||
const secrets = await vaultClient.listSecrets(path);
|
||||
res.json(secrets);
|
||||
res.json({ items: secrets });
|
||||
});
|
||||
|
||||
return router;
|
||||
|
||||
@@ -93,8 +93,7 @@ describe('VaultApi', () => {
|
||||
it('should return success token renew', async () => {
|
||||
setupHandlers();
|
||||
const api = new VaultClient({ config });
|
||||
const apiRenew = await api.renewToken();
|
||||
expect(apiRenew).toBeTruthy();
|
||||
expect(await api.renewToken()).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should render frontend url', () => {
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import fetch from 'cross-fetch';
|
||||
import plimit from 'p-limit';
|
||||
import { getVaultConfig, VaultConfig } from '../config';
|
||||
|
||||
/**
|
||||
* Object received as a response from the Vault API when fetching secrets
|
||||
* @public
|
||||
* @internal
|
||||
*/
|
||||
export type VaultSecretList = {
|
||||
data: {
|
||||
@@ -40,9 +42,8 @@ export type VaultSecret = {
|
||||
|
||||
/**
|
||||
* Object received as response when the token is renewed using the Vault API
|
||||
* @public
|
||||
*/
|
||||
export type RenewTokenResponse = {
|
||||
type RenewTokenResponse = {
|
||||
auth: {
|
||||
client_token: string;
|
||||
};
|
||||
@@ -63,10 +64,10 @@ export interface VaultApi {
|
||||
*/
|
||||
listSecrets(secretPath: string): Promise<VaultSecret[]>;
|
||||
/**
|
||||
* Optional, to renew the token used to list the secrets. Returns true
|
||||
* if the action was successfull, false otherwise.
|
||||
* Optional, to renew the token used to list the secrets. Throws an
|
||||
* error if the token renewal went wrong.
|
||||
*/
|
||||
renewToken?(): Promise<boolean>;
|
||||
renewToken?(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +76,7 @@ export interface VaultApi {
|
||||
*/
|
||||
export class VaultClient implements VaultApi {
|
||||
private vaultConfig: VaultConfig;
|
||||
private readonly limit = plimit(5);
|
||||
|
||||
constructor({ config }: { config: Config }) {
|
||||
this.vaultConfig = getVaultConfig(config);
|
||||
@@ -84,7 +86,7 @@ export class VaultClient implements VaultApi {
|
||||
path: string,
|
||||
query: { [key in string]: any },
|
||||
method: string = 'GET',
|
||||
): Promise<T | undefined> {
|
||||
): Promise<T> {
|
||||
const url = new URL(path, this.vaultConfig.baseUrl);
|
||||
const response = await fetch(
|
||||
`${url.toString()}?${new URLSearchParams(query).toString()}`,
|
||||
@@ -96,15 +98,14 @@ export class VaultClient implements VaultApi {
|
||||
},
|
||||
},
|
||||
);
|
||||
if (response.status === 200) {
|
||||
if (response.ok) {
|
||||
return (await response.json()) as T;
|
||||
} else if (response.status === 404) {
|
||||
throw new NotFoundError(`No secrets found in path '${path}'`);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private isFolder(secretName: string): boolean {
|
||||
const regex = /^.*\/$/gm;
|
||||
return regex.test(secretName);
|
||||
throw new Error(
|
||||
`Unexpected error while fetching secrets from path '${path}'`,
|
||||
);
|
||||
}
|
||||
|
||||
getFrontendSecretsUrl(): string {
|
||||
@@ -116,17 +117,19 @@ export class VaultClient implements VaultApi {
|
||||
this.vaultConfig.kvVersion === 2
|
||||
? `v1/${this.vaultConfig.secretEngine}/metadata/${secretPath}`
|
||||
: `v1/${this.vaultConfig.secretEngine}/${secretPath}`;
|
||||
const result = await this.callApi<VaultSecretList>(listUrl, { list: true });
|
||||
if (!result) {
|
||||
return [];
|
||||
}
|
||||
const result = await this.limit(() =>
|
||||
this.callApi<VaultSecretList>(listUrl, { list: true }),
|
||||
);
|
||||
|
||||
const secrets: VaultSecret[] = [];
|
||||
|
||||
await Promise.all(
|
||||
result.data.keys.map(async secret => {
|
||||
if (this.isFolder(secret)) {
|
||||
if (secret.endsWith('/')) {
|
||||
secrets.push(
|
||||
...(await this.listSecrets(`${secretPath}/${secret.slice(0, -1)}`)),
|
||||
...(await this.limit(() =>
|
||||
this.listSecrets(`${secretPath}/${secret.slice(0, -1)}`),
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
secrets.push({
|
||||
@@ -141,17 +144,13 @@ export class VaultClient implements VaultApi {
|
||||
return secrets;
|
||||
}
|
||||
|
||||
async renewToken(): Promise<boolean> {
|
||||
async renewToken(): Promise<void> {
|
||||
const result = await this.callApi<RenewTokenResponse>(
|
||||
'v1/auth/token/renew-self',
|
||||
{},
|
||||
'POST',
|
||||
);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.vaultConfig.token = result.auth.client_token;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,36 @@
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
// @public
|
||||
export const EntityVaultCard: () => JSX.Element;
|
||||
|
||||
// @public
|
||||
export function isVaultAvailable(entity: Entity): boolean;
|
||||
|
||||
// @public (undocumented)
|
||||
export const VAULT_SECRET_PATH_ANNOTATION = 'vault.io/secrets-path';
|
||||
|
||||
// @public
|
||||
export interface VaultApi {
|
||||
listSecrets(secretPath: string): Promise<VaultSecret[]>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export const vaultApiRef: ApiRef<VaultApi>;
|
||||
|
||||
// @public
|
||||
export const vaultPlugin: BackstagePlugin<{}, {}>;
|
||||
|
||||
// @public
|
||||
export type VaultSecret = {
|
||||
name: string;
|
||||
showUrl: string;
|
||||
editUrl: string;
|
||||
};
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@backstage/catalog-model": "^1.0.3",
|
||||
"@backstage/core-components": "^0.9.5",
|
||||
"@backstage/core-plugin-api": "^1.0.3",
|
||||
"@backstage/errors": "^1.0.0",
|
||||
"@backstage/plugin-catalog-react": "^1.1.1",
|
||||
"@backstage/theme": "^0.2.15",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
|
||||
@@ -27,18 +27,20 @@ describe('api', () => {
|
||||
const mockBaseUrl = 'https://api-vault.com/api/vault';
|
||||
const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl);
|
||||
|
||||
const mockSecretsResult: VaultSecret[] = [
|
||||
{
|
||||
name: 'secret::one',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`,
|
||||
},
|
||||
{
|
||||
name: 'secret::two',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`,
|
||||
},
|
||||
];
|
||||
const mockSecretsResult: { items: VaultSecret[] } = {
|
||||
items: [
|
||||
{
|
||||
name: 'secret::one',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`,
|
||||
},
|
||||
{
|
||||
name: 'secret::two',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const setupHandlers = () => {
|
||||
server.use(
|
||||
@@ -47,31 +49,33 @@ describe('api', () => {
|
||||
if (path === 'test/success') {
|
||||
return res(ctx.json(mockSecretsResult));
|
||||
} else if (path === 'test/error') {
|
||||
return res(ctx.json([]));
|
||||
return res(ctx.json({ items: [] }));
|
||||
}
|
||||
return res(ctx.status(400));
|
||||
}),
|
||||
rest.get(`${mockBaseUrl}/v1/secrets/`, (_req, res, ctx) => {
|
||||
return res(ctx.json(mockSecretsResult));
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
it('should return secrets', async () => {
|
||||
setupHandlers();
|
||||
const api = new VaultClient({ discoveryApi });
|
||||
const secrets = await api.listSecrets('test/success');
|
||||
expect(secrets).toEqual(mockSecretsResult);
|
||||
expect(await api.listSecrets('test/success')).toEqual(
|
||||
mockSecretsResult.items,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return empty secret list', async () => {
|
||||
setupHandlers();
|
||||
const api = new VaultClient({ discoveryApi });
|
||||
expect(await api.listSecrets('test/error')).toEqual([]);
|
||||
expect(await api.listSecrets('')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return no secrets', async () => {
|
||||
it('should return all the secrets if no path defined', async () => {
|
||||
setupHandlers();
|
||||
const api = new VaultClient({ discoveryApi });
|
||||
const secrets = await api.listSecrets('');
|
||||
expect(secrets).toEqual([]);
|
||||
expect(await api.listSecrets('')).toEqual(mockSecretsResult.items);
|
||||
});
|
||||
});
|
||||
|
||||
+29
-11
@@ -14,21 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DiscoveryApi, createApiRef } from '@backstage/core-plugin-api';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const vaultApiRef = createApiRef<VaultApi>({
|
||||
id: 'plugin.vault.service',
|
||||
});
|
||||
|
||||
/**
|
||||
* Object containing the secret name and some links.
|
||||
* @public
|
||||
*/
|
||||
export type VaultSecret = {
|
||||
name: string;
|
||||
showUrl: string;
|
||||
editUrl: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for the VaultApi.
|
||||
* @public
|
||||
*/
|
||||
export interface VaultApi {
|
||||
/**
|
||||
* Returns a list of secrets used to show in a table.
|
||||
* @param secretPath - The path where the secrets are stored in Vault
|
||||
*/
|
||||
listSecrets(secretPath: string): Promise<VaultSecret[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of the VaultApi.
|
||||
* @public
|
||||
*/
|
||||
export class VaultClient implements VaultApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
|
||||
@@ -39,7 +59,7 @@ export class VaultClient implements VaultApi {
|
||||
private async callApi<T>(
|
||||
path: string,
|
||||
query: { [key in string]: any },
|
||||
): Promise<T | undefined> {
|
||||
): Promise<T> {
|
||||
const apiUrl = `${await this.discoveryApi.getBaseUrl('vault')}`;
|
||||
const response = await fetch(
|
||||
`${apiUrl}/${path}?${new URLSearchParams(query).toString()}`,
|
||||
@@ -49,23 +69,21 @@ export class VaultClient implements VaultApi {
|
||||
},
|
||||
},
|
||||
);
|
||||
if (response.status === 200) {
|
||||
if (response.ok) {
|
||||
return (await response.json()) as T;
|
||||
} else if (response.status === 404) {
|
||||
throw new NotFoundError(`No secrets found in path '${path}'`);
|
||||
}
|
||||
return undefined;
|
||||
throw new Error(
|
||||
`Unexpected error while fetching secrets from path '${path}'`,
|
||||
);
|
||||
}
|
||||
|
||||
async listSecrets(secretPath: string): Promise<VaultSecret[]> {
|
||||
if (secretPath === '') {
|
||||
return [];
|
||||
}
|
||||
const result = await this.callApi<VaultSecret[]>(
|
||||
const result = await this.callApi<{ items: VaultSecret[] }>(
|
||||
`v1/secrets/${encodeURIComponent(secretPath)}`,
|
||||
{},
|
||||
);
|
||||
if (!result) {
|
||||
return [];
|
||||
}
|
||||
return result;
|
||||
return result.items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,18 +68,20 @@ describe('EntityVaultTable', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const mockSecretsResult: VaultSecret[] = [
|
||||
{
|
||||
name: 'secret::one',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`,
|
||||
},
|
||||
{
|
||||
name: 'secret::two',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`,
|
||||
},
|
||||
];
|
||||
const mockSecretsResult: { items: VaultSecret[] } = {
|
||||
items: [
|
||||
{
|
||||
name: 'secret::one',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`,
|
||||
},
|
||||
{
|
||||
name: 'secret::two',
|
||||
editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`,
|
||||
showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const setupHandlers = () => {
|
||||
server.use(
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Table, TableColumn } from '@backstage/core-components';
|
||||
import { Link, Table, TableColumn } from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { Box, Typography } from '@material-ui/core';
|
||||
import Edit from '@material-ui/icons/Edit';
|
||||
@@ -27,7 +27,7 @@ import { VAULT_SECRET_PATH_ANNOTATION } from '../../constants';
|
||||
|
||||
export const vaultSecretPath = (entity: Entity) => {
|
||||
const secretPath =
|
||||
entity.metadata.annotations?.[VAULT_SECRET_PATH_ANNOTATION] ?? '';
|
||||
entity.metadata.annotations?.[VAULT_SECRET_PATH_ANNOTATION];
|
||||
|
||||
return { secretPath };
|
||||
};
|
||||
@@ -35,6 +35,11 @@ export const vaultSecretPath = (entity: Entity) => {
|
||||
export const EntityVaultTable = ({ entity }: { entity: Entity }) => {
|
||||
const vaultApi = useApi(vaultApiRef);
|
||||
const { secretPath } = vaultSecretPath(entity);
|
||||
if (!secretPath) {
|
||||
throw Error(
|
||||
`The secret path is undefined. Please, define the annotation ${VAULT_SECRET_PATH_ANNOTATION}`,
|
||||
);
|
||||
}
|
||||
const { value, loading, error } = useAsync(async (): Promise<
|
||||
VaultSecret[]
|
||||
> => {
|
||||
@@ -51,22 +56,22 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => {
|
||||
return {
|
||||
secret: secret.name,
|
||||
view: (
|
||||
<a
|
||||
<Link
|
||||
aria-label="View"
|
||||
title={`View ${secret.name}`}
|
||||
href={secret.showUrl}
|
||||
to={secret.showUrl}
|
||||
>
|
||||
<Visibility style={{ fontSize: 16 }} />
|
||||
</a>
|
||||
</Link>
|
||||
),
|
||||
edit: (
|
||||
<a
|
||||
<Link
|
||||
aria-label="Edit"
|
||||
title={`Edit ${secret.name}`}
|
||||
href={secret.editUrl}
|
||||
to={secret.editUrl}
|
||||
>
|
||||
<Edit style={{ fontSize: 16 }} />
|
||||
</a>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { VAULT_SECRET_PATH_ANNOTATION } from './constants';
|
||||
|
||||
/**
|
||||
* Checks if an entity contains the annotation for Vault.
|
||||
* @param entity - The entity to check if the annotation exists
|
||||
* @returns If the annotation exists or not
|
||||
* @public
|
||||
*/
|
||||
export function isVaultAvailable(entity: Entity): boolean {
|
||||
return Boolean(
|
||||
entity.metadata.annotations?.hasOwnProperty(VAULT_SECRET_PATH_ANNOTATION),
|
||||
|
||||
@@ -13,4 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const VAULT_SECRET_PATH_ANNOTATION = 'vault.io/secrets-path';
|
||||
|
||||
@@ -14,3 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { vaultPlugin, EntityVaultCard } from './plugin';
|
||||
export { isVaultAvailable } from './conditions';
|
||||
export { VAULT_SECRET_PATH_ANNOTATION } from './constants';
|
||||
export { vaultApiRef } from './api';
|
||||
export type { VaultApi, VaultSecret } from './api';
|
||||
|
||||
@@ -9718,7 +9718,7 @@ component-emitter@1.2.1:
|
||||
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||
integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=
|
||||
|
||||
component-emitter@^1.2.0, component-emitter@^1.2.1, component-emitter@^1.3.0, component-emitter@~1.3.0:
|
||||
component-emitter@^1.2.1, component-emitter@^1.3.0, component-emitter@~1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
@@ -10032,7 +10032,7 @@ cookie@0.5.0, cookie@~0.5.0:
|
||||
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookiejar@^2.1.0, cookiejar@^2.1.3:
|
||||
cookiejar@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
||||
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
|
||||
@@ -13048,7 +13048,7 @@ form-data-encoder@^1.4.3, form-data-encoder@^1.7.1:
|
||||
resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96"
|
||||
integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==
|
||||
|
||||
form-data@^2.3.1, form-data@^2.3.2, form-data@^2.5.0:
|
||||
form-data@^2.3.2, form-data@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
|
||||
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
|
||||
@@ -13105,11 +13105,6 @@ formdata-node@^4.3.1:
|
||||
node-domexception "1.0.0"
|
||||
web-streams-polyfill "4.0.0-beta.1"
|
||||
|
||||
formidable@^1.2.0:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
|
||||
integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==
|
||||
|
||||
formidable@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff"
|
||||
@@ -17943,7 +17938,7 @@ meros@^1.1.4:
|
||||
resolved "https://registry.npmjs.org/meros/-/meros-1.1.4.tgz#c17994d3133db8b23807f62bec7f0cb276cfd948"
|
||||
integrity sha512-E9ZXfK9iQfG9s73ars9qvvvbSIkJZF5yOo9j4tcwM5tN8mUKfj/EKN5PzOr3ZH0y5wL7dLAHw3RVEfpQV9Q7VQ==
|
||||
|
||||
methods@^1.0.0, methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
|
||||
methods@^1.0.0, methods@^1.1.2, methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
@@ -18282,7 +18277,7 @@ mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, m
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
mime@1.6.0, mime@^1.3.4, mime@^1.4.1:
|
||||
mime@1.6.0, mime@^1.3.4:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
@@ -21137,7 +21132,7 @@ q@^1.5.1:
|
||||
resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
|
||||
|
||||
qs@6.10.3, qs@^6.10.1, qs@^6.10.2, qs@^6.10.3, qs@^6.5.1, qs@^6.9.1, qs@^6.9.4, qs@^6.9.6:
|
||||
qs@6.10.3, qs@^6.10.1, qs@^6.10.2, qs@^6.10.3, qs@^6.9.1, qs@^6.9.4, qs@^6.9.6:
|
||||
version "6.10.3"
|
||||
resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
|
||||
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
|
||||
@@ -23963,22 +23958,6 @@ sucrase@^3.18.0, sucrase@^3.20.2:
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
superagent@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
|
||||
integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==
|
||||
dependencies:
|
||||
component-emitter "^1.2.0"
|
||||
cookiejar "^2.1.0"
|
||||
debug "^3.1.0"
|
||||
extend "^3.0.0"
|
||||
form-data "^2.3.1"
|
||||
formidable "^1.2.0"
|
||||
methods "^1.1.1"
|
||||
mime "^1.4.1"
|
||||
qs "^6.5.1"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
superagent@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.npmjs.org/superagent/-/superagent-7.1.3.tgz#783ff8330e7c2dad6ad8f0095edc772999273b6b"
|
||||
@@ -23996,14 +23975,6 @@ superagent@^7.1.3:
|
||||
readable-stream "^3.6.0"
|
||||
semver "^7.3.7"
|
||||
|
||||
supertest@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36"
|
||||
integrity sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==
|
||||
dependencies:
|
||||
methods "^1.1.2"
|
||||
superagent "^3.8.3"
|
||||
|
||||
supertest@^6.1.3, supertest@^6.1.6:
|
||||
version "6.2.3"
|
||||
resolved "https://registry.npmjs.org/supertest/-/supertest-6.2.3.tgz#291b220126e5faa654d12abe1ada3658757c8c67"
|
||||
|
||||
Reference in New Issue
Block a user