diff --git a/changelog/30275.txt b/changelog/30275.txt new file mode 100644 index 0000000000..5c4f484a4d --- /dev/null +++ b/changelog/30275.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui/database: Adding password input field for creating a static role +``` \ No newline at end of file diff --git a/ui/app/models/database/role.js b/ui/app/models/database/role.js index dec14c8f4a..ac39819ebe 100644 --- a/ui/app/models/database/role.js +++ b/ui/app/models/database/role.js @@ -125,6 +125,8 @@ export default class RoleModel extends Model { }) revocation_statement; + @attr('string', { readOnly: true }) last_vault_rotation; + // ENTERPRISE ONLY @attr({ label: 'Rotate immediately', @@ -135,6 +137,12 @@ export default class RoleModel extends Model { }) skip_import_rotation; + @attr('string', { + sensitive: true, + subText: 'The database password that this Vault role corresponds to.', + }) + password; + /* FIELD ATTRIBUTES */ get fieldAttrs() { // Main fields on edit/create form @@ -156,6 +164,7 @@ export default class RoleModel extends Model { 'default_ttl', 'max_ttl', 'username', + 'password', 'rotation_period', 'skip_import_rotation', 'creation_statements', @@ -169,7 +178,9 @@ export default class RoleModel extends Model { // remove enterprise-only attrs if on community if (!this.version.isEnterprise) { - allRoleSettingFields = allRoleSettingFields.filter((role) => role !== 'skip_import_rotation'); + allRoleSettingFields = allRoleSettingFields.filter( + (role) => !['skip_import_rotation', 'password'].includes(role) + ); } return expandAttributeMeta(this, allRoleSettingFields); diff --git a/ui/app/templates/components/database-role-setting-form.hbs b/ui/app/templates/components/database-role-setting-form.hbs index 2e8022c64a..678f5c8e54 100644 --- a/ui/app/templates/components/database-role-setting-form.hbs +++ b/ui/app/templates/components/database-role-setting-form.hbs @@ -14,6 +14,16 @@ {{else}} {{/if}} + {{else if (and (eq @mode "edit") (eq attr.name "password"))}} + + + {{else}} {{#if (and (eq attr.name "skip_import_rotation") this.isOverridden)}} diff --git a/ui/app/utils/model-helpers/database-helpers.js b/ui/app/utils/model-helpers/database-helpers.js index 59697afcfe..ad436016d3 100644 --- a/ui/app/utils/model-helpers/database-helpers.js +++ b/ui/app/utils/model-helpers/database-helpers.js @@ -197,7 +197,7 @@ export const AVAILABLE_PLUGIN_TYPES = [ ]; export const ROLE_FIELDS = { - static: ['username', 'rotation_period', 'skip_import_rotation'], + static: ['username', 'password', 'rotation_period', 'skip_import_rotation'], dynamic: ['default_ttl', 'max_ttl'], }; diff --git a/ui/lib/core/addon/components/enable-input.hbs b/ui/lib/core/addon/components/enable-input.hbs index 00574fa1d6..15f46095df 100644 --- a/ui/lib/core/addon/components/enable-input.hbs +++ b/ui/lib/core/addon/components/enable-input.hbs @@ -14,14 +14,16 @@ {{/if}} -
- -
+ {{#unless @disabled}} +
+ +
+ {{/unless}} {{/if}} \ No newline at end of file diff --git a/ui/lib/core/addon/components/enable-input.ts b/ui/lib/core/addon/components/enable-input.ts index ee5c8b3314..8937adedcc 100644 --- a/ui/lib/core/addon/components/enable-input.ts +++ b/ui/lib/core/addon/components/enable-input.ts @@ -9,6 +9,7 @@ import { tracked } from '@glimmer/tracking'; interface Args { attr?: AttrData; label?: string; + disabled?: boolean; // specifically used for disabling on edit } interface AttrData { name: string; // required if @attr is passed @@ -38,6 +39,7 @@ interface AttrData { * @param {object} [attr] - used to generate label for `ReadonlyFormField`, `name` key is required. Can be an attribute from a model exported with expandAttributeMeta. * @param {string} [label] - required if no attr passed. Used to ensure a11y conformance for the readonly input. + * @param {boolean} [disabled=false] - to be used in specific scenarios where a user can visually see but not interact with the input field. ie. disabling a field on edit */ export default class EnableInputComponent extends Component { diff --git a/ui/tests/integration/components/database-role-edit-test.js b/ui/tests/integration/components/database-role-edit-test.js index b88ab3f01b..dab207248b 100644 --- a/ui/tests/integration/components/database-role-edit-test.js +++ b/ui/tests/integration/components/database-role-edit-test.js @@ -10,6 +10,7 @@ import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { capabilitiesStub } from 'vault/tests/helpers/stubs'; import { click, fillIn } from '@ember/test-helpers'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; module('Integration | Component | database-role-edit', function (hooks) { setupRenderingTest(hooks); @@ -87,6 +88,7 @@ module('Integration | Component | database-role-edit', function (hooks) { { path: 'static-roles', username: 'staticTestUser', + password: 'testPassword', rotation_period: '172800s', // 2 days in seconds skip_import_rotation: true, }, @@ -95,13 +97,18 @@ module('Integration | Component | database-role-edit', function (hooks) { }); await render(hbs``); - await fillIn('[data-test-ttl-value="Rotation period"]', '2'); - await click('[data-test-toggle-input="toggle-skip_import_rotation"]'); + await fillIn(GENERAL.ttl.input('Rotation period'), '2'); + await click(GENERAL.toggleInput('toggle-skip_import_rotation')); + await fillIn(GENERAL.inputByAttr('password'), 'testPassword'); // fill in password field await click('[data-test-secret-save]'); await render(hbs``); - assert.dom('[data-test-value-div="Rotate immediately"]').containsText('No'); + assert.dom(GENERAL.infoRowValue('Rotate immediately')).containsText('No'); + assert.dom(GENERAL.infoRowValue('password')).doesNotExist(); // verify password field doesn't show on details view + + await render(hbs``); + assert.dom(GENERAL.icon('edit')).exists(); // verify password field is enabled for edit & enable button is rendered bc role hasn't been rotated }); test('enterprise: it should successfully create user that does rotate immediately & verify warning modal pops up', async function (assert) { @@ -115,7 +122,11 @@ module('Integration | Component | database-role-edit', function (hooks) { await click('[data-test-issuer-save]'); // click continue button on modal await render(hbs``); - assert.dom('[data-test-value-div="Rotate immediately"]').containsText('Yes'); + assert.dom(GENERAL.infoRowValue('Rotate immediately')).containsText('Yes'); + + this.modelStatic.last_vault_rotation = '2025-04-21T12:51:59.063124-04:00'; // Setting a sample rotation time here to simulate what returns from BE after rotation + await render(hbs``); + assert.dom(GENERAL.icon('edit')).doesNotExist(); // verify password field is disabled for edit & enable button isn't rendered bc role has already been rotated }); test('it should show Get credentials button when a user has the correct policy', async function (assert) {