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);