From e38d3afa582ba12c0447e97af1f653e4564c8760 Mon Sep 17 00:00:00 2001 From: Dharini Date: Thu, 28 May 2026 17:12:06 -0700 Subject: [PATCH] Chore migrate credential library storage bucket (#3282) --- .../mirage/factories/credential-library.js | 3 + .../form/auth-method/ldap/index.hbs | 2 - .../form/auth-method/oidc/index.hbs | 5 -- .../vault-ssh-certificate/index.hbs | 64 +++++++++--------- .../components/form/field/key-value/index.hbs | 5 +- .../form/host-catalog/aws/index.hbs | 1 - .../components/form/host-set/aws/index.hbs | 2 - .../components/form/host-set/azure/index.hbs | 1 - .../components/form/host-set/gcp/index.hbs | 2 - .../form/storage-bucket/aws/index.hbs | 65 ++++++++++--------- .../components/form/storage-bucket/index.hbs | 1 + .../credential-libraries/index.js | 11 ++++ .../scopes/scope/storage-buckets/index.js | 38 ++++++++++- .../credential-libraries/new.js | 3 +- .../scopes/scope/storage-buckets/new.js | 5 +- .../storage-buckets/storage-bucket/index.hbs | 1 + .../credential-library/create-test.js | 1 - .../credential-library/selectors.js | 7 +- .../credential-library/update-test.js | 1 - .../acceptance/storage-buckets/create-test.js | 12 ++++ .../acceptance/storage-buckets/selectors.js | 7 ++ .../components/form/field/key-value-test.js | 42 ++++++++---- 22 files changed, 177 insertions(+), 102 deletions(-) diff --git a/addons/api/app/mirage/factories/credential-library.js b/addons/api/app/mirage/factories/credential-library.js index 222e26c0a..a0e7a96f1 100644 --- a/addons/api/app/mirage/factories/credential-library.js +++ b/addons/api/app/mirage/factories/credential-library.js @@ -48,6 +48,9 @@ export default factory.extend({ critical_options: { [faker.word.noun()]: faker.word.words(2), }, + extensions: { + [faker.word.noun()]: faker.word.words(2), + }, }; case TYPE_CREDENTIAL_LIBRARY_VAULT_LDAP: return { diff --git a/ui/admin/app/components/form/auth-method/ldap/index.hbs b/ui/admin/app/components/form/auth-method/ldap/index.hbs index a2ade405b..32fc75f68 100644 --- a/ui/admin/app/components/form/auth-method/ldap/index.hbs +++ b/ui/admin/app/components/form/auth-method/ldap/index.hbs @@ -89,7 +89,6 @@ @data={{@model.certificates}} @legend={{t 'resources.auth-method.form.certificates.label'}} @helperText={{t 'resources.auth-method.form.certificates.help'}} - @errors={{@model.errors.certificates}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -406,7 +405,6 @@ @data={{@model.account_attribute_maps}} @legend={{t 'resources.auth-method.form.account_attribute_maps.label'}} @helperText={{t 'resources.auth-method.form.account_attribute_maps.help'}} - @errors={{@model.errors.account_attribute_maps}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index 912b81836..8bb777a49 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -154,7 +154,6 @@ @data={{@model.signing_algorithms}} @legend={{t 'form.signing_algorithms.label'}} @helperText={{t 'form.signing_algorithms.help'}} - @errors={{@model.errors.signing_algorithms}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -191,7 +190,6 @@ @data={{@model.allowed_audiences}} @legend={{t 'form.allowed_audiences.label'}} @helperText={{t 'form.allowed_audiences.help'}} - @errors={{@model.errors.allowed_audiences}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -217,7 +215,6 @@ @data={{@model.claims_scopes}} @legend={{t 'form.claims_scopes.label'}} @helperText={{t 'form.claims_scopes.help'}} - @errors={{@model.errors.claims_scopes}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -243,7 +240,6 @@ @data={{@model.account_claim_maps}} @legend={{t 'form.account_claim_maps.label'}} @helperText={{t 'form.account_claim_maps.help'}} - @errors={{@model.errors.account_claim_maps}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -288,7 +284,6 @@ @data={{@model.idp_ca_certs}} @legend={{t 'form.idp_ca_certs.label'}} @helperText={{t 'form.idp_ca_certs.help'}} - @errors={{@model.errors.idp_ca_certs}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs b/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs index 562fdc664..ee1e2c963 100644 --- a/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs +++ b/ui/admin/app/components/form/credential-library/vault-ssh-certificate/index.hbs @@ -232,7 +232,6 @@ @data={{@model.critical_options}} @legend={{t 'resources.credential-library.form.critical_options.label'}} @helperText={{t 'resources.credential-library.form.critical_options.help'}} - @errors={{@model.errors.critical_options}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -262,43 +261,40 @@ - - <:fieldset as |F|> - - {{t 'resources.credential-library.form.extensions.label'}} - - - {{t 'resources.credential-library.form.extensions.help'}} - + <:row as |R|> + + {{t 'form.key.label'}} + + - {{#if @model.errors.extensions}} - - {{#each @model.errors.extensions as |error|}} - {{error.message}} - {{/each}} - + + {{t 'form.value.label'}} + + + + {{#if R.canDelete}} + {{/if}} - - <:field as |F|> - - <:key as |K|> - - - <:value as |V|> - - - - - - + + {{#if (can 'save model' @model)}} - {{#if @errors}} + + {{#if (get @model.errors @name)}} - {{#each @errors as |error|}} + {{#each (get @model.errors @name) as |error|}} {{error.message}} {{/each}} 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 ac7bc46e7..10085d870 100644 --- a/ui/admin/app/components/form/host-catalog/aws/index.hbs +++ b/ui/admin/app/components/form/host-catalog/aws/index.hbs @@ -279,7 +279,6 @@ @data={{@model.role_tags}} @legend={{t 'resources.storage-bucket.form.role_tags.label'}} @helperText={{t 'resources.storage-bucket.form.role_tags.help'}} - @errors={{@model.errors.role_tags}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/host-set/aws/index.hbs b/ui/admin/app/components/form/host-set/aws/index.hbs index fbb571a98..0d69e486e 100644 --- a/ui/admin/app/components/form/host-set/aws/index.hbs +++ b/ui/admin/app/components/form/host-set/aws/index.hbs @@ -69,7 +69,6 @@ @data={{@model.preferred_endpoints}} @legend={{t 'resources.host-set.form.preferred_endpoints.label'}} @helperText={{t 'resources.host-set.form.preferred_endpoints.help'}} - @errors={{@model.errors.preferred_endpoints}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -94,7 +93,6 @@ @data={{@model.filters}} @legend={{t 'resources.host-set.form.filter.label'}} @helperText={{t 'resources.host-set.form.filter.aws.help'}} - @errors={{@model.errors.filters}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/host-set/azure/index.hbs b/ui/admin/app/components/form/host-set/azure/index.hbs index a065889f3..849800c6f 100644 --- a/ui/admin/app/components/form/host-set/azure/index.hbs +++ b/ui/admin/app/components/form/host-set/azure/index.hbs @@ -69,7 +69,6 @@ @data={{@model.preferred_endpoints}} @legend={{t 'resources.host-set.form.preferred_endpoints.label'}} @helperText={{t 'resources.host-set.form.preferred_endpoints.help'}} - @errors={{@model.errors.preferred_endpoints}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/host-set/gcp/index.hbs b/ui/admin/app/components/form/host-set/gcp/index.hbs index a5e5661f8..8b63b07b1 100644 --- a/ui/admin/app/components/form/host-set/gcp/index.hbs +++ b/ui/admin/app/components/form/host-set/gcp/index.hbs @@ -68,7 +68,6 @@ @data={{@model.preferred_endpoints}} @legend={{t 'resources.host-set.form.preferred_endpoints.label'}} @helperText={{t 'resources.host-set.form.preferred_endpoints.help'}} - @errors={{@model.errors.preferred_endpoints}} @disabled={{form.disabled}} @isOptional={{true}} > @@ -93,7 +92,6 @@ @data={{@model.filters}} @legend={{t 'resources.host-set.form.filter.label'}} @helperText={{t 'resources.host-set.form.filter.gcp.help'}} - @errors={{@model.errors.filters}} @disabled={{form.disabled}} @isOptional={{true}} > diff --git a/ui/admin/app/components/form/storage-bucket/aws/index.hbs b/ui/admin/app/components/form/storage-bucket/aws/index.hbs index 2fa206ef9..71ec0d282 100644 --- a/ui/admin/app/components/form/storage-bucket/aws/index.hbs +++ b/ui/admin/app/components/form/storage-bucket/aws/index.hbs @@ -4,6 +4,7 @@ }} {{! role_tags }} - - <:fieldset as |F|> - - {{t 'resources.storage-bucket.form.role_tags.label'}} - - - {{t 'resources.storage-bucket.form.role_tags.help'}} - - - {{#if @model.errors.role_tags}} - - {{#each @model.errors.role_tags as |error|}} - {{error.message}} - {{/each}} - + <:row as |R|> + + + + + + + {{#if R.canDelete}} + {{/if}} - - <:field as |F|> - - <:key as |K|> - - - <:value as |V|> - - - - - + + {{/if}} {{! worker_filter }} diff --git a/ui/admin/app/components/form/storage-bucket/index.hbs b/ui/admin/app/components/form/storage-bucket/index.hbs index 2456aaf0d..078dc2177 100644 --- a/ui/admin/app/components/form/storage-bucket/index.hbs +++ b/ui/admin/app/components/form/storage-bucket/index.hbs @@ -5,6 +5,7 @@ {{#if @model.type}} item.key?.trim()); } + + if (credentialLibrary.extensions) { + credentialLibrary.extensions = credentialLibrary.extensions.filter( + (item) => item.key?.trim(), + ); + } + await credentialLibrary.save(); if (this.abilities.can('read model', credentialLibrary)) { await this.router.transitionTo( @@ -130,6 +137,10 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari if (credentialLibrary.critical_options?.length === 0) { credentialLibrary.critical_options = [{ key: '', value: '' }]; } + + if (credentialLibrary.extensions?.length === 0) { + credentialLibrary.extensions = [{ key: '', value: '' }]; + } } /** diff --git a/ui/admin/app/controllers/scopes/scope/storage-buckets/index.js b/ui/admin/app/controllers/scopes/scope/storage-buckets/index.js index b1265646c..7e6b7b8f0 100644 --- a/ui/admin/app/controllers/scopes/scope/storage-buckets/index.js +++ b/ui/admin/app/controllers/scopes/scope/storage-buckets/index.js @@ -9,7 +9,10 @@ import { action } from '@ember/object'; import { loading } from 'ember-loading'; import { confirm } from 'core/decorators/confirm'; import { notifySuccess, notifyError } from 'core/decorators/notify'; -import { TYPE_CREDENTIAL_STATIC } from 'api/models/storage-bucket'; +import { + TYPE_CREDENTIAL_STATIC, + TYPE_CREDENTIAL_DYNAMIC, +} from 'api/models/storage-bucket'; export default class ScopesScopeStorageBucketsIndexController extends Controller { // =services @@ -45,6 +48,17 @@ export default class ScopesScopeStorageBucketsIndexController extends Controller return this.intl.t(description, { resource }); } + /** + * Generates one empty role_tags row for storage bucket when the field is empty. + * @param {storageBucketModel} bucket + */ + generateAwsRoleTagsRowIfEmpty(bucket) { + // If role_tags is an empty array, add an empty row + if (!bucket.role_tags || bucket.role_tags.length === 0) { + bucket.role_tags = [{ key: '', value: '' }]; + } + } + // =actions /** @@ -60,6 +74,13 @@ export default class ScopesScopeStorageBucketsIndexController extends Controller if (!storageBucket.role_arn) { storageBucket.credentialType = TYPE_CREDENTIAL_STATIC; } + + if (storageBucket.role_tags) { + storageBucket.role_tags = storageBucket.role_tags.filter((item) => + item.key?.trim(), + ); + } + await storageBucket.save(); await this.router.transitionTo( 'scopes.scope.storage-buckets.storage-bucket', @@ -105,6 +126,10 @@ export default class ScopesScopeStorageBucketsIndexController extends Controller @action changeCredentialType(storageBucket, credentialType) { storageBucket.credentialType = credentialType; + + if (credentialType === TYPE_CREDENTIAL_DYNAMIC) { + this.generateAwsRoleTagsRowIfEmpty(storageBucket); + } } /** @@ -117,4 +142,15 @@ export default class ScopesScopeStorageBucketsIndexController extends Controller queryParams: { compositeType: pluginType }, }); } + + @action + edit(storageBucket) { + if (storageBucket.role_tags) { + storageBucket.role_tags = structuredClone(storageBucket.role_tags); + } + + if (storageBucket.credentialType === TYPE_CREDENTIAL_DYNAMIC) { + this.generateAwsRoleTagsRowIfEmpty(storageBucket); + } + } } diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js index d2dbaebd1..85b603f19 100644 --- a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js @@ -77,9 +77,10 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari name, description, }; - // if the type is vault-ssh-certificate, initialize critical_options with an empty row + // if the type is vault-ssh-certificate, initialize critical_options and extensions with an empty row if (resolvedType === TYPE_CREDENTIAL_LIBRARY_VAULT_SSH_CERTIFICATE) { attrs.critical_options = [{ key: '', value: '' }]; + attrs.extensions = [{ key: '', value: '' }]; } return this.store.createRecord('credential-library', attrs); diff --git a/ui/admin/app/routes/scopes/scope/storage-buckets/new.js b/ui/admin/app/routes/scopes/scope/storage-buckets/new.js index 26bc24b88..3823e4c86 100644 --- a/ui/admin/app/routes/scopes/scope/storage-buckets/new.js +++ b/ui/admin/app/routes/scopes/scope/storage-buckets/new.js @@ -53,7 +53,8 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route { bucket_name, bucket_prefix, region, - worker_filter; + worker_filter, + role_tags; if (this.currentModel?.isNew) { ({ @@ -64,6 +65,7 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route { bucket_prefix, region, worker_filter, + role_tags, } = this.currentModel); this.currentModel.rollbackAttributes(); } @@ -78,6 +80,7 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route { bucket_prefix, region, worker_filter, + role_tags: role_tags || [{ key: '', value: '' }], }); return record; } diff --git a/ui/admin/app/templates/scopes/scope/storage-buckets/storage-bucket/index.hbs b/ui/admin/app/templates/scopes/scope/storage-buckets/storage-bucket/index.hbs index 5f4f2f75c..7eab0c9d2 100644 --- a/ui/admin/app/templates/scopes/scope/storage-buckets/storage-bucket/index.hbs +++ b/ui/admin/app/templates/scopes/scope/storage-buckets/storage-bucket/index.hbs @@ -42,6 +42,7 @@ + <:row as |R|> @@ -221,7 +231,7 @@ module('Integration | Component | form/field/key-value', function (hooks) { {{#if R.canDelete}} - + {{/if}} <:footer as |F|> @@ -235,9 +245,13 @@ module('Integration | Component | form/field/key-value', function (hooks) { .hasText('Value must be unique'); // Without errors - this.set('errors', undefined); + this.set('model', { ...model, errors: {} }); await render(hbs` - + <:row as |R|> @@ -246,7 +260,7 @@ module('Integration | Component | form/field/key-value', function (hooks) { {{#if R.canDelete}} - + {{/if}} <:footer as |F|> @@ -322,6 +336,12 @@ module('Integration | Component | form/field/key-value', function (hooks) { test('custom handlers take precedence over default handlers', async function (assert) { const model = { tags: [{ key: '', value: '' }], + errors: { + tags: [ + { message: 'Key is required' }, + { message: 'Value must be unique' }, + ], + }, }; this.set('model', model);