mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-22 23:21:08 +02:00
* fix: don't show an undefined error in flash msg when unsyncing * tests/int/secrets-test: add flash message tests
107 lines
4.2 KiB
JavaScript
107 lines
4.2 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import ApplicationAdapter from 'vault/adapters/application';
|
|
import { assert } from '@ember/debug';
|
|
import { all } from 'rsvp';
|
|
|
|
export default class SyncAssociationAdapter extends ApplicationAdapter {
|
|
namespace = 'v1/sys/sync';
|
|
|
|
buildURL(modelName, id, snapshot, requestType, query = {}) {
|
|
const { destinationType, destinationName } = snapshot ? snapshot.attributes() : query;
|
|
if (!destinationType || !destinationName) {
|
|
return `${super.buildURL()}/associations`;
|
|
}
|
|
const { action } = snapshot?.adapterOptions || {};
|
|
const uri = action ? `/${action}` : '';
|
|
return `${super.buildURL()}/destinations/${destinationType}/${destinationName}/associations${uri}`;
|
|
}
|
|
|
|
query(store, { modelName }, query) {
|
|
// endpoint doesn't accept the typical list query param and we don't want to pass options from lazyPaginatedQuery
|
|
const url = this.buildURL(modelName, null, null, 'query', query);
|
|
return this.ajax(url, 'GET');
|
|
}
|
|
|
|
// typically associations are queried for a specific destination which is what the standard query method does
|
|
// in specific cases we can query all associations to access total_associations and total_secrets values
|
|
queryAll() {
|
|
const url = `${this.buildURL('sync/association')}`;
|
|
return this.ajax(url, 'GET', { data: { list: true } }).then((response) => {
|
|
const { total_associations, total_secrets } = response.data;
|
|
return { total_associations, total_secrets };
|
|
});
|
|
}
|
|
|
|
// fetch associations for many destinations
|
|
// returns aggregated association information for each destination
|
|
// information includes total associations, total unsynced and most recent updated datetime
|
|
async fetchByDestinations(destinations) {
|
|
const promises = destinations.map(({ name: destinationName, type: destinationType }) => {
|
|
return this.query(this.store, { modelName: 'sync/association' }, { destinationName, destinationType });
|
|
});
|
|
const queryResponses = await all(promises);
|
|
const serializer = this.store.serializerFor('sync/association');
|
|
return queryResponses.map((response) => serializer.normalizeFetchByDestinations(response));
|
|
}
|
|
|
|
// array of association data for each destination a secret is synced to
|
|
fetchSyncStatus({ mount, secretName }) {
|
|
const url = `${this.buildURL()}/destinations`;
|
|
return this.ajax(url, 'GET', { data: { mount, secret_name: secretName } }).then((resp) => {
|
|
const { associated_destinations } = resp.data;
|
|
const syncData = [];
|
|
for (const key in associated_destinations) {
|
|
const data = associated_destinations[key];
|
|
// renaming keys to match query() response
|
|
syncData.push({
|
|
destinationType: data.type,
|
|
destinationName: data.name,
|
|
syncStatus: data.sync_status,
|
|
updatedAt: data.updated_at,
|
|
});
|
|
}
|
|
return syncData;
|
|
});
|
|
}
|
|
|
|
// snapshot is needed for mount and secret_name values which are used to parse response since all associations are returned
|
|
_setOrRemove(store, { modelName }, snapshot) {
|
|
assert(
|
|
"action type of set or remove required when saving association => association.save({ adapterOptions: { action: 'set' }})",
|
|
['set', 'remove'].includes(snapshot?.adapterOptions?.action)
|
|
);
|
|
const url = this.buildURL(modelName, null, snapshot);
|
|
const data = snapshot.serialize();
|
|
const serializer = store.serializerFor('sync/association');
|
|
|
|
return this.ajax(url, 'POST', { data }).then((resp) => {
|
|
const association = Object.values(resp.data.associated_secrets).find((association) => {
|
|
return association.mount === data.mount && association.secret_name === data.secret_name;
|
|
});
|
|
|
|
// generate an id if an association is found
|
|
// (an association may not be found if the secret is being unsynced)
|
|
const id = association ? serializer.generateId(association) : undefined;
|
|
|
|
return {
|
|
...association,
|
|
id,
|
|
destinationName: resp.data.store_name,
|
|
destinationType: resp.data.store_type,
|
|
};
|
|
});
|
|
}
|
|
|
|
createRecord() {
|
|
return this._setOrRemove(...arguments);
|
|
}
|
|
|
|
updateRecord() {
|
|
return this._setOrRemove(...arguments);
|
|
}
|
|
}
|