Icu 5578 - introduce injected application credential on the api (#1269)

* chore: 🤖 include injected cred sources to the target model
This commit is contained in:
Dharini Jeeva authored and GitHub committed 2022-09-01 10:07:36 -07:00
1 parent bd0854b0f6
commit 2614b311a1
11 files changed
+395 -171

No files matched your search

+108 -10
View File
@@ -20,7 +20,7 @@ export default class TargetModel extends GeneratedTargetModel {
host_sources;
/**
* Credential source ids are read only and can be
* Brokered Credential source ids are read only and can be
* persisted via a dedicated call to `addBrokeredCredentialSources()`.
*/
@attr('string-array', {
@@ -29,6 +29,16 @@ export default class TargetModel extends GeneratedTargetModel {
})
brokered_credential_source_ids;
/**
* Injected Application Credential source ids are read only and can be
* persisted via a dedicated call to `addInjectedApplicationCredentialSources()`.
*/
@attr('string-array', {
readOnly: true,
emptyArrayIfMissing: true,
})
injected_application_credential_source_ids;
/**
* An array of resolved host set and host catalog instances. Model instances
* must already be loaded into the store (this method will not load unloaded
@@ -62,6 +72,24 @@ export default class TargetModel extends GeneratedTargetModel {
.filter(Boolean);
}
/**
* An array of resolved credential library and credential instances. Model instances
* must already be loaded into the store (this method will not load unloaded
* instances). Unresolvable instances are excluded from the array.
* @type {[CredentialLibraryModel, CredentialModel]}
*/
get injectedApplicationCredentialSources() {
return this.injected_application_credential_source_ids
.map((source) => {
if (source.value.startsWith('cred')) {
return this.store.peekRecord('credential', source.value);
} else {
return this.store.peekRecord('credential-library', source.value);
}
})
.filter(Boolean);
}
/**
* Sessions associated with this target (but only already loaded sessions).
* @type {SessionModel[]}
@@ -152,18 +180,44 @@ export default class TargetModel extends GeneratedTargetModel {
/**
* Adds credential sources via the `add-credential-sources` method.
* See serializer and adapter for more information.
* @param {[string]} credentialSourceIDs
* @param {[string]} brokeredCredentialSourceIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
addBrokeredCredentialSources(
credentialSourceIDs,
brokeredCredentialSourceIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'add-credential-sources',
credentialSourceIDs,
brokeredCredentialSourceIDs,
};
// There is no "deep merge" in ES.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
/**
* Adds credential sources via the `add-credential-sources` method.
* See serializer and adapter for more information.
* @param {[string]} injectedApplicationCredentialSourceIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
addInjectedApplicationCredentialSources(
injectedApplicationCredentialSourceIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'add-credential-sources',
injectedApplicationCredentialSourceIDs,
};
// There is no "deep merge" in ES.
return this.save({
@@ -178,18 +232,18 @@ export default class TargetModel extends GeneratedTargetModel {
/**
* Delete credential libraries and credentials via the `remove-credential-sources` method.
* See serializer and adapter for more information.
* @param {[string]} credentialSourceIDs
* @param {[string]} brokeredCredentialSourceIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
removeBrokeredCredentialSources(
credentialSourceIDs,
brokeredCredentialSourceIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'remove-credential-sources',
credentialSourceIDs,
brokeredCredentialSourceIDs,
};
// There is no "deep merge" in ES.
return this.save({
@@ -203,14 +257,58 @@ export default class TargetModel extends GeneratedTargetModel {
/**
* Delete a single credential library/credential set via the `remove-credential-sources` method.
* @param {number} credentialSourceID
* @param {number} brokeredCredentialSourceID
* @param {object} options
* @return {Promise}
*/
removeBrokeredCredentialSource(credentialSourceID, options) {
return this.removeBrokeredCredentialSources([credentialSourceID], options);
removeBrokeredCredentialSource(brokeredCredentialSourceID, options) {
return this.removeBrokeredCredentialSources(
[brokeredCredentialSourceID],
options
);
}
/**
* Delete credential libraries and credentials via the `remove-credential-sources` method.
* See serializer and adapter for more information.
* @param {[string]} injectedApplicationCredentialSourceIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
removeInjectedApplicationCredentialSources(
injectedApplicationCredentialSourceIDs,
options = { adapterOptions: {} }
) {
const defaultAdapterOptions = {
method: 'remove-credential-sources',
injectedApplicationCredentialSourceIDs,
};
// There is no "deep merge" in ES.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
/**
* Delete a single credential library/credential set via the `remove-credential-sources` method.
* @param {number} injectedApplicationCredentialSourceID
* @param {object} options
* @return {Promise}
*/
removeInjectedApplicationCredentialSource(
injectedApplicationCredentialSourceID,
options
) {
return this.removeInjectedApplicationCredentialSources(
[injectedApplicationCredentialSourceID],
options
);
}
/**
* True if the target type is tcp.
* @type {boolean}
+36 -7
View File
@@ -21,11 +21,20 @@ export default class TargetSerializer extends ApplicationSerializer {
if (hostSourceIDs) {
serialized = this.serializeWithHostSources(snapshot, hostSourceIDs);
}
const credentialSourceIDs = snapshot?.adapterOptions?.credentialSourceIDs;
if (credentialSourceIDs)
serialized = this.serializeWithCredentialSources(
const brokeredCredentialSourceIDs =
snapshot?.adapterOptions?.brokeredCredentialSourceIDs;
const injectedApplicationCredentialSourceIDs =
snapshot?.adapterOptions?.injectedApplicationCredentialSourceIDs;
if (brokeredCredentialSourceIDs)
serialized = this.serializeWithBrokeredCredentialSources(
snapshot,
credentialSourceIDs
brokeredCredentialSourceIDs
);
if (injectedApplicationCredentialSourceIDs)
serialized = this.serializeWithInjectedApplicationCredentialSources(
snapshot,
injectedApplicationCredentialSourceIDs
);
return serialized;
}
@@ -48,13 +57,33 @@ export default class TargetSerializer extends ApplicationSerializer {
* Returns a payload containing only version and an array of passed IDs,
* rather than existing instances on the model.
* @param {Snapshot} snapshot
* @param {[string]} credentialSourceIDs
* @param {[string]} brokered_credential_source_ids
* @return {object}
*/
serializeWithCredentialSources(snapshot, credentialSourceIDs) {
serializeWithBrokeredCredentialSources(
snapshot,
brokered_credential_source_ids
) {
return {
version: snapshot.attr('version'),
brokered_credential_source_ids: credentialSourceIDs,
brokered_credential_source_ids,
};
}
/**
* Returns a payload containing only version and an array of passed IDs,
* rather than existing instances on the model.
* @param {Snapshot} snapshot
* @param {[string]} injected_application_credential_source_ids
* @return {object}
*/
serializeWithInjectedApplicationCredentialSources(
snapshot,
injected_application_credential_source_ids
) {
return {
version: snapshot.attr('version'),
injected_application_credential_source_ids,
};
}
}
+2 -58
View File
@@ -1,5 +1,6 @@
import config from '../config/environment';
import { authHandler, deauthHandler } from './route-handlers/auth';
import { targetHandler } from './route-handlers/target';
import { pickRandomStatusString } from './factories/session';
import { Response } from 'miragejs';
import initializeMockIPC from './scenarios/ipc';
@@ -435,64 +436,7 @@ export default function () {
this.get('/targets/:id');
this.patch('/targets/:id');
this.del('/targets/:id');
this.post(
'/targets/:idMethod',
function ({ targets }, { params: { idMethod } }) {
const attrs = this.normalizedRequestAttrs();
const id = idMethod.split(':')[0];
const method = idMethod.split(':')[1];
const target = targets.find(id);
const updatedAttrs = {
version: attrs.version,
};
// If adding host sources, push them into the array
if (method === 'add-host-sources') {
updatedAttrs.hostSetIds = target.hostSetIds;
attrs.hostSourceIds.forEach((id) => {
if (!updatedAttrs.hostSetIds.includes(id)) {
updatedAttrs.hostSetIds.push(id);
}
});
}
// If deleting host sources, filter them out of the array
if (method === 'remove-host-sources') {
updatedAttrs.hostSetIds = target.hostSetIds;
updatedAttrs.hostSetIds = updatedAttrs.hostSetIds.filter((id) => {
return !attrs.hostSourceIds.includes(id);
});
}
// If adding brokered credential sources, push them into the array
if (method === 'add-credential-sources') {
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
updatedAttrs.credentialIds = target.credentialIds;
attrs.brokeredCredentialSourceIds.forEach((id) => {
if (
!updatedAttrs.credentialLibraryIds.includes(id) ||
!updatedAttrs.credentialIds.includes(id)
) {
if (id.startsWith('cred')) {
updatedAttrs.credentialIds.push(id);
} else {
updatedAttrs.credentialLibraryIds.push(id);
}
}
});
}
// If deleting brokered credential sources, filter them out of the array
if (method === 'remove-credential-sources') {
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
updatedAttrs.credentialIds = target.credentialIds;
updatedAttrs.credentialLibraryIds =
updatedAttrs.credentialLibraryIds.filter((id) => {
return !attrs.brokeredCredentialSourceIds.includes(id);
});
updatedAttrs.credentialIds = updatedAttrs.credentialIds.filter((id) => {
return !attrs.brokeredCredentialSourceIds.includes(id);
});
}
return target.update(updatedAttrs);
}
);
this.post('/targets/:idMethod', targetHandler);
// session
+12 -4
View File
@@ -65,14 +65,22 @@ export default factory.extend({
.where(
(credentialLibrary) => credentialLibrary.scopeId === target.scope.id
)
.models.filter(() => randomBoolean());
.models.filter(() => randomBoolean())
.map((cred) => cred.id);
randomlySelectedCredentials = server.schema.credentials
.where((credential) => credential.scopeId === target.scope.id)
.models.filter(() => randomBoolean());
.models.filter(() => randomBoolean())
.map((cred) => cred.id);
target.update({
hostSets: randomlySelectedHostSets,
credentialLibraries: randomlySelectedCredentialLibraries,
credentials: randomlySelectedCredentials,
brokeredCredentialSourceIds: [
...randomlySelectedCredentialLibraries,
...randomlySelectedCredentials,
],
injectedApplicationCredentialSourceIds: [
...randomlySelectedCredentialLibraries,
...randomlySelectedCredentials,
],
});
},
}),
-2
View File
@@ -4,6 +4,4 @@ import { belongsTo, hasMany } from 'ember-cli-mirage';
export default Model.extend({
scope: belongsTo(),
hostSets: hasMany(),
credentialLibraries: hasMany(),
credentials: hasMany(),
});
@@ -0,0 +1,99 @@
export function targetHandler({ targets }, { params: { idMethod } }) {
const attrs = this.normalizedRequestAttrs();
const id = idMethod.split(':')[0];
const method = idMethod.split(':')[1];
const target = targets.find(id);
const {
brokeredCredentialSourceIds: originalBrokeredCredentials,
injectedApplicationCredentialSourceIds: originalInjectedCredentials,
} = target;
const selectedBrokeredCredentials = attrs.brokeredCredentialSourceIds;
const selectedInjectedCredentials =
attrs.injectedApplicationCredentialSourceIds;
const selectedHostSourceIds = attrs.hostSourceIds;
const originHostSetIds = target.hostSetIds;
const updatedAttrs = {
version: attrs.version,
};
const addToSourcesList = (originalSourceIds, selectedSourceIds) => {
const sourceIds = new Set([...originalSourceIds, ...selectedSourceIds]);
return [...sourceIds];
};
const removeFromSourcesList = (originalSourceIds, selectedSourceIds) => {
const sourceIds = new Set(originalSourceIds);
for (const elem of selectedSourceIds) {
sourceIds.delete(elem);
}
return [...sourceIds];
};
// If adding host sources, push them into the array
if (method === 'add-host-sources') {
if (selectedHostSourceIds) {
const listOfHostSources = addToSourcesList(
originHostSetIds,
selectedHostSourceIds
);
if (listOfHostSources?.length) {
updatedAttrs.hostSetIds = listOfHostSources;
}
}
}
// If deleting host sources, filter them out of the array
if (method === 'remove-host-sources') {
if (selectedHostSourceIds) {
const removedHostSources = removeFromSourcesList(
originHostSetIds,
selectedHostSourceIds
);
updatedAttrs.hostSetIds = removedHostSources;
}
}
// If adding brokered or injected application cred sources, push them into the array
if (method === 'add-credential-sources') {
if (selectedBrokeredCredentials) {
const listOfBrokeredCredentialSources = addToSourcesList(
originalBrokeredCredentials,
selectedBrokeredCredentials
);
if (listOfBrokeredCredentialSources?.length) {
updatedAttrs.brokeredCredentialSourceIds =
listOfBrokeredCredentialSources;
}
}
if (selectedInjectedCredentials) {
const listOfInjectedCredentialSources = addToSourcesList(
originalInjectedCredentials,
selectedInjectedCredentials
);
if (listOfInjectedCredentialSources?.length) {
updatedAttrs.injectedApplicationCredentialSourceIds =
listOfInjectedCredentialSources;
}
}
}
// If deleting brokered or injected application cred sources, filter them out of the array
if (method === 'remove-credential-sources') {
if (selectedBrokeredCredentials) {
const removedCredentialSources = removeFromSourcesList(
originalBrokeredCredentials,
selectedBrokeredCredentials
);
updatedAttrs.brokeredCredentialSourceIds = removedCredentialSources;
}
if (selectedInjectedCredentials) {
const removedCredentialSources = removeFromSourcesList(
originalInjectedCredentials,
selectedInjectedCredentials
);
updatedAttrs.injectedApplicationCredentialSourceIds =
removedCredentialSources;
}
}
return target.update(updatedAttrs);
}
-6
View File
@@ -23,12 +23,6 @@ export default ApplicationSerializer.extend({
return { id: host_set_id, host_catalog_id };
});
}
if (model.credentialLibraryIds?.length || model.credentialIds?.length) {
json.brokered_credential_source_ids = [
...model.credentialLibraryIds,
...model.credentialIds,
];
}
return json;
},
});
@@ -215,6 +215,7 @@ module('Unit | Serializer | application', function (hooks) {
scope: { id: 'o_123', scope_id: 'o_123' },
host_sources: [],
brokered_credential_source_ids: [],
injected_application_credential_source_ids: [],
},
relationships: {},
},
@@ -16,6 +16,10 @@ module('Unit | Serializer | target', function (hooks) {
{ host_source_id: '3', host_catalog_id: '4' },
],
brokered_credential_source_ids: [{ value: '1' }, { value: '2' }],
injected_application_credential_source_ids: [
{ value: '4' },
{ value: '5' },
],
scope: {
scope_id: 'org_1',
type: 'org',
@@ -66,7 +70,7 @@ module('Unit | Serializer | target', function (hooks) {
});
});
test('it serializes only credential sources and version when an `adapterOptions.credentialSourceIDs` array is passed', function (assert) {
test('it serializes only credential sources and version when an `adapterOptions.brokeredCredentialSourceIDs` array is passed', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('target');
@@ -78,15 +82,40 @@ module('Unit | Serializer | target', function (hooks) {
});
const snapshot = record._createSnapshot();
snapshot.adapterOptions = {
credentialSourceIDs: ['4', '5'],
brokeredCredentialSourceIDs: ['4', '5'],
};
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
brokered_credential_source_ids: ['4', '5'],
version: 1,
});
});
test('it serializes only credential sources and version when an `adapterOptions.injectedApplicationCredentialSourceIDs` array is passed', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('target');
const record = store.createRecord('target', {
name: 'User',
description: 'Description',
injected_application_credential_source_ids: [
{ value: '1' },
{ value: '2' },
],
version: 1,
});
const snapshot = record._createSnapshot();
snapshot.adapterOptions = {
injectedApplicationCredentialSourceIDs: ['4', '5'],
};
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
injected_application_credential_source_ids: ['4', '5'],
version: 1,
});
});
test('it serializes the worker_filter attribute if present', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
@@ -132,6 +161,7 @@ module('Unit | Serializer | target', function (hooks) {
{ host_source_id: '3', host_catalog_id: '4' },
],
brokered_credential_source_ids: ['1'],
injected_application_credential_source_ids: ['3'],
};
const normalized = serializer.normalizeSingleResponse(
store,
@@ -151,6 +181,7 @@ module('Unit | Serializer | target', function (hooks) {
{ host_source_id: '3', host_catalog_id: '4' },
],
brokered_credential_source_ids: [{ value: '1' }],
injected_application_credential_source_ids: [{ value: '3' }],
},
relationships: {},
},
@@ -181,6 +212,7 @@ module('Unit | Serializer | target', function (hooks) {
type: 'target',
attributes: {
brokered_credential_source_ids: [],
injected_application_credential_source_ids: [],
authorized_actions: [],
host_sources: [],
name: 'Target 1',
@@ -217,6 +249,7 @@ module('Unit | Serializer | target', function (hooks) {
scope: { id: 'o_123', scope_id: 'o_123' },
host_sources: [],
brokered_credential_source_ids: [],
injected_application_credential_source_ids: [],
},
relationships: {},
},
@@ -248,6 +281,7 @@ module('Unit | Serializer | target', function (hooks) {
name: 'Target 1',
host_sources: [],
brokered_credential_source_ids: [],
injected_application_credential_source_ids: [],
worker_filter: 'worker',
},
relationships: {},
+2 -3
View File
@@ -5,11 +5,10 @@ import { loading } from 'ember-loading';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeWorkersRoute extends Route {
// =services
@service can
@service can;
@service session;
@service router;
@@ -4,20 +4,17 @@ import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { Response } from 'miragejs';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import {
authenticateSession,
// These are left here intentionally for future reference.
//currentSession,
//invalidateSession,
} from 'ember-simple-auth/test-support';
import { authenticateSession } from 'ember-simple-auth/test-support';
module('Acceptance | targets | credential sources', function (hooks) {
module('Acceptance | targets | brokered credential sources', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let getCredentialLibraryCount;
let getCredentialCount;
let credentialSourceCount;
let randomlySelectedCredentialLibraries;
let randomlySelectedCredentials;
const instances = {
scopes: {
@@ -41,6 +38,7 @@ module('Acceptance | targets | credential sources', function (hooks) {
credentialLibraries: null,
credentialLibrary: null,
addBrokeredCredentialSources: null,
brokeredCredentialSources: null,
};
hooks.beforeEach(function () {
@@ -78,38 +76,54 @@ module('Acceptance | targets | credential sources', function (hooks) {
instances.credential = instances.credentials[0];
instances.target = this.server.create('target', {
scope: instances.scopes.project,
credentialLibraries: instances.credentialLibraries,
});
randomlySelectedCredentials = this.server.schema.credentials
.all()
.models.filter(Boolean)
.map((cred) => cred.id);
randomlySelectedCredentialLibraries = this.server.schema.credentialLibraries
.all()
.models.filter(Boolean)
.map((cred) => cred.id);
instances.target.update({
brokeredCredentialSourceIds: [
...randomlySelectedCredentialLibraries,
...randomlySelectedCredentials,
],
});
// Generate route URLs for resources
urls.globalScope = `/scopes/global/scopes`;
urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`;
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
urls.targets = `${urls.projectScope}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.credentialSources = `${urls.target}/brokered-credential-sources`;
urls.brokeredCredentialSources = `${urls.target}/brokered-credential-sources`;
urls.credentialLibrary = `${urls.projectScope}/credential-stores/${instances.credentialLibrary.credentialStoreId}/credential-libraries/${instances.credentialLibrary.id}`;
urls.credential = `${urls.projectScope}/credential-stores/${instances.credential.credentialStoreId}/credentials/${instances.credential.id}`;
urls.addBrokeredCredentialSources = `${urls.target}/add-brokered-credential-sources`;
// Generate resource counter
getCredentialLibraryCount = () =>
this.server.schema.credentialLibraries.all().models.length;
getCredentialCount = () =>
this.server.schema.credentials.all().models.length;
credentialSourceCount = getCredentialLibraryCount() + getCredentialCount();
authenticateSession({});
});
test('visiting target credential sources', async function (assert) {
test('visiting target brokered credential sources', async function (assert) {
assert.expect(2);
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
await a11yAudit();
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(findAll('tbody tr').length, getCredentialLibraryCount());
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
});
test('can navigate to a vault type credential library', async function (assert) {
assert.expect(1);
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
await click('main tbody .rose-table-header-cell:nth-child(1) a');
await a11yAudit();
assert.strictEqual(currentURL(), urls.credentialLibrary);
@@ -118,51 +132,43 @@ module('Acceptance | targets | credential sources', function (hooks) {
test('can navigate to a username & password type credential', async function (assert) {
assert.expect(1);
instances.target.update({
credentialLibraries: [],
credentials: instances.credentials,
brokeredCredentialSourceIds: [...randomlySelectedCredentials],
});
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
await click('main tbody .rose-table-header-cell:nth-child(1) a');
await a11yAudit();
assert.strictEqual(currentURL(), urls.credential);
});
test('visiting add credential sources', async function (assert) {
test('visiting add brokered credential sources', async function (assert) {
assert.expect(1);
await visit(urls.addBrokeredCredentialSources);
await a11yAudit();
assert.strictEqual(currentURL(), urls.addBrokeredCredentialSources);
});
test('displays list of all credential source types available', async function (assert) {
test('displays list of all brokered credential source types available', async function (assert) {
assert.expect(2);
instances.target.update({ credentialLibraries: [] });
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
assert.notOk(find('.rose-message-title'));
assert.dom('.rose-message-title').doesNotExist();
});
test('displays list of credential sources with only credential libraries available', async function (assert) {
test('displays list of brokered credential sources with only credential libraries available', async function (assert) {
assert.expect(2);
instances.target.update({ credentialLibraries: [] });
this.server.db.credentials.remove();
instances.target.update({
brokeredCredentialSourceIds: [...randomlySelectedCredentialLibraries],
});
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, getCredentialLibraryCount());
assert.notOk(find('.rose-message-title'));
assert.dom('.rose-message-title').doesNotExist();
});
test('displays list of credential sources with only credentials available', async function (assert) {
assert.expect(2);
await visit(urls.addBrokeredCredentialSources);
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) {
test('displays no brokered 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.addBrokeredCredentialSources);
assert.strictEqual(
find('.rose-message-title').textContent.trim(),
@@ -170,53 +176,59 @@ module('Acceptance | targets | credential sources', function (hooks) {
);
});
test('when no credential sources available, button routes to credential sources', async function (assert) {
test('when no brokered credential sources available, button routes to add brokered 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.addBrokeredCredentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
await click(find('.rose-message-link'));
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.addBrokeredCredentialSources);
});
test('can select and save a vault type credential library to add', async function (assert) {
assert.expect(4);
instances.target.update({ credentialLibraries: [] });
await visit(urls.credentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
await click('tbody tr:first-child label');
await click('form [type="submit"]');
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 1);
});
test('can select and save a username & password type credential to add', async function (assert) {
assert.expect(4);
instances.target.update({ credentialLibraries: [] });
await visit(urls.credentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
await click('tbody tr:last-child label');
await click('form [type="submit"]');
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 1);
});
test('can select and save both a credential-library and a credential to add', async function (assert) {
assert.expect(4);
instances.target.update({ credentialLibraries: [] });
await visit(urls.credentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
await click('tbody tr:last-child label');
await click('tbody tr:first-child label');
await click('form [type="submit"]');
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 2);
});
@@ -226,34 +238,38 @@ module('Acceptance | targets | credential sources', function (hooks) {
instances.target.authorized_actions.filter(
(item) => item !== 'add-credential-sources'
);
await visit(urls.credentialSources);
assert.notOk(find(`[href="${urls.addBrokeredCredentialSources}"]`));
await visit(urls.brokeredCredentialSources);
assert.dom(`[href="${urls.addBrokeredCredentialSources}"]`).doesNotExist();
});
test('can select and cancel credential sources to add', async function (assert) {
assert.expect(4);
instances.target.update({ credentialLibraries: [] });
await visit(urls.credentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
await click('tbody label');
await click('form [type="button"]');
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
});
test('can select multiple credential sources to add and cancel', async function (assert) {
test('can select multiple brokered credential sources to add and cancel', async function (assert) {
assert.expect(4);
instances.target.update({ credentialLibraries: [] });
await visit(urls.credentialSources);
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialSourceCount);
await click('tbody tr:last-child label');
await click('tbody tr:first-child label');
await click('form [type="button"]');
assert.strictEqual(currentURL(), urls.credentialSources);
assert.strictEqual(currentURL(), urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, 0);
});
@@ -271,40 +287,42 @@ module('Acceptance | targets | credential sources', function (hooks) {
}
);
});
instances.target.update({ credentialLibraries: [] });
instances.target.update({
brokeredCredentialSourceIds: [],
});
await visit(urls.addBrokeredCredentialSources);
await click('tbody tr:last-child label');
await click('tbody tr:first-child label');
await click('form [type="submit"]');
assert.ok(find('[role="alert"]'));
assert.dom('[role="alert"]').isVisible();
});
test('can remove a vault type credential library', async function (assert) {
assert.expect(3);
instances.target.update({
brokeredCredentialSourceIds: [...randomlySelectedCredentialLibraries],
});
const credentialLibraryCount = getCredentialLibraryCount();
const credentialCount = getCredentialCount();
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialLibraryCount);
await click('tbody tr .rose-dropdown-button-danger');
assert.strictEqual(findAll('tbody tr').length, credentialLibraryCount - 1);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialCount + 1);
assert.strictEqual(findAll('tbody tr').length, credentialLibraryCount + 1);
});
test('can remove a username & password type credential', async function (assert) {
assert.expect(3);
instances.target.update({
credentialLibraries: [],
credentials: instances.credentials,
brokeredCredentialSourceIds: [...randomlySelectedCredentials],
});
const credentialCount = getCredentialCount();
const credentialLibraryCount = getCredentialLibraryCount();
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialCount);
await click('tbody tr .rose-dropdown-button-danger');
assert.strictEqual(findAll('tbody tr').length, credentialCount - 1);
await visit(urls.addBrokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, credentialLibraryCount + 1);
assert.strictEqual(findAll('tbody tr').length, credentialCount + 1);
});
test('cannot remove credential libraries without proper authorization', async function (assert) {
@@ -313,12 +331,15 @@ module('Acceptance | targets | credential sources', function (hooks) {
instances.target.authorized_actions.filter(
(item) => item !== 'remove-credential-sources'
);
await visit(urls.credentialSources);
assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
await visit(urls.brokeredCredentialSources);
assert.dom('tbody tr .rose-dropdown-button-danger').doesNotExist();
});
test('removing a target credential library which errors displays error messages', async function (assert) {
assert.expect(2);
instances.target.update({
brokeredCredentialSourceIds: [...randomlySelectedCredentialLibraries],
});
this.server.post('/targets/:idMethod', () => {
return new Response(
400,
@@ -332,17 +353,16 @@ module('Acceptance | targets | credential sources', function (hooks) {
);
});
const count = getCredentialLibraryCount();
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, count);
await click('tbody tr .rose-dropdown-button-danger');
assert.ok(find('[role="alert"]'));
assert.dom('[role="alert"]').isVisible();
});
test('removing a target credential which errors displays error messages', async function (assert) {
assert.expect(2);
instances.target.update({
credentialLibraries: [],
credentials: instances.credentials,
brokeredCredentialSourceIds: [...randomlySelectedCredentials],
});
this.server.post('/targets/:idMethod', () => {
return new Response(
@@ -357,9 +377,9 @@ module('Acceptance | targets | credential sources', function (hooks) {
);
});
const count = getCredentialCount();
await visit(urls.credentialSources);
await visit(urls.brokeredCredentialSources);
assert.strictEqual(findAll('tbody tr').length, count);
await click('tbody tr .rose-dropdown-button-danger');
assert.ok(find('[role="alert"]'));
assert.dom('[role="alert"]').isVisible();
});
});