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