UI: add Database static role password rotation (#14268)

* Add UI feature allowing database role credential rotation

* Only show the 'rotate credentials' option for static roles

* rotate role path uses id for permissions

* Add rotate credentials button to show page on static role

* Mirage handlers for role for simple testing

* Add changelog

* lint rules

* fix lint

Co-authored-by: Bartek Marczak <bartek.marczak@gmail.com>
This commit is contained in:
Chelsea Shaw 2022-02-25 12:16:54 -06:00 committed by GitHub
parent 13f9de3845
commit 67ba021e36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 1 deletions

3
changelog/14268.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Allow static role credential rotation in Database secrets engines
```

View File

@ -47,4 +47,11 @@ export default ApplicationAdapter.extend({
queryRecord(store, type, query) {
return this.fetchByQuery(store, query);
},
rotateRoleCredentials(backend, id) {
return this.ajax(
`${this.buildURL()}/${encodeURIComponent(backend)}/rotate-role/${encodeURIComponent(id)}`,
'POST'
);
},
});

View File

@ -108,4 +108,17 @@ export default class DatabaseRoleEdit extends Component {
this.loading = false;
});
}
@action
rotateRoleCred(id) {
const backend = this.args.model?.backend;
let adapter = this.store.adapterFor('database/credential');
adapter
.rotateRoleCredentials(backend, id)
.then(() => {
this.flashMessages.success(`Success: Credentials for ${id} role were rotated`);
})
.catch((e) => {
this.flashMessages.danger(e.errors);
});
}
}

View File

@ -58,4 +58,17 @@ export default class DatabaseListItem extends Component {
this.flashMessages.danger(e.errors);
});
}
@action
rotateRoleCred(id) {
const { backend } = this.args.item;
let adapter = this.store.adapterFor('database/credential');
adapter
.rotateRoleCredentials(backend, id)
.then(() => {
this.flashMessages.success(`Success: Credentials for ${id} role were rotated`);
})
.catch((e) => {
this.flashMessages.danger(e.errors);
});
}
}

View File

@ -130,4 +130,6 @@ export default Model.extend({
canGetCredentials: alias('staticCredentialPath.canRead'),
databasePath: lazyCapabilities(apiPath`${'backend'}/config/${'database[0]'}`, 'backend', 'database'),
canUpdateDb: alias('databasePath.canUpdate'),
rotateRolePath: lazyCapabilities(apiPath`${'backend'}/rotate-role/${'id'}`, 'backend', 'id'),
canRotateRoleCredentials: alias('rotateRolePath.canUpdate'),
});

View File

@ -31,6 +31,11 @@
</ConfirmAction>
<div class="toolbar-separator"></div>
{{/if}}
{{#if (and @model.canRotateRoleCredentials (eq @model.type "static"))}}
<button type="button" class="toolbar-link" {{on "click" (fn this.rotateRoleCred @model.id)}}>
Rotate credentials
</button>
{{/if}}
{{#if @model.canGenerateCredentials}}
<button
type="button"

View File

@ -70,6 +70,13 @@
</LinkTo>
</li>
{{/if}}
{{#if (and @item.canRotateRoleCredentials (eq this.keyTypeValue "static"))}}
<li class="action">
<button type="button" class="link" {{on "click" (fn this.rotateRoleCred @item.id)}}>
Rotate credentials
</button>
</li>
{{/if}}
{{#if @item.canRotateRoot}}
<li class="action">
<button type="button" class="link" {{on "click" (fn this.rotateRootCred @item.id)}}>

27
ui/mirage/handlers/db.js Normal file
View File

@ -0,0 +1,27 @@
export default function (server) {
server.get('/database/static-roles', function () {
return {
data: { keys: ['dev-static', 'prod-static'] },
};
});
server.get('/database/static-roles/:rolename', function (db, req) {
if (req.params.rolename.includes('tester')) {
return new Response(400);
}
return {
data: {
rotation_statements: [
'{ "db": "admin", "roles": [{ "role": "readWrite" }, {"role": "read", "db": "foo"}] }',
],
db_name: 'connection',
username: 'alice',
rotation_period: '1h',
},
};
});
server.post('/database/rotate-role/:rolename', function () {
return new Response(204);
});
}

View File

@ -4,5 +4,6 @@ import base from './base';
import mfa from './mfa';
import activity from './activity';
import clients from './clients';
import db from './db';
export { base, activity, mfa, clients };
export { base, activity, mfa, clients, db };