Dan Rivera ceb9c6d062
UI: Add password field to static role creation page (#30275)
* adding password to static roles

* adding check for password rotation to disable password edit

* update field type and tests

* adding changelog

* replacing readonly with enableinput, added disable arg, test updates

* update to unless

* PR comments
2025-04-21 16:10:58 -04:00

224 lines
6.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Model, { attr } from '@ember-data/model';
import { service } from '@ember/service';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { getRoleFields } from 'vault/utils/model-helpers/database-helpers';
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
import { withModelValidations } from 'vault/decorators/model-validations';
const validations = {
name: [{ type: 'presence', message: 'Role name is required.' }],
database: [{ type: 'presence', message: 'Database is required.' }],
type: [{ type: 'presence', message: 'Type is required.' }],
username: [
{
validator(model) {
const { type, username } = model;
if (!type || type === 'dynamic') return true;
if (username) return true;
},
message: 'Username is required.',
},
],
};
@withModelValidations(validations)
export default class RoleModel extends Model {
@service version;
idPrefix = 'role/';
@attr('string', { readOnly: true }) backend;
@attr('string', { label: 'Role name' }) name;
@attr('array', {
label: 'Connection name',
editType: 'searchSelect',
fallbackComponent: 'string-list',
models: ['database/connection'],
selectLimit: 1,
onlyAllowExisting: true,
subText: 'The database connection for which credentials will be generated.',
})
database;
@attr('string', {
label: 'Type of role',
noDefault: true,
possibleValues: ['static', 'dynamic'],
})
type;
@attr({
editType: 'ttl',
defaultValue: '1h',
label: 'Generated credentialss Time-to-Live (TTL)',
helperTextDisabled: 'Vault will use a TTL of 1 hour.',
defaultShown: 'Engine default',
})
default_ttl;
@attr({
editType: 'ttl',
defaultValue: '24h',
label: 'Generated credentialss maximum Time-to-Live (Max TTL)',
helperTextDisabled: 'Vault will use a TTL of 24 hours.',
defaultShown: 'Engine default',
})
max_ttl;
@attr('string', { subText: 'The database username that this Vault role corresponds to.' }) username;
@attr({
editType: 'ttl',
defaultValue: '24h',
helperTextDisabled:
'Specifies the amount of time Vault should wait before rotating the password. The minimum is 5 seconds. Default is 24 hours.',
helperTextEnabled: 'Vault will rotate password after.',
})
rotation_period;
@attr('array', {
editType: 'stringArray',
})
creation_statements;
@attr('array', {
editType: 'stringArray',
defaultShown: 'Default',
})
revocation_statements;
@attr('array', {
editType: 'stringArray',
defaultShown: 'Default',
})
rotation_statements;
@attr('array', {
editType: 'stringArray',
defaultShown: 'Default',
})
rollback_statements;
@attr('array', {
editType: 'stringArray',
defaultShown: 'Default',
})
renew_statements;
@attr('string', {
editType: 'json',
allowReset: true,
theme: 'hashi short',
defaultShown: 'Default',
})
creation_statement;
@attr('string', {
editType: 'json',
allowReset: true,
theme: 'hashi short',
defaultShown: 'Default',
})
revocation_statement;
@attr('string', { readOnly: true }) last_vault_rotation;
// ENTERPRISE ONLY
@attr({
label: 'Rotate immediately',
editType: 'toggleButton',
helperTextEnabled: 'Vault will rotate the password for this static role on creation.',
helperTextDisabled: "Vault will not rotate this role's password on creation.",
isOppositeValue: true,
})
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
const fields = ['name', 'database', 'type'];
return expandAttributeMeta(this, fields);
}
get showFields() {
let fields = ['name', 'database', 'type'];
fields = fields.concat(getRoleFields(this.type)).concat(['creation_statements']);
// elasticsearch does not support revocation statements: https://developer.hashicorp.com/vault/api-docs/secret/databases/elasticdb#parameters-1
if (this.database[0] !== 'elasticsearch') {
fields = fields.concat(['revocation_statements']);
}
return expandAttributeMeta(this, fields);
}
get roleSettingAttrs() {
// logic for which get displayed is on DatabaseRoleSettingForm
let allRoleSettingFields = [
'default_ttl',
'max_ttl',
'username',
'password',
'rotation_period',
'skip_import_rotation',
'creation_statements',
'creation_statement', // for editType: JSON
'revocation_statements',
'revocation_statement', // only for MongoDB (editType: JSON)
'rotation_statements',
'rollback_statements',
'renew_statements',
];
// remove enterprise-only attrs if on community
if (!this.version.isEnterprise) {
allRoleSettingFields = allRoleSettingFields.filter(
(role) => !['skip_import_rotation', 'password'].includes(role)
);
}
return expandAttributeMeta(this, allRoleSettingFields);
}
/* CAPABILITIES */
// only used for secretPath
@attr('string', { readOnly: true }) path;
@lazyCapabilities(apiPath`${'backend'}/${'path'}/${'id'}`, 'backend', 'path', 'id') secretPath;
@lazyCapabilities(apiPath`${'backend'}/roles/+`, 'backend') dynamicPath;
@lazyCapabilities(apiPath`${'backend'}/static-roles/+`, 'backend') staticPath;
@lazyCapabilities(apiPath`${'backend'}/creds/${'id'}`, 'backend', 'id') credentialPath;
@lazyCapabilities(apiPath`${'backend'}/static-creds/${'id'}`, 'backend', 'id') staticCredentialPath;
@lazyCapabilities(apiPath`${'backend'}/config/${'database[0]'}`, 'backend', 'database') databasePath;
@lazyCapabilities(apiPath`${'backend'}/rotate-role/${'id'}`, 'backend', 'id') rotateRolePath;
get canEditRole() {
return this.secretPath.get('canUpdate');
}
get canDelete() {
return this.secretPath.get('canDelete');
}
get canCreateDynamic() {
return this.dynamicPath.get('canCreate');
}
get canCreateStatic() {
return this.staticPath.get('canCreate');
}
get canGenerateCredentials() {
return this.credentialPath.get('canRead');
}
get canGetCredentials() {
return this.staticCredentialPath.get('canRead');
}
get canUpdateDb() {
return this.databasePath.get('canUpdate');
}
get canRotateRoleCredentials() {
return this.rotateRolePath.get('canUpdate');
}
}