mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-12-27 15:54:50 +00:00
* UI: Implement overview page for KV v2 (#28162) * build json editor patch form * finish patch component and tests * add tab to each route * and path route * add overview tab to tests * update overview to use updated_time instead of created_time * redirect relevant secret.details to secret.index * compute secretState in component instead of pass as arg * add capabilities service * add error handling to fetchSubkeys adapter request * add overview tabs to test * add subtext to overview card * remaining redirects in secret edit * remove create new version from popup menu * fix breadcrumbs for overview * separate adding capabilities service * add service to kv engine * Revert "separate adding capabilities service" This reverts commit bb70b12ab7dbcde0fbd2d4d81768e5c8b1c420cc. * Revert "add service to kv engine" This reverts commit bfa880535ef7d529d7610936b2c1aae55673d23f. * update navigation test * consistently navigate to secret.index route to be explicit * finish overview navigation tests * add copyright header * update delete tests * fix nav testrs * cleanup secret edit redirects * remove redundant async/awaits * fix create test * edge case tests * secret acceptance tests * final component tests * rename kvSecretDetails external route to kvSecretOverview * add comment * UI: Add patch route and implement Page::Secret::Patch page component (sidebranch) (#28192) * add tab to each route * and path route * add overview tab to tests * update overview to use updated_time instead of created_time * redirect relevant secret.details to secret.index * compute secretState in component instead of pass as arg * add capabilities service * add error handling to fetchSubkeys adapter request * add patch route and put in page component * add patch secret action to subkeys card * fix component name * add patch capability * alphabetize computed capabilities * update links, cleanup selectors * fix more merge conflict stuff * add capabilities test * add models to patch link * add test for patch route * rename external route * add error templates * make notes about enterprise tests, filter one * remove errors, transition (redirect) instead * redirect patch routes * UI: Move fetching secret data to child route (#28198) * remove @secret from metadata details * use metadata model instead of secret in paths page * put delete back into kv/data adapter * grant access in control group test * update metadata route and permissions * remove secret from parent route, only fetch in details route * change more permissions to route perms, add tests * revert overview redirect from list view * wrap model in conditional for perms * remove redundant canReadCustomMetadata check * rename adapter method * handle overview 404 * remove comment * add customMetadata as an arg * update grantAccess in test * make version param easier to follow * VAULT-30494 handle 404 jira * refactor capabilities to return an object * update create tests * add test for default truthy capabilities * remove destroy-all-versions from kv/data adapter * UI: Add enterprise checks (#28215) * add enterprise check for subkey card * add max height and scroll to subkey card * only fetch subkeys if enterprise * remove check in overview * add test * Update ui/tests/integration/components/kv/page/kv-page-overview-test.js * fix test failures (#28222) * add assertion * add optional chaining * create/delete versioned secret in each module * wait for transition * add another waitUntil * UI: Add patch latest version to toolbar (#28223) * add patch latest version action to toolbar * make isPatchAllowed arg all encompassing * no longer need model check * use hash so both promises fire at the same time * add subkeys to policy * Update ui/lib/kv/addon/routes/secret.js * add changelog * small cleanup items! (#28229) * add conditional for enterprise checking tabs * cleanup fetchMultiplePaths method * add test * remove todo comment, ticket created and design wants to hold off * keep transition, update comments * cleanup tests, add index to breadcrumbs * add some test coverage * toggle so value is readable
167 lines
5.5 KiB
JavaScript
167 lines
5.5 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import ApplicationAdapter from '../application';
|
|
import { kvDataPath, kvDeletePath, kvDestroyPath, kvSubkeysPath, kvUndeletePath } from 'vault/utils/kv-path';
|
|
import { assert } from '@ember/debug';
|
|
import ControlGroupError from 'vault/lib/control-group-error';
|
|
|
|
export default class KvDataAdapter extends ApplicationAdapter {
|
|
namespace = 'v1';
|
|
|
|
_url(fullPath) {
|
|
return `${this.buildURL()}/${fullPath}`;
|
|
}
|
|
|
|
createRecord(store, type, snapshot) {
|
|
const { backend, path } = snapshot.record;
|
|
const url = this._url(kvDataPath(backend, path));
|
|
|
|
return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then((res) => {
|
|
return {
|
|
data: {
|
|
id: kvDataPath(backend, path, res.data.version),
|
|
backend,
|
|
path,
|
|
...res.data,
|
|
},
|
|
};
|
|
});
|
|
}
|
|
|
|
fetchSubkeys(backend, path, query) {
|
|
const url = this._url(kvSubkeysPath(backend, path, query));
|
|
return (
|
|
this.ajax(url, 'GET')
|
|
.then((resp) => resp.data)
|
|
// deleted/destroyed secret versions throw an error
|
|
// but still have metadata that we want to return
|
|
.catch((errorOrResponse) => {
|
|
return this.parseErrorOrResponse(errorOrResponse, { backend, path }, true);
|
|
})
|
|
);
|
|
}
|
|
|
|
fetchWrapInfo(query) {
|
|
const { backend, path, version, wrapTTL } = query;
|
|
const id = kvDataPath(backend, path, version);
|
|
return this.ajax(this._url(id), 'GET', { wrapTTL }).then((resp) => resp.wrap_info);
|
|
}
|
|
|
|
// patching a secret happens without retrieving the ember data model
|
|
// so we use a custom method instead of updateRecord
|
|
patchSecret(backend, path, patchData, version) {
|
|
const url = this._url(kvDataPath(backend, path));
|
|
const data = {
|
|
options: { cas: version },
|
|
data: patchData,
|
|
};
|
|
return this.ajax(url, 'PATCH', { data });
|
|
}
|
|
|
|
queryRecord(store, type, query) {
|
|
const { backend, path, version } = query;
|
|
// ID is the full path for the data (including version)
|
|
let id = kvDataPath(backend, path, version);
|
|
return this.ajax(this._url(id), 'GET')
|
|
.then((resp) => {
|
|
// if no version is queried, add version from response to ID
|
|
// otherwise duplicate ember data models will exist in store
|
|
// (one with an ID that includes the version and one without)
|
|
if (!version) {
|
|
id = kvDataPath(backend, path, resp.data.metadata.version);
|
|
}
|
|
return {
|
|
...resp,
|
|
data: {
|
|
id,
|
|
backend,
|
|
path,
|
|
...resp.data,
|
|
},
|
|
};
|
|
})
|
|
.catch((errorOrResponse) => {
|
|
return this.parseErrorOrResponse(errorOrResponse, { id, backend, path, version });
|
|
});
|
|
}
|
|
|
|
/* Five types of delete operations */
|
|
deleteRecord(store, type, snapshot) {
|
|
const { backend, path } = snapshot.record;
|
|
const { deleteType, deleteVersions } = snapshot.adapterOptions;
|
|
|
|
if (!backend || !path) {
|
|
throw new Error('The request to delete or undelete is missing required attributes.');
|
|
}
|
|
|
|
switch (deleteType) {
|
|
case 'delete-latest-version':
|
|
return this.ajax(this._url(kvDataPath(backend, path)), 'DELETE');
|
|
case 'delete-version':
|
|
return this.ajax(this._url(kvDeletePath(backend, path)), 'POST', {
|
|
data: { versions: deleteVersions },
|
|
});
|
|
case 'destroy':
|
|
return this.ajax(this._url(kvDestroyPath(backend, path)), 'PUT', {
|
|
data: { versions: deleteVersions },
|
|
});
|
|
case 'undelete':
|
|
return this.ajax(this._url(kvUndeletePath(backend, path)), 'POST', {
|
|
data: { versions: deleteVersions },
|
|
});
|
|
default:
|
|
assert('deleteType must be one of delete-latest-version, delete-version, destroy, or undelete.');
|
|
}
|
|
}
|
|
|
|
handleResponse(status, headers, payload, requestData) {
|
|
// after deleting a secret version, data is null and the API returns a 404
|
|
// but there could be relevant metadata
|
|
if (status === 404 && payload.data?.metadata) {
|
|
return super.handleResponse(200, headers, payload, requestData);
|
|
}
|
|
return super.handleResponse(...arguments);
|
|
}
|
|
|
|
parseErrorOrResponse(errorOrResponse, secretDataBaseResponse, isSubkeys = false) {
|
|
// if it's a legitimate error - throw it!
|
|
if (errorOrResponse instanceof ControlGroupError) {
|
|
throw errorOrResponse;
|
|
}
|
|
|
|
const errorCode = errorOrResponse.httpStatus;
|
|
if (errorCode === 403) {
|
|
return {
|
|
data: {
|
|
...secretDataBaseResponse,
|
|
fail_read_error_code: errorCode,
|
|
},
|
|
};
|
|
}
|
|
|
|
// in the case of a deleted/destroyed secret the API returns a 404 because { data: null }
|
|
// however, there could be a metadata block with important information like deletion_time
|
|
// handleResponse below checks 404 status codes for metadata and updates the code to 200 if it exists.
|
|
// we still end up in the good ol' catch() block, but instead of a 404 adapter error we've "caught"
|
|
// the metadata that sneakily tried to hide from us
|
|
if (errorOrResponse.data) {
|
|
// subkeys response doesn't correspond to a model, no need to include base response
|
|
if (isSubkeys) return errorOrResponse.data;
|
|
|
|
return {
|
|
...errorOrResponse,
|
|
data: {
|
|
...secretDataBaseResponse,
|
|
...errorOrResponse.data, // includes the { metadata } key we want
|
|
},
|
|
};
|
|
}
|
|
|
|
// If we get here, it's probably a 404 because it doesn't exist
|
|
throw errorOrResponse;
|
|
}
|
|
}
|