mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
Secrets Sync: Bug fixes part 1 (#24580)
This commit is contained in:
@@ -29,7 +29,8 @@ export default class SyncAssociationAdapter extends ApplicationAdapter {
|
||||
// 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() {
|
||||
return this.query(this.store, { modelName: 'sync/association' }).then((response) => {
|
||||
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 };
|
||||
});
|
||||
@@ -49,8 +50,8 @@ export default class SyncAssociationAdapter extends ApplicationAdapter {
|
||||
|
||||
// array of association data for each destination a secret is synced to
|
||||
fetchSyncStatus({ mount, secretName }) {
|
||||
const url = `${this.buildURL()}/${mount}/${secretName}`;
|
||||
return this.ajax(url, 'GET').then((resp) => {
|
||||
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) {
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
<LoadingDropdownOption />
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="is-inline-block">
|
||||
<li class="action">
|
||||
<Hds::Button
|
||||
@text="Sync now"
|
||||
class="link"
|
||||
class="link is-flex-start"
|
||||
@isFullWidth={{true}}
|
||||
disabled={{not association.canSync}}
|
||||
data-test-association-action="sync"
|
||||
{{on "click" (fn this.update association "set")}}
|
||||
@@ -60,7 +61,7 @@
|
||||
data-test-association-action="unsync"
|
||||
@isInDropdown={{true}}
|
||||
@buttonText="Unsync"
|
||||
@confirmMessage="This secret will be unsynced from all destinations."
|
||||
@confirmMessage="This secret will be unsynced from this destination."
|
||||
@onConfirmAction={{fn this.update association "remove"}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
@@ -39,9 +39,14 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
||||
async update(association: SyncAssociationModel, operation: string) {
|
||||
try {
|
||||
await association.save({ adapterOptions: { action: operation } });
|
||||
// this message can be expanded after testing -- deliberately generic for now
|
||||
this.flashMessages.success(
|
||||
'Sync operation successfully initiated. Status will be updated on secret when complete.'
|
||||
const action: string = operation === 'set' ? 'Sync' : 'Unsync';
|
||||
this.flashMessages.success(`${action} operation initiated.`);
|
||||
// refresh route to update displayed secrets
|
||||
this.store.clearDataset('sync/association');
|
||||
this.router.transitionTo(
|
||||
'vault.cluster.sync.secrets.destinations.destination.secrets',
|
||||
this.args.destination.type,
|
||||
this.args.destination.name
|
||||
);
|
||||
} catch (error) {
|
||||
this.flashMessages.danger(`Sync operation error: \n ${errorMessage(error)}`);
|
||||
|
||||
@@ -74,10 +74,15 @@ export default class DestinationSyncPageComponent extends Component<Args> {
|
||||
@action
|
||||
setMount(selected: Array<string>) {
|
||||
this.mountPath = selected[0] || '';
|
||||
if (this.mountPath === '') {
|
||||
// clear secret path when mount is cleared
|
||||
this.secretPath = '';
|
||||
}
|
||||
}
|
||||
|
||||
setAssociation = task({}, async (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.error = ''; // reset error
|
||||
try {
|
||||
this.syncedSecret = '';
|
||||
const { name: destinationName, type: destinationType } = this.args.destination;
|
||||
|
||||
@@ -17,6 +17,12 @@ interface SyncDestinationSecretsRouteParams {
|
||||
export default class SyncDestinationSecretsRoute extends Route {
|
||||
@service declare readonly store: StoreService;
|
||||
|
||||
queryParams = {
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
model(params: SyncDestinationSecretsRouteParams) {
|
||||
const destination = this.modelFor('secrets.destinations.destination') as SyncDestinationModel;
|
||||
return hash({
|
||||
|
||||
@@ -19,6 +19,18 @@ interface SyncSecretsDestinationsIndexRouteParams {
|
||||
export default class SyncSecretsDestinationsIndexRoute extends Route {
|
||||
@service declare readonly store: StoreService;
|
||||
|
||||
queryParams = {
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
name: {
|
||||
refreshModel: true,
|
||||
},
|
||||
type: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
filterData(dataset: Array<SyncDestinationModel>, name: string, type: string): Array<SyncDestinationModel> {
|
||||
let filteredDataset = dataset;
|
||||
const filter = (key: keyof SyncDestinationModel, value: string) => {
|
||||
|
||||
@@ -29,7 +29,7 @@ export const associationsResponse = (schema, req) => {
|
||||
};
|
||||
|
||||
export const syncStatusResponse = (schema, req) => {
|
||||
const { mount, name: secret_name } = req.params;
|
||||
const { mount, secret_name } = req.queryParams;
|
||||
const records = schema.db.syncAssociations.where({ mount, secret_name });
|
||||
if (!records.length) {
|
||||
return new Response(404, {}, { errors: [] });
|
||||
|
||||
@@ -99,9 +99,17 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook
|
||||
});
|
||||
|
||||
test('it renders secret details and toggles json view', async function (assert) {
|
||||
assert.expect(8);
|
||||
this.server.get(`sys/sync/associations/:mount/*name`, (schema, req) => {
|
||||
assert.expect(9);
|
||||
this.server.get(`sys/sync/associations/destinations`, (schema, req) => {
|
||||
assert.ok(true, 'request made to fetch sync status');
|
||||
assert.propEqual(
|
||||
req.queryParams,
|
||||
{
|
||||
mount: this.backend,
|
||||
secret_name: this.path,
|
||||
},
|
||||
'query params include mount and secret name'
|
||||
);
|
||||
// no records so response returns 404
|
||||
return syncStatusResponse(schema, req);
|
||||
});
|
||||
@@ -233,7 +241,7 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook
|
||||
});
|
||||
|
||||
test('it renders sync status page alert', async function (assert) {
|
||||
assert.expect(3); // assert count important because confirms request made to fetch sync status twice
|
||||
assert.expect(5); // assert count important because confirms request made to fetch sync status twice
|
||||
const destinationName = 'my-destination';
|
||||
this.server.create('sync-association', {
|
||||
type: 'aws-sm',
|
||||
@@ -241,9 +249,17 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook
|
||||
mount: this.backend,
|
||||
secret_name: this.path,
|
||||
});
|
||||
this.server.get('sys/sync/associations/:mount/*name', (schema, req) => {
|
||||
this.server.get(`sys/sync/associations/destinations`, (schema, req) => {
|
||||
// this assertion should be hit twice, once on init and again when the 'Refresh' button is clicked
|
||||
assert.ok(true, 'request made to fetch sync status');
|
||||
assert.propEqual(
|
||||
req.queryParams,
|
||||
{
|
||||
mount: this.backend,
|
||||
secret_name: this.path,
|
||||
},
|
||||
'query params include mount and secret name'
|
||||
);
|
||||
return syncStatusResponse(schema, req);
|
||||
});
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ module('Integration | Component | sync | Secrets::Page::Destinations::Destinatio
|
||||
await click(kvSuggestion.input);
|
||||
assert.dom(searchSelect.option()).hasText('my-path/', 'Nested secret path renders');
|
||||
assert.dom(searchSelect.option(1)).hasText('my-secret', 'Secret renders');
|
||||
await click(searchSelect.removeSelected);
|
||||
assert.dom(kvSuggestion.input).hasValue('', 'secret path value is cleared when mount is unset');
|
||||
});
|
||||
|
||||
test('it should render secret suggestions for nested paths', async function (assert) {
|
||||
|
||||
@@ -59,10 +59,11 @@ module('Unit | Adapter | sync | association', function (hooks) {
|
||||
});
|
||||
|
||||
test('it should make request to correct endpoint for queryAll associations', async function (assert) {
|
||||
assert.expect(2);
|
||||
assert.expect(3);
|
||||
|
||||
this.server.get('/sys/sync/associations', () => {
|
||||
this.server.get('/sys/sync/associations', (schema, req) => {
|
||||
assert.ok(true, 'request is made to correct endpoint for queryAll');
|
||||
assert.propEqual(req.queryParams, { list: 'true' }, 'query params include list: true');
|
||||
return {
|
||||
data: {
|
||||
key_info: {},
|
||||
|
||||
Reference in New Issue
Block a user