diff --git a/addons/api/addon/generated/models/host-catalog.js b/addons/api/addon/generated/models/host-catalog.js
index 30ada833f..585b8031b 100644
--- a/addons/api/addon/generated/models/host-catalog.js
+++ b/addons/api/addon/generated/models/host-catalog.js
@@ -58,6 +58,7 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS & Azure
@attr('boolean', {
+ for: 'plugin',
isNestedAttribute: true,
description: '',
})
@@ -66,12 +67,14 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS specific
@attr('string', {
+ for: 'plugin',
description:
'An expression used to filter the workers that have network access to a service that is hosting the external object store.',
})
worker_filter;
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: '',
})
@@ -79,12 +82,14 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS static credentials
@attr('string', {
+ for: 'plugin',
isNestedSecret: true,
description: '',
})
access_key_id;
@attr('string', {
+ for: 'plugin',
isNestedSecret: true,
description: '',
})
@@ -92,24 +97,28 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// AWS dynamic credentials
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: 'The role ARN to use.',
})
role_arn;
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: 'The role external ID to use.',
})
role_external_id;
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: 'The role session to use.',
})
role_session_name;
@attr('object-as-array', {
+ for: 'plugin',
isNestedAttribute: true,
description: 'The role tags to use.',
})
@@ -118,30 +127,35 @@ export default class GeneratedHostCatalogModel extends BaseModel {
// Azure specific
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: '',
})
tenant_id;
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: '',
})
client_id;
@attr('string', {
+ for: 'plugin',
isNestedAttribute: true,
description: '',
})
subscription_id;
@attr('string', {
+ for: 'plugin',
isNestedSecret: true,
description: '',
})
secret_id;
@attr('string', {
+ for: 'plugin',
isNestedSecret: true,
description: '',
})
diff --git a/addons/api/addon/models/host-catalog.js b/addons/api/addon/models/host-catalog.js
index 4a556035a..e8b47a2ac 100644
--- a/addons/api/addon/models/host-catalog.js
+++ b/addons/api/addon/models/host-catalog.js
@@ -27,6 +27,13 @@ export const TYPES_CREDENTIALS = Object.freeze([
TYPE_CREDENTIAL_DYNAMIC,
]);
+const DYNAMIC_CREDENTIAL_FIELDS = [
+ 'role_arn',
+ 'role_external_id',
+ 'role_session_name',
+ 'role_tags',
+];
+
export default class HostCatalogModel extends GeneratedHostCatalogModel {
// =attributes
@@ -53,7 +60,7 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel {
*/
get credentialType() {
if (!this.#credentialType) {
- if (this.role_arn) {
+ if (DYNAMIC_CREDENTIAL_FIELDS.some((field) => this[field])) {
this.#credentialType = TYPE_CREDENTIAL_DYNAMIC;
} else {
this.#credentialType = TYPE_CREDENTIAL_STATIC;
diff --git a/addons/api/addon/serializers/host-catalog.js b/addons/api/addon/serializers/host-catalog.js
index b83a053f3..ff885c8af 100644
--- a/addons/api/addon/serializers/host-catalog.js
+++ b/addons/api/addon/serializers/host-catalog.js
@@ -68,15 +68,29 @@ export default class HostCatalogSerializer extends ApplicationSerializer {
const value = super.serializeAttribute(...arguments);
const { compositeType, credentialType, isPlugin } = snapshot.record;
const { options } = attribute;
-
const fields =
compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE
? azureFields
: AWSfieldsWithCredentialType[credentialType];
- // Delete any nested attribute fields that don't belong to the record type
+ // Delete any nested attribute fields that don't belong to the record's compositeType
if (isPlugin && options.isNestedAttribute && json.attributes) {
- if (!fields.includes(key)) {
+ // If compositeType is AWS, delete azure fields from the json, and set the rest that don't belong to respective compositeType to null
+ if (compositeType === TYPE_HOST_CATALOG_PLUGIN_AWS) {
+ if (!AWSfieldsWithCredentialType[credentialType].includes(key)) {
+ if (azureFields.includes(key)) {
+ delete json.attributes[key];
+ } else {
+ json.attributes[key] = null;
+ }
+ }
+ }
+
+ // If compositeType is Azure, delete AWS fields from the json
+ if (
+ compositeType === TYPE_HOST_CATALOG_PLUGIN_AZURE &&
+ !azureFields.includes(key)
+ ) {
delete json.attributes[key];
}
}
@@ -98,7 +112,7 @@ export default class HostCatalogSerializer extends ApplicationSerializer {
// Delete unnecessary fields for static host-catalog
delete serialized.attributes;
delete serialized.secrets;
- delete serialized.worker_filter;
+
return serialized;
}
}
diff --git a/addons/api/tests/unit/serializers/host-catalog-test.js b/addons/api/tests/unit/serializers/host-catalog-test.js
index b8b1b7530..45b6ef55f 100644
--- a/addons/api/tests/unit/serializers/host-catalog-test.js
+++ b/addons/api/tests/unit/serializers/host-catalog-test.js
@@ -45,14 +45,9 @@ module('Unit | Serializer | host catalog', function (hooks) {
description: 'this is a Aws plugin host-catalog',
disable_credential_rotation: true,
worker_filter: 'workerfilter',
- // these are AWS fields and should be included
region: 'west',
access_key_id: 'foobars',
secret_access_key: 'testing',
- // these are Azure fields and should be excluded
- tenant_id: 'a1b2c3',
- client_id: 'a1b2c3',
- subscription_id: 'a1b2c3',
secret_id: 'a1b2c3',
secret_value: 'a1b2c3',
role_arn: 'test',
@@ -71,6 +66,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: true,
region: 'west',
+ role_arn: null,
+ role_external_id: null,
+ role_session_name: null,
+ role_tags: null,
},
secrets: {
access_key_id: 'foobars',
@@ -152,6 +151,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: true,
region: 'spain',
+ role_arn: null,
+ role_external_id: null,
+ role_session_name: null,
+ role_tags: null,
},
secrets: {
access_key_id: 'foobars',
@@ -225,6 +228,10 @@ module('Unit | Serializer | host catalog', function (hooks) {
attributes: {
disable_credential_rotation: false,
region: 'andorra',
+ role_arn: null,
+ role_external_id: null,
+ role_session_name: null,
+ role_tags: null,
},
};
assert.deepEqual(record.serialize(), expectedResult);
diff --git a/ui/admin/app/components/form/host-catalog/aws/index.hbs b/ui/admin/app/components/form/host-catalog/aws/index.hbs
index 1776cf8d0..f5c304e60 100644
--- a/ui/admin/app/components/form/host-catalog/aws/index.hbs
+++ b/ui/admin/app/components/form/host-catalog/aws/index.hbs
@@ -144,74 +144,51 @@
- {{#if @model.isNew}}
-
-
- {{t 'resources.host-catalog.types.credential.label'}}
-
-
- {{t 'resources.host-catalog.types.credential.help'}}
-
+
+
+ {{t 'resources.host-catalog.types.credential.label'}}
+
+
+ {{t 'resources.host-catalog.types.credential.help'}}
+
- {{#each this.credentials as |credentialType|}}
-
-
- {{t
- (concat
- 'resources.host-catalog.types.credential.'
- credentialType
- '.label'
- )
- }}
-
-
- {{t
- (concat
- 'resources.host-catalog.types.credential.'
- credentialType
- '.help'
- )
- }}
-
-
- {{/each}}
-
- {{else}}
-
-
- {{t 'resources.host-catalog.types.credential.label'}}
-
-
-
+
+ {{t
(concat
- 'resources.host-catalog.types.credential.'
- this.selectedCredentialType
- '.edit'
+ 'resources.host-catalog.types.credential.' credentialType '.label'
)
}}
- />
-
-
- {{/if}}
+
+
+ {{t
+ (concat
+ 'resources.host-catalog.types.credential.' credentialType '.help'
+ )
+ }}
+
+
+ {{/each}}
+
{{#if this.showDynamicCredentials}}
{{! role_arn }}
\ No newline at end of file
diff --git a/ui/admin/tests/acceptance/host-catalogs/update-test.js b/ui/admin/tests/acceptance/host-catalogs/update-test.js
index 7e87dbca2..b5840313a 100644
--- a/ui/admin/tests/acceptance/host-catalogs/update-test.js
+++ b/ui/admin/tests/acceptance/host-catalogs/update-test.js
@@ -4,7 +4,7 @@
*/
import { module, test } from 'qunit';
-import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
+import { visit, currentURL, click, fillIn, find } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { Response } from 'miragejs';
@@ -15,6 +15,10 @@ import {
//invalidateSession,
} from 'ember-simple-auth/test-support';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
+import {
+ TYPE_HOST_CATALOG_DYNAMIC,
+ TYPE_HOST_CATALOG_PLUGIN_AWS,
+} from 'api/models/host-catalog';
module('Acceptance | host-catalogs | update', function (hooks) {
setupApplicationTest(hooks);
@@ -35,6 +39,7 @@ module('Acceptance | host-catalogs | update', function (hooks) {
projectScope: null,
hostCatalogs: null,
hostCatalog: null,
+ AWSHostCatalogWithStaticCredential: null,
};
const NAME_INPUT_SELECTOR = '[name="name"]';
@@ -44,6 +49,8 @@ module('Acceptance | host-catalogs | update', function (hooks) {
const MODAL_DISCARD_BUTTON_SELECTOR =
'.rose-dialog-footer button:first-child';
const MODAL_CANCEL_BUTTON_SELECTOR = '.rose-dialog-footer button:last-child';
+ const CREDENTIAL_TYPE_SELECTOR =
+ '.dynamic-credential-selection input:checked';
hooks.beforeEach(function () {
// Generate resources
@@ -59,15 +66,43 @@ module('Acceptance | host-catalogs | update', function (hooks) {
instances.hostCatalog = this.server.create('host-catalog', {
scope: instances.scopes.project,
});
+ instances.AWSHostCatalogWithStaticCredential = this.server.create(
+ 'host-catalog',
+ {
+ scope: instances.scopes.project,
+ type: TYPE_HOST_CATALOG_DYNAMIC,
+ plugin: { name: TYPE_HOST_CATALOG_PLUGIN_AWS },
+ credentialType: 'static-credential',
+ },
+ );
// 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.hostCatalogs = `${urls.projectScope}/host-catalogs`;
urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`;
+ urls.AWSHostCatalogWithStaticCredential = `${urls.hostCatalogs}/${instances.AWSHostCatalogWithStaticCredential.id}`;
authenticateSession({});
});
+ test('can update static AWS credentials to Dynamic AWS credentials', async function (assert) {
+ await visit(urls.AWSHostCatalogWithStaticCredential);
+ await click(EDIT_BUTTON_SELECTOR, 'Activate edit mode');
+ assert.strictEqual(
+ find(CREDENTIAL_TYPE_SELECTOR).value,
+ 'static-credential',
+ );
+ await click('[value=dynamic-credential]', 'Dynamic Credential');
+ await fillIn('[name=role_arn]', 'arn:aws:iam');
+
+ await click(SAVE_BUTTON_SELECTOR);
+ // Check if the host catalog is updated
+ assert.strictEqual(
+ find(CREDENTIAL_TYPE_SELECTOR).value,
+ 'dynamic-credential',
+ );
+ });
+
test('can save changes to existing host catalog', async function (assert) {
assert.notEqual(instances.hostCatalog.name, 'random string');
await visit(urls.hostCatalog);