From 9f09f01f3646c68ef08bc5f7ca2ea0f5de02f391 Mon Sep 17 00:00:00 2001 From: Cameron Perera Date: Tue, 2 Aug 2022 11:55:26 -0500 Subject: [PATCH] Icu 5253 add query to include credentials (#1227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 add-credential-sources to include credentials * refactor: 💡 update add-credential-sources template Update to how AddCredentialSources form is rendered in the template ✅ Closes: ICU-5253 * refactor: 💡 Update add-credential-sources method signatures * refactor: 💡 change how add-credential-source form is rendered * refactor: 💡 Remove duplicate add-credential-sources message Remove the duplicate No Credential Credential Sources message from add-credential-sources form * refactor: 💡 Move filter logic to controller Move credential source filter logic from route to controller for add-credential-sources * test: 💍 Verify all available credential sources are listed --- addons/core/translations/resources/en-us.yaml | 3 + .../target/add-credential-libraries/index.hbs | 63 ---------------- .../target/add-credential-libraries/index.js | 65 ----------------- .../target/add-credential-sources/index.hbs | 45 ++++++++++++ .../target/add-credential-sources/index.js | 36 +++++++++ .../targets/target/add-credential-sources.js | 31 ++++++++ .../targets/target/add-credential-sources.js | 18 +++-- .../targets/target/add-credential-sources.hbs | 8 +- .../targets/credential-sources-test.js | 73 +++++++++++++++++-- 9 files changed, 199 insertions(+), 143 deletions(-) delete mode 100644 ui/admin/app/components/form/target/add-credential-libraries/index.hbs delete mode 100644 ui/admin/app/components/form/target/add-credential-libraries/index.js create mode 100644 ui/admin/app/components/form/target/add-credential-sources/index.hbs create mode 100644 ui/admin/app/components/form/target/add-credential-sources/index.js diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 4901c02bc..2579a0763 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -297,6 +297,9 @@ target: credential-source: title: Credential Source title_plural: Credential Sources + types: + vault: Vault + username_password: Username & Password messages: welcome: title: Welcome to Credential Sources diff --git a/ui/admin/app/components/form/target/add-credential-libraries/index.hbs b/ui/admin/app/components/form/target/add-credential-libraries/index.hbs deleted file mode 100644 index ce0c51912..000000000 --- a/ui/admin/app/components/form/target/add-credential-libraries/index.hbs +++ /dev/null @@ -1,63 +0,0 @@ -{{#if this.hasAvailableCredentialLibraries}} - - - - - - - {{t 'form.id.label'}} - {{t 'form.name.label'}} - {{t 'form.type.label'}} - - - - {{#each this.filteredCredentialLibraries as |credentialLibrary|}} - - - - - {{credentialLibrary.name}} - - {{t - (concat - 'resources.credential-library.types.' credentialLibrary.type - ) - }} - - - {{/each}} - - - -{{/if}} - -{{#unless this.hasAvailableCredentialLibraries}} - - - - {{t 'resources.target.credential-source.messages.none.description'}} - - - - {{t 'actions.back'}} - - - -{{/unless}} \ No newline at end of file diff --git a/ui/admin/app/components/form/target/add-credential-libraries/index.js b/ui/admin/app/components/form/target/add-credential-libraries/index.js deleted file mode 100644 index 78b4a29b0..000000000 --- a/ui/admin/app/components/form/target/add-credential-libraries/index.js +++ /dev/null @@ -1,65 +0,0 @@ -import Component from '@glimmer/component'; -import { computed, action } from '@ember/object'; -import { A } from '@ember/array'; - -export default class FormTargetAddCredentialLibrariesIndexComponent extends Component { - // =properties - - /** - * Array of selected credential library IDs. - * @type {EmberArray} - */ - selectedCredentialLibraryIDs = A(); - - /** - * Checks for unassigned credential libraries. - * @param {[CredentialLibraryModel]} filteredCredentialLibraries - * @type {boolean} - */ - @computed('filteredCredentialLibraries.length') - get hasAvailableCredentialLibraries() { - return this.filteredCredentialLibraries.length > 0; - } - - /** - * Filter out credential libraries not already added to the target. - * @type {[CredentialLibraryModel]} - */ - @computed( - 'args.{credentialLibraries.[],model.application_credential_source_ids.[]}' - ) - get filteredCredentialLibraries() { - // Get IDs for credential libraries already added to the current target - const currentCredentialLibraryIDs = - this.args.model.application_credential_source_ids.map( - (source) => source.value - ); - const notAddedCredentialLibraries = this.args.credentialLibraries.filter( - ({ id }) => !currentCredentialLibraryIDs.includes(id) - ); - return notAddedCredentialLibraries; - } - - // =actions - - /** - * Add/Remove credential library to current selection - * @param CredentialLibraryModel - */ - @action - toggleCredentialLibrary(credentialLibrary) { - if (!this.selectedCredentialLibraryIDs.includes(credentialLibrary.id)) { - this.selectedCredentialLibraryIDs.addObject(credentialLibrary.id); - } else { - this.selectedCredentialLibraryIDs.removeObject(credentialLibrary.id); - } - } - - /** - * Submit selected credential library ids - */ - @action - submit(fn) { - fn(this.selectedCredentialLibraryIDs); - } -} diff --git a/ui/admin/app/components/form/target/add-credential-sources/index.hbs b/ui/admin/app/components/form/target/add-credential-sources/index.hbs new file mode 100644 index 000000000..1f7f7704b --- /dev/null +++ b/ui/admin/app/components/form/target/add-credential-sources/index.hbs @@ -0,0 +1,45 @@ + + + + + + + {{t 'form.id.label'}} + {{t 'form.name.label'}} + {{t 'form.type.label'}} + + + + {{#each @filteredCredentialSources as |credentialSource|}} + + + + + {{credentialSource.name}} + + {{t + (concat + 'resources.target.credential-source.types.' + credentialSource.type + ) + }} + + + {{/each}} + + + \ No newline at end of file diff --git a/ui/admin/app/components/form/target/add-credential-sources/index.js b/ui/admin/app/components/form/target/add-credential-sources/index.js new file mode 100644 index 000000000..451d47f15 --- /dev/null +++ b/ui/admin/app/components/form/target/add-credential-sources/index.js @@ -0,0 +1,36 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { A } from '@ember/array'; + +export default class FormTargetAddCredentialSourcesIndexComponent extends Component { + // =properties + + /** + * Array of selected credential source IDs. + * @type {EmberArray} + */ + selectedCredentialSourceIDs = A(); + + // =actions + + /** + * Add/Remove credential source to current selection + * @param {CredentialLibraryModel, CredentialModel} credentialSource + */ + @action + toggleCredentialSource(credentialSource) { + if (!this.selectedCredentialSourceIDs.includes(credentialSource.id)) { + this.selectedCredentialSourceIDs.addObject(credentialSource.id); + } else { + this.selectedCredentialSourceIDs.removeObject(credentialSource.id); + } + } + + /** + * Submit selected credential source ids + */ + @action + submit(fn) { + fn(this.selectedCredentialSourceIDs); + } +} diff --git a/ui/admin/app/controllers/scopes/scope/targets/target/add-credential-sources.js b/ui/admin/app/controllers/scopes/scope/targets/target/add-credential-sources.js index 9bda83b4f..b022058d2 100644 --- a/ui/admin/app/controllers/scopes/scope/targets/target/add-credential-sources.js +++ b/ui/admin/app/controllers/scopes/scope/targets/target/add-credential-sources.js @@ -1,5 +1,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; export default class ScopesScopeTargetsTargetAddCredentialSourcesController extends Controller { // =services @@ -8,6 +9,36 @@ export default class ScopesScopeTargetsTargetAddCredentialSourcesController exte // =attributes + /** + * Checks for unassigned credential sources. + * @type {boolean} + */ + get hasAvailableCredentialSources() { + return this.filteredCredentialSources.length > 0; + } + + /** + * Filter out credential sources not already added to the target. + * @type {[CredentialLibraryModel, CredentialModel]} + */ + @computed( + 'model.{target.application_credential_source_ids.[],credentialLibraries.[],credentials.[]}' + ) + get filteredCredentialSources() { + // Get IDs for credential sources already added to the current target + const currentCredentialSourceIDs = + this.model.target.application_credential_source_ids.map( + (source) => source.value + ); + const notAddedCredentialLibraries = this.model.credentialLibraries.filter( + ({ id }) => !currentCredentialSourceIDs.includes(id) + ); + const notAddedCredentials = this.model.credentials.filter( + ({ id }) => !currentCredentialSourceIDs.includes(id) + ); + return [...notAddedCredentialLibraries, ...notAddedCredentials]; + } + /** * Translated roles breadcrumb * @type {string} diff --git a/ui/admin/app/routes/scopes/scope/targets/target/add-credential-sources.js b/ui/admin/app/routes/scopes/scope/targets/target/add-credential-sources.js index 2da08908d..30484e4c6 100644 --- a/ui/admin/app/routes/scopes/scope/targets/target/add-credential-sources.js +++ b/ui/admin/app/routes/scopes/scope/targets/target/add-credential-sources.js @@ -15,15 +15,16 @@ export default class ScopesScopeTargetsTargetAddCredentialSourcesRoute extends R // =methods /** - * Empty out any previously loaded credential libraries. + * Empty out any previously loaded credential sources. */ beforeModel() { this.store.unloadAll('credential-library'); + this.store.unloadAll('credential'); } /** - * Returns the current target and all credential libraries. - * @return {{target: TargetModel, credentialLibraries: [CredentialLibraryModel]}} + * Returns the current target and credential sources. + * @return {{target: TargetModel, credentialLibraries: [CredentialLibraryModel], credentials: [CredentialModel]}} */ async model() { const target = this.modelFor('scopes.scope.targets.target'); @@ -33,17 +34,24 @@ export default class ScopesScopeTargetsTargetAddCredentialSourcesRoute extends R }); await all( credentialStores.map(({ id: credential_store_id, isStatic }) => { - //credential libraries don't have a type static so exclude them - if (!isStatic) + if (isStatic) { + return this.store.query('credential', { + credential_store_id, + }); + } else { return this.store.query('credential-library', { credential_store_id, }); + } }) ); const credentialLibraries = this.store.peekAll('credential-library'); + const credentials = this.store.peekAll('credential'); + return { target, credentialLibraries, + credentials, }; } diff --git a/ui/admin/app/templates/scopes/scope/targets/target/add-credential-sources.hbs b/ui/admin/app/templates/scopes/scope/targets/target/add-credential-sources.hbs index 90d3bdcc5..9b972259e 100644 --- a/ui/admin/app/templates/scopes/scope/targets/target/add-credential-sources.hbs +++ b/ui/admin/app/templates/scopes/scope/targets/target/add-credential-sources.hbs @@ -14,16 +14,16 @@ - {{#if @model.credentialLibraries}} - {{/if}} - {{#unless @model.credentialLibraries}} + {{#unless this.hasAvailableCredentialSources}} this.server.schema.credentialLibraries.all().models.length; + getCredentialCount = () => + this.server.schema.credentials.all().models.length; + credentialSourceCount = getCredentialLibraryCount() + getCredentialCount(); authenticateSession({}); }); @@ -104,13 +119,59 @@ module('Acceptance | targets | credential-libraries', function (hooks) { assert.strictEqual(currentURL(), urls.addCredentialSources); }); + test('displays list of all credential source types available', async function (assert) { + assert.expect(2); + instances.target.update({ credentialLibraries: [] }); + await visit(urls.addCredentialSources); + assert.strictEqual(findAll('tbody tr').length, credentialSourceCount); + assert.notOk(find('.rose-message-title')); + }); + + test('displays list of credential sources with only credential libraries available', async function (assert) { + assert.expect(2); + instances.target.update({ credentialLibraries: [] }); + this.server.db.credentials.remove(); + await visit(urls.addCredentialSources); + assert.strictEqual(findAll('tbody tr').length, getCredentialLibraryCount()); + assert.notOk(find('.rose-message-title')); + }); + + test('displays list of credential sources with only credentials available', async function (assert) { + assert.expect(2); + await visit(urls.addCredentialSources); + assert.strictEqual(findAll('tbody tr').length, getCredentialCount()); + assert.notOk(find('.rose-message-title')); + }); + + test('displays no credential sources message when none available', async function (assert) { + assert.expect(1); + instances.target.update({ credentialLibraries: [] }); + this.server.db.credentialLibraries.remove(); + this.server.db.credentials.remove(); + await visit(urls.addCredentialSources); + assert.strictEqual( + find('.rose-message-title').textContent.trim(), + 'No Credential Sources Available' + ); + }); + + test('when no credential sources available, button routes to credential sources', async function (assert) { + assert.expect(1); + instances.target.update({ credentialLibraries: [] }); + this.server.db.credentialLibraries.remove(); + this.server.db.credentials.remove(); + await visit(urls.addCredentialSources); + await click(find('.rose-message-link')); + assert.strictEqual(currentURL(), urls.credentialSources); + }); + test('can select and save credential sources to add', async function (assert) { assert.expect(4); instances.target.update({ credentialLibraries: [] }); await visit(urls.credentialSources); assert.strictEqual(findAll('tbody tr').length, 0); await visit(urls.addCredentialSources); - assert.strictEqual(findAll('tbody tr').length, getCredentialLibraryCount()); + assert.strictEqual(findAll('tbody tr').length, credentialSourceCount); await click('tbody label'); await click('form [type="submit"]'); assert.strictEqual(currentURL(), urls.credentialSources); @@ -133,7 +194,7 @@ module('Acceptance | targets | credential-libraries', function (hooks) { await visit(urls.credentialSources); assert.strictEqual(findAll('tbody tr').length, 0); await visit(urls.addCredentialSources); - assert.strictEqual(findAll('tbody tr').length, getCredentialLibraryCount()); + assert.strictEqual(findAll('tbody tr').length, credentialSourceCount); await click('tbody label'); await click('form [type="button"]'); assert.strictEqual(currentURL(), urls.credentialSources);