Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/30275.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui/database: Adding password input field for creating a static role
```
13 changes: 12 additions & 1 deletion ui/app/models/database/role.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -135,6 +137,12 @@ export default class RoleModel extends Model {
})
skip_import_rotation;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would imagine that this field is actually not editType password. Instead, it should be sensitive—see example here. The difference is that password fields (only used once that I can see) is old and not really something we should support in form-fields. Imagine a password field as something you login with. Sensitive fields by contrast are masked as you input them but allow you to toggle the view which is helpful when trying to set something.

@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
Expand All @@ -156,6 +164,7 @@ export default class RoleModel extends Model {
'default_ttl',
'max_ttl',
'username',
'password',
'rotation_period',
'skip_import_rotation',
'creation_statements',
Expand All @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions ui/app/templates/components/database-role-setting-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
{{else}}
<ReadonlyFormField @attr={{attr}} @value={{get @model attr.name}} />
{{/if}}
{{else if (and (eq @mode "edit") (eq attr.name "password"))}}
<EnableInput
data-test-enable-field={{attr.name}}
class="field"
@attr={{attr}}
{{! password field is disabled on edit once password has been rotated }}
@disabled={{(not (eq (get @model "last_vault_rotation") undefined))}}
>
<FormField @attr={{attr}} @model={{@model}} />
</EnableInput>
{{else}}
<FormField data-test-field={{true}} @attr={{attr}} @model={{@model}} @modelValidations={{@modelValidations}} />
{{#if (and (eq attr.name "skip_import_rotation") this.isOverridden)}}
Expand Down
2 changes: 1 addition & 1 deletion ui/app/utils/model-helpers/database-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
};

Expand Down
20 changes: 11 additions & 9 deletions ui/lib/core/addon/components/enable-input.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@
<Input aria-label={{@label}} readonly class="input" @type="text" @value="**********" />
{{/if}}
</div>
<div class="align-self-end">
<Hds::Button
@text="Enable input"
@icon="edit"
@isIconOnly={{true}}
@color="tertiary"
{{on "click" (fn (mut this.enable))}}
/>
</div>
{{#unless @disabled}}
<div class="align-self-end">
<Hds::Button
@text="Enable input"
@icon="edit"
@isIconOnly={{true}}
@color="tertiary"
{{on "click" (fn (mut this.enable))}}
/>
</div>
{{/unless}}
</div>
{{/if}}
2 changes: 2 additions & 0 deletions ui/lib/core/addon/components/enable-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Args> {
Expand Down
19 changes: 15 additions & 4 deletions ui/tests/integration/components/database-role-edit-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
},
Expand All @@ -95,13 +97,18 @@ module('Integration | Component | database-role-edit', function (hooks) {
});

await render(hbs`<DatabaseRoleEdit @model={{this.modelStatic}} @mode="create"/>`);
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`<DatabaseRoleEdit @model={{this.modelStatic}} @mode="show"/>`);
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`<DatabaseRoleEdit @model={{this.modelStatic}} @mode="edit"/>`);
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) {
Expand All @@ -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`<DatabaseRoleEdit @model={{this.modelStatic}} @mode="show"/>`);
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`<DatabaseRoleEdit @model={{this.modelStatic}} @mode="edit"/>`);
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) {
Expand Down
Loading