Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforcement view at MFA method level #15485

Merged
merged 2 commits into from
May 18, 2022
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
4 changes: 4 additions & 0 deletions ui/app/adapters/mfa-method.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export default class MfaMethodAdapter extends ApplicationAdapter {
return this.createOrUpdate(...arguments);
}

urlForDeleteRecord(id, modelName, snapshot) {
return this.buildURL(modelName, id, snapshot, 'POST');
}

query(store, type, query) {
const url = this.urlForQuery(query, type.modelName);
return this.ajax(url, 'GET', {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

export default class MfaMethodController extends Controller {
@service router;
@service flashMessages;

queryParams = ['tab'];
tab = 'config';

@action
async deleteMethod() {
try {
await this.model.method.destroyRecord();
this.flashMessages.success('MFA method deleted successfully deleted.');
this.router.transitionTo('vault.cluster.access.mfa.methods');
} catch (error) {
this.flashMessages.danger(`There was an error deleting this MFA method.`);
}
}
}
18 changes: 16 additions & 2 deletions ui/app/routes/vault/cluster/access/mfa/methods/method.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import Route from '@ember/routing/route';

import { hash } from 'rsvp';
Copy link
Contributor

Choose a reason for hiding this comment

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

Jordan might have some thoughts on Hash. I think there's a work around he mentioned, but I know we're under a time crunch so this is more of a if you're interested reach out to him about it.

export default class MfaMethodRoute extends Route {
model({ id }) {
return this.store.findRecord('mfa-method', id);
return hash({
method: this.store.findRecord('mfa-method', id).then((data) => data),
Copy link
Contributor

Choose a reason for hiding this comment

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

what if no record can be found? could that situation happen?

Copy link
Contributor

Choose a reason for hiding this comment

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

After looking into the documentation for hash further I think it is fine to use it in a route model hook if the resolved data from all of the promises is required for the route. hash returns a promise that will be rejected if any of the supplied promises are rejected which will then trigger the error event in the route.

With that said, I think a method can exist on it's own which makes the result of the promise to fetch enforcements not required to load the route. Since you are swallowing the error it shouldn't be a problem here but just wanted to provide a bit of background on this pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup hash acts similar to Promise all. But yeah since error is being swallowed, even if enforcements are not present, it will work fine.

enforcements: this.store
.query('mfa-login-enforcement', {})
.then((data) => {
let filteredEnforcements = data.filter((item) => {
let results = item.hasMany('mfa_methods').ids();
return results.includes(id);
});
return filteredEnforcements;
})
.catch(() => {
// Do nothing
}),
});
}
setupController(controller, model) {
controller.set('model', model);
Expand Down
32 changes: 32 additions & 0 deletions ui/app/templates/components/mfa/login-enforcement-list-item.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<LinkedBlock class="list-item-row" @params={{array "vault.cluster.access.mfa.enforcements.enforcement" @model.id}}>
<div class="level is-mobile">
<div class="level-left">
<div>
<Icon @name="lock" />
<span class="has-text-weight-semibold has-text-black">
{{@model.name}}
</span>
</div>
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu">
<ul class="menu-list">
<li>
<LinkTo @route="vault.cluster.access.mfa.enforcements.enforcement" @model={{@model.name}}>
Details
</LinkTo>
</li>
<li>
<LinkTo @route="vault.cluster.access.mfa.enforcements.enforcement.edit" @model={{@model.name}}>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
</div>
</div>
</div>
</LinkedBlock>
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
{{/each}}
{{else if (eq this.tab "methods")}}
{{#each this.model.mfa_methods as |method|}}
<MfaMethodListItem @model={{method}} />
<Mfa::MethodListItem @model={{method}} />
{{/each}}
{{/if}}

Expand Down
33 changes: 1 addition & 32 deletions ui/app/templates/vault/cluster/access/mfa/enforcements/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,7 @@

{{#if this.model.meta.total}}
{{#each this.model as |item|}}
<LinkedBlock class="list-item-row" @params={{array "vault.cluster.access.mfa.enforcements.enforcement" item.id}}>
<div class="level is-mobile">
<div class="level-left">
<div>
<Icon @name="lock" />
<span class="has-text-weight-semibold has-text-black">
{{item.name}}
</span>
</div>
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
<PopupMenu>
<nav class="menu">
<ul class="menu-list">
<li>
<LinkTo @route="vault.cluster.access.mfa.enforcements.enforcement" @model={{item.name}}>
Details
</LinkTo>
</li>
<li>
<LinkTo @route="vault.cluster.access.mfa.enforcements.enforcement.edit" @model={{item.name}}>
Edit
</LinkTo>
</li>
</ul>
</nav>
</PopupMenu>
</div>
</div>
</div>
</LinkedBlock>
<Mfa::LoginEnforcementListItem @model={{item}} />
{{/each}}
{{/if}}
{{#if (gt this.model.meta.lastPage 1)}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

{{#if this.model.meta.total}}
{{#each this.model as |item|}}
<MfaMethodListItem @model={{item}} />
<Mfa::MethodListItem @model={{item}} />
{{/each}}
{{/if}}
{{#if (gt this.model.meta.lastPage 1)}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<p.levelLeft>
<h1 class="title is-3">
Configure
{{this.model.name}}
{{this.model.method.name}}
MFA
</h1>
</p.levelLeft>
</PageHeader>

<Mfa::MethodForm
@model={{this.model}}
@model={{this.model.method}}
@hasActions={{true}}
@onSave={{transition-to "vault.cluster.access.mfa.methods.method" this.model.id}}
@onClose={{transition-to "vault.cluster.access.mfa.methods.method" this.model.id}}
@onSave={{transition-to "vault.cluster.access.mfa.methods.method" this.model.method.id}}
@onClose={{transition-to "vault.cluster.access.mfa.methods.method" this.model.method.id}}
/>
90 changes: 67 additions & 23 deletions ui/app/templates/vault/cluster/access/mfa/methods/method/index.hbs
Original file line number Diff line number Diff line change
@@ -1,34 +1,78 @@
<PageHeader as |p|>
<p.levelLeft>
<h1 class="title is-3">
<Icon @size="24" @name={{this.model.type}} />
{{this.model.name}}
<Icon @size="24" @name={{this.model.method.type}} />
{{this.model.method.name}}
</h1>
</p.levelLeft>
</PageHeader>

<Toolbar>
<ToolbarActions>
<ToolbarLink @params={{array "vault.cluster.access.mfa.methods.method.edit" this.model.id}}>
Edit
</ToolbarLink>
</ToolbarActions>
</Toolbar>
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs">
<ul>
<LinkTo @route="vault.cluster.access.mfa.methods.method" @query={{hash tab="config"}}>
Configuration
</LinkTo>
<LinkTo @route="vault.cluster.access.mfa.methods.method" @query={{hash tab="enforcements"}}>
Enforcements
</LinkTo>
</ul>
</nav>
</div>

<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#each this.model.attrs as |attr|}}
{{#if (eq attr.type "object")}}
<InfoTableRow
@alwaysRender={{not (is-empty-value (get @model attr.name))}}
@label={{or attr.options.label (to-label attr.name)}}
@value={{stringify (get @model attr.name)}}
{{#if (eq this.tab "config")}}
<Toolbar>
<ToolbarActions>
<ConfirmAction
@buttonClasses="toolbar-link"
@disabled={{not (is-empty this.model.enforcements)}}
@onConfirmAction={{this.deleteMethod}}
@confirmTitle="Are you sure?"
@confirmMessage="Deleting this MFA configuration is permanent, and it will no longer be available."
@confirmButtonText="Delete"
>
Delete
</ConfirmAction>
<ToolbarLink @params={{array "vault.cluster.access.mfa.methods.method.edit" this.model.method.id}}>
Edit
</ToolbarLink>
</ToolbarActions>
</Toolbar>
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#each this.model.method.attrs as |attr|}}
{{#if (eq attr.type "object")}}
<InfoTableRow
@alwaysRender={{not (is-empty-value (get this.model.method attr.name))}}
@label={{or attr.options.label (to-label attr.name)}}
@value={{stringify (get this.model.method attr.name)}}
/>
{{else}}
<InfoTableRow
@alwaysRender={{not (is-empty-value (get this.model.method attr.name))}}
@label={{or attr.options.label (to-label attr.name)}}
@value={{get this.model.method attr.name}}
/>
{{/if}}
{{/each}}
</div>
{{else if (eq this.tab "enforcements")}}
<Toolbar>
<ToolbarActions>
<ToolbarLink @type="add" @params={{array "vault.cluster.access.mfa.enforcements.create"}}>
New enforcement
</ToolbarLink>
</ToolbarActions>
</Toolbar>
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#if (is-empty this.model.enforcements)}}
<EmptyState
@title="No enforcements found."
@message="No enforcements are applied to this MFA method. Edit an existing enforcement or add a new one to get started."
/>
{{else}}
<InfoTableRow
@alwaysRender={{not (is-empty-value (get @model attr.name))}}
@label={{or attr.options.label (to-label attr.name)}}
@value={{get @model attr.name}}
/>
{{#each this.model.enforcements as |item|}}
<Mfa::LoginEnforcementListItem @model={{item}} />
{{/each}}
{{/if}}
{{/each}}
</div>
</div>
{{/if}}