mirror of https://github.com/status-im/consul.git
ui: Implement ACLs access based on ACLs (#9835)
Adds restrictions to everything within the ACLs (and nspaces) area based on your ACLs (including readonly views etc.)
This commit is contained in:
parent
6fe45c075f
commit
fa6687b7f4
|
@ -1,9 +1,6 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
// ACL ability covers all of the ACL things, like tokens, policies, roles and
|
||||
// auth methods and this therefore should not be deleted once we remove the on
|
||||
// legacy ACLs related classes
|
||||
export default class ACLAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
|
@ -13,4 +10,10 @@ export default class ACLAbility extends BaseAbility {
|
|||
get canRead() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canRead;
|
||||
}
|
||||
get canDuplicate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canWrite;
|
||||
}
|
||||
get canDelete() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && this.item.ID !== 'anonymous' && super.canWrite;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class AuthMethodAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
resource = 'acl';
|
||||
segmented = false;
|
||||
|
||||
get canRead() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canRead;
|
||||
}
|
||||
|
||||
get canCreate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canCreate;
|
||||
}
|
||||
|
||||
get canDelete() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canDelete;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,10 @@ export default class NspaceAbility extends BaseAbility {
|
|||
return this.canCreate;
|
||||
}
|
||||
|
||||
get canDelete() {
|
||||
return this.item.Name !== 'default' && super.canDelete;
|
||||
}
|
||||
|
||||
get canChoose() {
|
||||
return this.env.var('CONSUL_NSPACES_ENABLED') && this.nspaces.length > 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { typeOf } from 'consul-ui/helpers/policy/typeof';
|
||||
|
||||
export default class PolicyAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
resource = 'acl';
|
||||
segmented = false;
|
||||
|
||||
get canRead() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canRead;
|
||||
}
|
||||
|
||||
get canWrite() {
|
||||
return (
|
||||
this.env.var('CONSUL_ACLS_ENABLED') &&
|
||||
(typeof this.item === 'undefined' || typeOf([this.item]) !== 'policy-management') &&
|
||||
super.canRead
|
||||
);
|
||||
}
|
||||
|
||||
get canCreate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canCreate;
|
||||
}
|
||||
|
||||
get canDelete() {
|
||||
return (
|
||||
this.env.var('CONSUL_ACLS_ENABLED') &&
|
||||
(typeof this.item === 'undefined' || typeOf([this.item]) !== 'policy-management') &&
|
||||
super.canDelete
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class RoleAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
resource = 'acl';
|
||||
segmented = false;
|
||||
|
||||
get canRead() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canRead;
|
||||
}
|
||||
|
||||
get canCreate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canCreate;
|
||||
}
|
||||
|
||||
get canDelete() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canDelete;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import BaseAbility from './base';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
import { isLegacy } from 'consul-ui/helpers/token/is-legacy';
|
||||
import { isAnonymous } from 'consul-ui/helpers/token/is-anonymous';
|
||||
|
||||
export default class TokenAbility extends BaseAbility {
|
||||
@service('env') env;
|
||||
|
||||
resource = 'acl';
|
||||
segmented = false;
|
||||
|
||||
get canRead() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canRead;
|
||||
}
|
||||
|
||||
get canCreate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && super.canCreate;
|
||||
}
|
||||
|
||||
get canDelete() {
|
||||
return (
|
||||
this.env.var('CONSUL_ACLS_ENABLED') &&
|
||||
!isAnonymous([this.item]) &&
|
||||
this.item.AccessorID !== this.token.AccessorID &&
|
||||
super.canDelete
|
||||
);
|
||||
}
|
||||
|
||||
get canDuplicate() {
|
||||
return this.env.var('CONSUL_ACLS_ENABLED') && !isLegacy([this.item]) && super.canWrite;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
...attributes
|
||||
>
|
||||
{{yield}}
|
||||
{{#if (not disabled)}}
|
||||
<YieldSlot @name="create">{{yield}}</YieldSlot>
|
||||
<label class="type-text">
|
||||
<span><YieldSlot @name="label">{{yield}}</YieldSlot></span>
|
||||
|
@ -35,6 +36,7 @@
|
|||
</PowerSelect>
|
||||
</DataCollection>
|
||||
</label>
|
||||
{{/if}}
|
||||
{{#if (gt items.length 0)}}
|
||||
<YieldSlot @name="set">{{yield}}</YieldSlot>
|
||||
{{else}}
|
||||
|
|
|
@ -29,7 +29,13 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="menu" as |confirm send keypressClick|>
|
||||
<li role="none">
|
||||
<a data-test-edit role="menuitem" tabindex="-1" href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||
<a data-test-edit role="menuitem" tabindex="-1" href={{href-to 'dc.acls.edit' item.ID}}>
|
||||
{{#if (can "write acl" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
{{#if (eq item.ID token.SecretID) }}
|
||||
<li role="none">
|
||||
|
@ -87,10 +93,12 @@
|
|||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if (can "duplicate acl" item=item)}}
|
||||
<li role="none">
|
||||
<button role="menuitem" tabindex="-1" type="button" data-test-clone {{action @onclone item}}>Duplicate</button>
|
||||
</li>
|
||||
{{# if (not-eq item.ID 'anonymous') }}
|
||||
{{/if}}
|
||||
{{#if (can "delete acl" item=item)}}
|
||||
<li role="none" class="dangerous">
|
||||
<label for={{concat confirm 'delete'}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Delete</label>
|
||||
<div role="menu">
|
||||
|
|
|
@ -29,10 +29,14 @@ as |item|>
|
|||
<Actions as |Action|>
|
||||
<Action data-test-edit-action @href={{href-to 'dc.nspaces.edit' item.Name}}>
|
||||
<BlockSlot @name="label">
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{#if (not-eq item.Name 'default') }}
|
||||
{{#if (can "delete nspace" item=item)}}
|
||||
<Action data-test-delete-action @onclick={{action @ondelete item}} class="dangerous">
|
||||
<BlockSlot @name="label">
|
||||
Delete
|
||||
|
|
|
@ -35,14 +35,14 @@ as |item|>
|
|||
<Actions as |Action|>
|
||||
<Action data-test-edit-action @href={{href-to 'dc.acls.policies.edit' item.ID}}>
|
||||
<BlockSlot @name="label">
|
||||
{{#if (eq (policy/typeof item) 'policy-management')}}
|
||||
View
|
||||
{{else}}
|
||||
{{#if (can "write policy" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{#if (not-eq (policy/typeof item) 'policy-management')}}
|
||||
{{#if (can "delete policy" item=item)}}
|
||||
<Action data-test-delete-action @onclick={{action @ondelete item}} class="dangerous">
|
||||
<BlockSlot @name="label">
|
||||
Delete
|
||||
|
|
|
@ -19,9 +19,14 @@ as |item|>
|
|||
<Actions as |Action|>
|
||||
<Action data-test-edit-action @href={{href-to 'dc.acls.roles.edit' item.ID}}>
|
||||
<BlockSlot @name="label">
|
||||
{{#if (can "write role" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{#if (can "delete role" item=item)}}
|
||||
<Action data-test-delete-action @onclick={{action @ondelete item}} class="dangerous">
|
||||
<BlockSlot @name="label">
|
||||
Delete
|
||||
|
@ -42,6 +47,7 @@ as |item|>
|
|||
</Confirmation>
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{/if}}
|
||||
</Actions>
|
||||
</BlockSlot>
|
||||
</ListCollection>
|
|
@ -30,19 +30,25 @@ as |item|>
|
|||
</dl>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions" as |Actions|>
|
||||
|
||||
<Actions as |Action|>
|
||||
<Action data-test-edit-action @href={{href-to 'dc.acls.tokens.edit' item.AccessorID}}>
|
||||
<BlockSlot @name="label">
|
||||
{{#if (can "write token" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{#if (not (token/is-legacy item))}}
|
||||
{{#if (can "duplicate token" item=item)}}
|
||||
<Action data-test-clone-action @onclick={{action @onclone item}}>
|
||||
<BlockSlot @name="label">
|
||||
Duplicate
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (eq item.AccessorID token.AccessorID)}}
|
||||
<Action data-test-logout-action class="dangerous" @onclick={{action @onlogout item}}>
|
||||
<BlockSlot @name="label">
|
||||
|
@ -86,7 +92,9 @@ as |item|>
|
|||
</BlockSlot>
|
||||
</Action>
|
||||
{{/if}}
|
||||
{{#if (not (or (token/is-anonymous item) (eq item.AccessorID @token.AccessorID)))}}
|
||||
|
||||
|
||||
{{#if (can "delete token" item=item token=@token)}}
|
||||
<Action data-test-delete-action @onclick={{action @ondelete item}} class="dangerous">
|
||||
<BlockSlot @name="label">
|
||||
Delete
|
||||
|
@ -107,7 +115,8 @@ as |item|>
|
|||
</Confirmation>
|
||||
</BlockSlot>
|
||||
</Action>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</Actions>
|
||||
</BlockSlot>
|
||||
</ListCollection>
|
|
@ -1,5 +1,8 @@
|
|||
{{yield}}
|
||||
<fieldset ...attributes>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write policy" item=item)) "disabled"}}
|
||||
...attributes
|
||||
>
|
||||
{{#yield-slot name='template'}}
|
||||
{{else}}
|
||||
<header>
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
<ChildSelector ...attributes @repo={{repo}} @dc={{dc}} @nspace={{nspace}} @type="policy" @placeholder="Search for policy" @items={{items}}>
|
||||
<ChildSelector
|
||||
@disabled={{disabled}}
|
||||
@repo={{repo}}
|
||||
@dc={{dc}}
|
||||
@nspace={{nspace}}
|
||||
@type="policy"
|
||||
@placeholder="Search for policy"
|
||||
@items={{items}}
|
||||
...attributes
|
||||
>
|
||||
{{yield}}
|
||||
<BlockSlot @name="label">
|
||||
Apply an existing policy
|
||||
|
@ -112,6 +121,7 @@
|
|||
/>
|
||||
{{/if}}
|
||||
</label>
|
||||
{{#if (not disabled)}}
|
||||
<div>
|
||||
<ConfirmationDialog @message="Are you sure you want to remove this policy from this token?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
|
@ -127,6 +137,7 @@
|
|||
</BlockSlot>
|
||||
</ConfirmationDialog>
|
||||
</div>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</TabularDetails>
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
{{yield}}
|
||||
<fieldset class="role-form" data-test-role-form>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write role" item=item)) "disabled"}}
|
||||
class="role-form"
|
||||
data-test-role-form
|
||||
>
|
||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||
<span>Name</span>
|
||||
<input type="text" value={{item.Name}} name="role[Name]" autofocus="autofocus" oninput={{action 'change'}} />
|
||||
|
@ -21,6 +25,11 @@
|
|||
{{#yield-slot name='policy' params=(block-params item)}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
<PolicySelector @dc={{dc}} @nspace={{nspace}} @items={{item.Policies}} />
|
||||
<PolicySelector
|
||||
@disabled={{not (can "write role" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace={{nspace}}
|
||||
@items={{item.Policies}}
|
||||
/>
|
||||
{{/yield-slot}}
|
||||
</fieldset>
|
||||
|
|
|
@ -59,7 +59,7 @@ as |modal|>
|
|||
</BlockSlot>
|
||||
</ModalDialog>
|
||||
|
||||
<ChildSelector @repo={{repo}} @dc={{dc}} @nspace={{nspace}} @type="role" @placeholder="Search for role" @items={{items}}>
|
||||
<ChildSelector @disabled={{disabled}} @repo={{repo}} @dc={{dc}} @nspace={{nspace}} @type="role" @placeholder="Search for role" @items={{items}}>
|
||||
<BlockSlot @name="label">
|
||||
Apply an existing role
|
||||
</BlockSlot>
|
||||
|
@ -70,7 +70,6 @@ as |modal|>
|
|||
>
|
||||
<span>Create new role</span>
|
||||
</label>
|
||||
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="option" as |option|>
|
||||
{{option.Name}}
|
||||
|
@ -100,8 +99,15 @@ as |modal|>
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="menu" as |confirm send keypressClick|>
|
||||
<li role="none">
|
||||
<a role="menuitem" tabindex="-1" href={{href-to 'dc.acls.roles.edit' item.ID}}>Edit</a>
|
||||
<a role="menuitem" tabindex="-1" href={{href-to 'dc.acls.roles.edit' item.ID}}>
|
||||
{{#if (can "edit role" item=item)}}
|
||||
Edit
|
||||
{{else}}
|
||||
View
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
{{#if (not disabled)}}
|
||||
<li role="none" class="dangerous">
|
||||
<label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Remove</label>
|
||||
<div role="menu">
|
||||
|
@ -125,6 +131,7 @@ as |modal|>
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</BlockSlot>
|
||||
|
|
|
@ -128,42 +128,69 @@ export const routes = {
|
|||
},
|
||||
// ACLs
|
||||
acls: {
|
||||
_options: { path: '/acls' },
|
||||
_options: {
|
||||
path: '/acls',
|
||||
abilities: ['read acls'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:id' },
|
||||
},
|
||||
create: {
|
||||
_options: { path: '/create' },
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create acls'],
|
||||
},
|
||||
},
|
||||
policies: {
|
||||
_options: { path: '/policies' },
|
||||
_options: {
|
||||
path: '/policies',
|
||||
abilities: ['read policies'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:id' },
|
||||
},
|
||||
create: {
|
||||
_options: { path: '/create' },
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create policies'],
|
||||
},
|
||||
},
|
||||
},
|
||||
roles: {
|
||||
_options: { path: '/roles' },
|
||||
_options: {
|
||||
path: '/roles',
|
||||
abilities: ['read roles'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:id' },
|
||||
},
|
||||
create: {
|
||||
_options: { path: '/create' },
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create roles'],
|
||||
},
|
||||
},
|
||||
},
|
||||
tokens: {
|
||||
_options: { path: '/tokens' },
|
||||
_options: {
|
||||
path: '/tokens',
|
||||
abilities: ['read tokens'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:id' },
|
||||
},
|
||||
create: {
|
||||
_options: { path: '/create' },
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create tokens'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'auth-methods': {
|
||||
_options: { path: '/auth-methods' },
|
||||
_options: {
|
||||
path: '/auth-methods',
|
||||
abilities: ['read auth-methods'],
|
||||
},
|
||||
show: {
|
||||
_options: { path: '/show' },
|
||||
},
|
||||
|
@ -185,12 +212,18 @@ export const routes = {
|
|||
};
|
||||
if (env('CONSUL_NSPACES_ENABLED')) {
|
||||
routes.dc.nspaces = {
|
||||
_options: { path: '/namespaces' },
|
||||
_options: {
|
||||
path: '/namespaces',
|
||||
abilities: ['read nspaces'],
|
||||
},
|
||||
edit: {
|
||||
_options: { path: '/:name' },
|
||||
},
|
||||
create: {
|
||||
_options: { path: '/create' },
|
||||
_options: {
|
||||
path: '/create',
|
||||
abilities: ['create nspaces'],
|
||||
},
|
||||
},
|
||||
};
|
||||
routes.nspace = {
|
||||
|
|
|
@ -4,12 +4,10 @@ import CreatingRoute from 'consul-ui/mixins/creating-route';
|
|||
export default class CreateRoute extends Route.extend(CreatingRoute) {
|
||||
templateName = 'dc/nspaces/edit';
|
||||
|
||||
beforeModel() {
|
||||
async beforeModel() {
|
||||
// TODO: Update nspace CRUD to use Data components
|
||||
// we need to skip CreatingRoute.beforeModel here
|
||||
// TODO(octane): ideally we'd like to call Route.beforeModel
|
||||
// but its not clear how to do that with old ember
|
||||
// maybe it will be more normal with modern ember
|
||||
// up until now we haven't been calling super here anyway
|
||||
// so this is probably ok until we can skip a parent super
|
||||
// but still call Route.beforeModel
|
||||
return Route.prototype.beforeModel.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,19 @@ export default class BaseRoute extends Route {
|
|||
* Inspects a custom `abilities` array on the router for this route. Every
|
||||
* abililty needs to 'pass' for the route not to throw a 403 error. Anything
|
||||
* more complex then this (say ORs) should use a single ability and perform
|
||||
* the OR lgic in the test for the ability. Note, this ability check happens
|
||||
* the OR logic in the test for the ability. Note, this ability check happens
|
||||
* before any calls to the backend for this model/route.
|
||||
*/
|
||||
async beforeModel() {
|
||||
const abilities = get(routes, `${this.routeName}._options.abilities`) || [];
|
||||
// remove any references to index as it is the same as the root routeName
|
||||
const routeName = this.routeName
|
||||
.split('.')
|
||||
.filter(item => item !== 'index')
|
||||
.join('.');
|
||||
const abilities = get(routes, `${routeName}._options.abilities`) || [];
|
||||
if (abilities.length > 0) {
|
||||
if (!abilities.every(ability => this.permissions.can(ability))) {
|
||||
throw new HTTPError(403);
|
||||
throw new HTTPError('403');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@ const REQUIRED_PERMISSIONS = [
|
|||
Resource: 'operator',
|
||||
Access: 'write',
|
||||
},
|
||||
{
|
||||
Resource: 'operator',
|
||||
Access: 'read',
|
||||
},
|
||||
{
|
||||
Resource: 'service',
|
||||
Access: 'read',
|
||||
|
|
|
@ -70,6 +70,9 @@ html[data-route$='create'] main,
|
|||
html[data-route$='edit'] main {
|
||||
@extend %content-container-restricted;
|
||||
}
|
||||
html:not([data-route$='index']):not([data-route$='instances']) main {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
@media #{$--lt-spacious-page-header} {
|
||||
.actions button.copy-btn {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<form>
|
||||
<fieldset>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write acl" item=item)) "disabled"}}
|
||||
>
|
||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||
<span>Name</span>
|
||||
<Input @value={{item.Name}} @name="name" @autofocus="autofocus" />
|
||||
|
@ -25,12 +27,16 @@
|
|||
{{/if}}
|
||||
</fieldset>
|
||||
<div>
|
||||
{{#if create }}
|
||||
{{#if (and create (can "create acls")) }}
|
||||
{{! we only need to check for an empty name here as ember munges autofocus, once we have autofocus back revisit this}}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid (eq item.Name '')) 'disabled'}}>Save</button>{{else}}<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid (eq item.Name '')) 'disabled'}}>Save</button>
|
||||
{{else}}
|
||||
{{#if (can "write acl" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and (not create) (not-eq item.ID 'anonymous')) }}
|
||||
{{# if (and (not create) (can "delete acl" item=item) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this ACL token?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button type="button" data-test-delete class="type-delete" {{action confirm 'delete' item parent}}>Delete</button>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<CopyButton @value={{item.ID}} @name="token ID">
|
||||
Copy token ID
|
||||
</CopyButton>
|
||||
{{#if (can "duplicate acl" item=item)}}
|
||||
<button type="button" {{ action "clone" item }}>Clone token</button>
|
||||
<ConfirmationDialog @message="Are you sure you want to use this ACL token?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
|
@ -38,6 +39,7 @@
|
|||
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
||||
</BlockSlot>
|
||||
</ConfirmationDialog>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
|
|
|
@ -32,7 +32,9 @@ as |sort filters items|}}
|
|||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
{{#if (can "create acls")}}
|
||||
<a data-test-create href="{{href-to 'dc.acls.create'}}" class="type-create">Create</a>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
|
|
|
@ -7,14 +7,16 @@
|
|||
<TokenList @caption="Applied to the following tokens:" @items={{items}} />
|
||||
{{/if}}
|
||||
<div>
|
||||
{{#if create }}
|
||||
{{#if (and create (can "create tokens")) }}
|
||||
{{! we only need to check for an empty name here as ember munges autofocus, once we have autofocus back revisit this}}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid (eq item.Name '')) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
{{#if (can "write policy" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (not create) }}
|
||||
{{# if (and (not create) (can "delete policy" item=item) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Policy?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button type="button" data-test-delete class="type-delete" {{action confirm 'delete' item}}>Delete</button>
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
{{#if create }}
|
||||
New Policy
|
||||
{{else}}
|
||||
{{#if (eq (policy/typeof item) 'policy-management')}}
|
||||
View Policy
|
||||
{{else}}
|
||||
{{#if (can "write policy" item=item)}}
|
||||
Edit Policy
|
||||
{{else}}
|
||||
View Policy
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
|
|
|
@ -8,14 +8,16 @@
|
|||
<TokenList @caption="Tokens" @items={{items}} />
|
||||
{{/if}}
|
||||
<div>
|
||||
{{#if create }}
|
||||
{{#if (and create (can "create roles")) }}
|
||||
{{! we only need to check for an empty name here as ember munges autofocus, once we have autofocus back revisit this}}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid (eq item.Name '')) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
{{#if (can "write role" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (not create) }}
|
||||
{{# if (and (not create) (can "delete role" item=item) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Role?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button type="button" data-test-delete class="type-delete" {{action confirm 'delete' item}}>Delete</button>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<fieldset>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write token" item=item)) "disabled"}}
|
||||
>
|
||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||
<span>Name</span>
|
||||
<Input @value={{item.Description}} @name="name" @autofocus="autofocus" />
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<fieldset>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write token" item=item)) "disabled"}}
|
||||
>
|
||||
{{#if create }}
|
||||
<div class="type-toggle">
|
||||
<label>
|
||||
|
@ -15,9 +17,19 @@
|
|||
</fieldset>
|
||||
<fieldset id="roles">
|
||||
<h2>Roles</h2>
|
||||
<RoleSelector @dc={{dc}} @nspace={{nspace}} @items={{item.Roles}} />
|
||||
<RoleSelector
|
||||
@disabled={{not (can "write token" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace={{nspace}}
|
||||
@items={{item.Roles}}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset id="policies">
|
||||
<h2>Policies</h2>
|
||||
<PolicySelector @dc={{dc}} @nspace={{nspace}} @items={{item.Policies}} />
|
||||
<PolicySelector
|
||||
@disabled={{not (can "write token" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace={{nspace}}
|
||||
@items={{item.Policies}}
|
||||
/>
|
||||
</fieldset>
|
||||
|
|
|
@ -6,14 +6,17 @@
|
|||
{{/if}}
|
||||
{{!TODO: Make this into a slotted component}}
|
||||
<div>
|
||||
{{#if create }}
|
||||
{{#if (and create (can "create tokens")) }}
|
||||
{{! new tokens can be saved without you filling anything in, old tokens remain using isPristine }}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or (and (token/is-legacy item) item.isPristine) item.isInvalid) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
{{#if (can "write token" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and (not create) (not (token/is-anonymous item)) (not-eq item.AccessorID token.AccessorID) ) }}
|
||||
{{# if (and (not create) (can "delete token" item=item token=token) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Token?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button type="button" data-test-delete class="type-delete" {{action confirm 'delete' item}}>Delete</button>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</BlockSlot>
|
||||
</ConfirmationDialog>
|
||||
{{/if}}
|
||||
{{#if (not (token/is-legacy item))}}
|
||||
{{#if (can "duplicate token" item=item)}}
|
||||
<button data-test-clone type="button" {{ action "clone" item }}>Duplicate</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -48,7 +48,9 @@ as |sort filters items|}}
|
|||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
{{#if (can "create tokens")}}
|
||||
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0)}}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<form>
|
||||
<fieldset>
|
||||
<fieldset
|
||||
disabled={{if (not (can "write nspace" item=item)) "disabled"}}
|
||||
>
|
||||
{{#if create }}
|
||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||
<span>Name</span>
|
||||
|
@ -21,26 +23,47 @@
|
|||
<fieldset id="roles">
|
||||
<h2>Roles</h2>
|
||||
<p>
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
By adding roles to this namespaces, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following roles are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<RoleSelector @dc={{dc}} @nspace="default" @items={{item.ACLs.RoleDefaults}} />
|
||||
<RoleSelector
|
||||
@disabled={{not (can "write nspace" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace="default"
|
||||
@items={{item.ACLs.RoleDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset id="policies">
|
||||
<h2>Policies</h2>
|
||||
<p>
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
By adding policies to this namespaces, you will apply them to all tokens created within this namespace.
|
||||
{{else}}
|
||||
The following policies are applied to all tokens created within this namespace.
|
||||
{{/if}}
|
||||
</p>
|
||||
<PolicySelector @dc={{dc}} @nspace="default" @allowIdentity={{false}} @items={{item.ACLs.PolicyDefaults}} />
|
||||
<PolicySelector
|
||||
@disabled={{not (can "write nspace" item=item)}}
|
||||
@dc={{dc}}
|
||||
@nspace="default"
|
||||
@allowIdentity={{false}}
|
||||
@items={{item.ACLs.PolicyDefaults}}
|
||||
/>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
<div>
|
||||
{{#if create }}
|
||||
{{#if (and create (can "create nspaces")) }}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
{{else}}
|
||||
{{#if (can "write nspace" item=item)}}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and (not create) (not-eq item.Name 'default')) }}
|
||||
{{# if (and (not create) (can "delete nspace" item=item) ) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Namespace?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button data-test-delete type="button" class="type-delete" {{action confirm 'delete' item parent}}>Delete</button>
|
||||
|
|
Loading…
Reference in New Issue