diff --git a/ui/packages/consul-ui/README.md b/ui/packages/consul-ui/README.md index 31c851ccf958..09ad1bdcd184 100644 --- a/ui/packages/consul-ui/README.md +++ b/ui/packages/consul-ui/README.md @@ -128,6 +128,7 @@ token/secret. | `CONSUL_EXPOSED_COUNT` | (random) | Configure the number of exposed paths that the API returns. | | `CONSUL_CHECK_COUNT` | (random) | Configure the number of health checks that the API returns. | | `CONSUL_OIDC_PROVIDER_COUNT` | (random) | Configure the number of OIDC providers that the API returns. | +| `CONSUL_RESOURCE__` | true | Configure permissions e.g `CONSUL_RESOURCE_INTENTION_WRITE=false`. | | `DEBUG_ROUTES_ENDPOINT` | undefined | When using the window.Routes() debug utility ([see utility functions](#browser-debug-utility-functions)), use a URL to pass the route DSL to. %s in the URL will be replaced with the route DSL - http://url.com?routes=%s | See `./mock-api` for more details. diff --git a/ui/packages/consul-ui/app/abilities/base.js b/ui/packages/consul-ui/app/abilities/base.js index 7d1a7eeca6cf..85570dd67f12 100644 --- a/ui/packages/consul-ui/app/abilities/base.js +++ b/ui/packages/consul-ui/app/abilities/base.js @@ -14,23 +14,27 @@ export default class BaseAbility extends Ability { return this.permissions.generate(this.resource, action); } - get canCreate() { - return this.permissions.has(this.generate(ACCESS_WRITE)); + get canRead() { + return this.permissions.has(this.generate(ACCESS_READ)); } - get canDelete() { + get canList() { + return this.permissions.has(this.generate(ACCESS_LIST)); + } + + get canWrite() { return this.permissions.has(this.generate(ACCESS_WRITE)); } - get canRead() { - return this.permissions.has(this.generate(ACCESS_READ)); + get canCreate() { + return this.canWrite; } - get canList() { - return this.permissions.has(this.generate(ACCESS_LIST)); + get canDelete() { + return this.canWrite; } get canUpdate() { - return this.permissions.has(this.generate(ACCESS_WRITE)); + return this.canWrite; } } diff --git a/ui/packages/consul-ui/app/abilities/intention.js b/ui/packages/consul-ui/app/abilities/intention.js index 62f2579a2d10..35551e670aac 100644 --- a/ui/packages/consul-ui/app/abilities/intention.js +++ b/ui/packages/consul-ui/app/abilities/intention.js @@ -2,4 +2,8 @@ import BaseAbility from './base'; export default class IntentionAbility extends BaseAbility { resource = 'intention'; + + get canWrite() { + return super.canWrite && (typeof this.item === 'undefined' || this.item.IsEditable); + } } diff --git a/ui/packages/consul-ui/app/abilities/session.js b/ui/packages/consul-ui/app/abilities/session.js new file mode 100644 index 000000000000..b37b3c5ccc74 --- /dev/null +++ b/ui/packages/consul-ui/app/abilities/session.js @@ -0,0 +1,5 @@ +import BaseAbility from './base'; + +export default class SessionAbility extends BaseAbility { + resource = 'session'; +} diff --git a/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs b/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs index 788faccc5e1c..4860bf509592 100644 --- a/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/intention/form/index.hbs @@ -32,7 +32,7 @@ as |api|> {{#let api.data as |item|}} - {{#if item.IsEditable}} +{{#if (can 'write intention' item=item)}} {{#if this.warn}} {{#let (changeset-get item 'Action') as |newAction|}} diff --git a/ui/packages/consul-ui/app/components/consul/intention/list/table/index.hbs b/ui/packages/consul-ui/app/components/consul/intention/list/table/index.hbs index faf35be4a45c..d54b8b0cd409 100644 --- a/ui/packages/consul-ui/app/components/consul/intention/list/table/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/intention/list/table/index.hbs @@ -59,7 +59,7 @@ as |item index|> More - {{#if item.IsEditable}} + {{#if (can "write intention" item=item)}}
  • Edit
  • diff --git a/ui/packages/consul-ui/app/components/consul/kv/form/index.hbs b/ui/packages/consul-ui/app/components/consul/kv/form/index.hbs index a0a067081443..da045157dd7b 100644 --- a/ui/packages/consul-ui/app/components/consul/kv/form/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/kv/form/index.hbs @@ -11,8 +11,11 @@ as |api| > +{{#let (cannot 'write kv' item=api.data) as |disabld|}}
    -
    +
    {{#if api.isCreate}}
    {{#if api.isCreate}} +{{#if (not disabld)}} +{{/if}} {{else}} +{{#if (not disabld)}} +{{/if}} +{{#if (not disabld)}} @@ -54,6 +77,8 @@ {{/if}} +{{/if}} +{{/let}} diff --git a/ui/packages/consul-ui/app/components/consul/kv/list/index.hbs b/ui/packages/consul-ui/app/components/consul/kv/list/index.hbs index da80b8948fd0..2c78ac191602 100644 --- a/ui/packages/consul-ui/app/components/consul/kv/list/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/kv/list/index.hbs @@ -17,6 +17,7 @@ as |item index|> More + {{#if (can 'write kv' item=item)}}
  • {{if item.isFolder 'View' 'Edit'}}
  • @@ -55,6 +56,11 @@ as |item index|> + {{else}} +
  • + View +
  • + {{/if}}
    diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs index 3eb0b8a163ef..5872006d4454 100644 --- a/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/lock-session/form/index.hbs @@ -41,6 +41,7 @@ {{/if}} +{{#if (can 'delete session' item=api.data)}} @@ -53,6 +54,7 @@ +{{/if}} \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs index 82d6650f17cc..6f3a16de254d 100644 --- a/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/lock-session/list/index.hbs @@ -50,6 +50,7 @@ +{{#if (can "delete sessions")}} @@ -70,5 +71,6 @@ +{{/if}} {{/if}} \ No newline at end of file diff --git a/ui/packages/consul-ui/app/components/topology-metrics/popover/index.hbs b/ui/packages/consul-ui/app/components/topology-metrics/popover/index.hbs index b8241d37a4ca..3c11d0fdce7b 100644 --- a/ui/packages/consul-ui/app/components/topology-metrics/popover/index.hbs +++ b/ui/packages/consul-ui/app/components/topology-metrics/popover/index.hbs @@ -96,6 +96,7 @@ returns=(set this 'popoverController') }} {{on 'click' (fn (optional this.popoverController.show))}} + {{disabled (cannot 'update intention' item=item.Intention)}} type="button" style={{{concat 'top:' @position.y 'px;left:' @position.x 'px;'}}} aria-label={{if (eq @type 'deny') 'Add intention' 'View intention'}} diff --git a/ui/packages/consul-ui/app/components/topology-metrics/popover/index.scss b/ui/packages/consul-ui/app/components/topology-metrics/popover/index.scss index b359261e8b94..6b89b7919057 100644 --- a/ui/packages/consul-ui/app/components/topology-metrics/popover/index.scss +++ b/ui/packages/consul-ui/app/components/topology-metrics/popover/index.scss @@ -5,11 +5,14 @@ background-color: $white; padding: 1px 1px; &:hover { - cursor:pointer; + cursor: pointer; } &:active, &:focus { outline: none; } + &:disabled { + cursor: default; + } } &.deny .informed-action header::before { display: none; diff --git a/ui/packages/consul-ui/app/instance-initializers/nspace.js b/ui/packages/consul-ui/app/instance-initializers/nspace.js index 8bd693841335..b1d27369e8dd 100644 --- a/ui/packages/consul-ui/app/instance-initializers/nspace.js +++ b/ui/packages/consul-ui/app/instance-initializers/nspace.js @@ -80,6 +80,9 @@ export function initialize(container) { .filter(function(item) { return item.startsWith('dc'); }) + .filter(function(item) { + return item.endsWith('path'); + }) .map(function(item) { return item.replace('._options.path', '').replace(dotRe, '/'); }) diff --git a/ui/packages/consul-ui/app/modifiers/disabled.js b/ui/packages/consul-ui/app/modifiers/disabled.js new file mode 100644 index 000000000000..3e582764633b --- /dev/null +++ b/ui/packages/consul-ui/app/modifiers/disabled.js @@ -0,0 +1,17 @@ +import { modifier } from 'ember-modifier'; + +export default modifier(function enabled($element, [bool], hash) { + if (['input', 'textarea', 'select', 'button'].includes($element.nodeName.toLowerCase())) { + if (bool) { + $element.disabled = bool; + } else { + $element.dataset.disabled = false; + } + return; + } + for (const $el of $element.querySelectorAll('input,textarea')) { + if ($el.dataset.disabled !== 'false') { + $el.disabled = bool; + } + } +}); diff --git a/ui/packages/consul-ui/app/router.js b/ui/packages/consul-ui/app/router.js index a85c77247530..e566c6ce1c5f 100644 --- a/ui/packages/consul-ui/app/router.js +++ b/ui/packages/consul-ui/app/router.js @@ -91,10 +91,16 @@ export const routes = { intentions: { _options: { path: '/intentions' }, edit: { - _options: { path: '/:intention_id' }, + _options: { + path: '/:intention_id', + abilities: ['read intentions'], + }, }, create: { - _options: { path: '/create' }, + _options: { + path: '/create', + abilities: ['create intentions'], + }, }, }, // Key/Value @@ -107,10 +113,16 @@ export const routes = { _options: { path: '/*key/edit' }, }, create: { - _options: { path: '/*key/create' }, + _options: { + path: '/*key/create', + abilities: ['create kvs'], + }, }, 'root-create': { - _options: { path: '/create' }, + _options: { + path: '/create', + abilities: ['create kvs'], + }, }, }, // ACLs diff --git a/ui/packages/consul-ui/app/routes/application.js b/ui/packages/consul-ui/app/routes/application.js index 82bf5bdd4fbb..640e84d2affd 100644 --- a/ui/packages/consul-ui/app/routes/application.js +++ b/ui/packages/consul-ui/app/routes/application.js @@ -36,7 +36,7 @@ export default Route.extend(WithBlockingActions, { error: function(e, transition) { // TODO: Normalize all this better let error = { - status: e.code || '', + status: e.code || e.statusCode || '', message: e.message || e.detail || 'Error', }; if (e.errors && e.errors[0]) { diff --git a/ui/packages/consul-ui/app/routes/dc/kv/edit.js b/ui/packages/consul-ui/app/routes/dc/kv/edit.js index a839cc7989f7..127332cf9b95 100644 --- a/ui/packages/consul-ui/app/routes/dc/kv/edit.js +++ b/ui/packages/consul-ui/app/routes/dc/kv/edit.js @@ -6,11 +6,9 @@ import { get } from '@ember/object'; import ascend from 'consul-ui/utils/ascend'; export default class EditRoute extends Route { - @service('repository/kv') - repo; - - @service('repository/session') - sessionRepo; + @service('repository/kv') repo; + @service('repository/session') sessionRepo; + @service('repository/permission') permissions; model(params) { const create = @@ -39,7 +37,7 @@ export default class EditRoute extends Route { // TODO: Consider loading this after initial page load if (typeof model.item !== 'undefined') { const session = get(model.item, 'Session'); - if (session) { + if (session && this.permissions.can('read sessions')) { return hash({ ...model, ...{ diff --git a/ui/packages/consul-ui/app/routing/route.js b/ui/packages/consul-ui/app/routing/route.js index d7311f6e7bcc..6259af33ca39 100644 --- a/ui/packages/consul-ui/app/routing/route.js +++ b/ui/packages/consul-ui/app/routing/route.js @@ -1,12 +1,33 @@ import Route from '@ember/routing/route'; import { get, setProperties } from '@ember/object'; +import { inject as service } from '@ember/service'; +import HTTPError from 'consul-ui/utils/http/error'; // paramsFor import { routes } from 'consul-ui/router'; import wildcard from 'consul-ui/utils/routing/wildcard'; + const isWildcard = wildcard(routes); export default class BaseRoute extends Route { + @service('repository/permission') permissions; + + /** + * 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 + * before any calls to the backend for this model/route. + */ + async beforeModel() { + const abilities = get(routes, `${this.routeName}._options.abilities`) || []; + if (abilities.length > 0) { + if (!abilities.every(ability => this.permissions.can(ability))) { + throw new HTTPError(403); + } + } + } + /** * By default any empty string query parameters should remove the query * parameter from the URL. This is the most common behavior if you don't @@ -16,28 +37,29 @@ export default class BaseRoute extends Route { * queryParameter configuration to configure what is deemed 'empty' */ serializeQueryParam(value, key, type) { - if(typeof value !== 'undefined') { + if (typeof value !== 'undefined') { const empty = get(this, `queryParams.${key}.empty`); - if(typeof empty === 'undefined') { + if (typeof empty === 'undefined') { // by default any queryParams when an empty string mean undefined, // therefore remove the queryParam from the URL - if(value === '') { + if (value === '') { value = undefined; } } else { const possible = empty[0]; let actual = value; - if(Array.isArray(actual)) { + if (Array.isArray(actual)) { actual = actual.split(','); } - const diff = possible.filter(item => !actual.includes(item)) - if(diff.length === 0) { + const diff = possible.filter(item => !actual.includes(item)); + if (diff.length === 0) { value = undefined; } } } return value; } + /** * Set the routeName for the controller so that it is available in the template * for the route/controller.. This is mainly used to give a route name to the @@ -49,6 +71,7 @@ export default class BaseRoute extends Route { }); super.setupController(...arguments); } + /** * Adds urldecoding to any wildcard route `params` passed into ember `model` * hooks, plus of course anywhere else where `paramsFor` is used. This means diff --git a/ui/packages/consul-ui/app/services/repository/permission.js b/ui/packages/consul-ui/app/services/repository/permission.js index 25c40753918b..1bcd7ab5b9a7 100644 --- a/ui/packages/consul-ui/app/services/repository/permission.js +++ b/ui/packages/consul-ui/app/services/repository/permission.js @@ -5,7 +5,7 @@ import { tracked } from '@glimmer/tracking'; const modelName = 'permission'; export default class PermissionService extends RepositoryService { @service('env') env; - + @service('can') _can; // move this to the store @tracked permissions = []; @@ -20,6 +20,10 @@ export default class PermissionService extends RepositoryService { }); } + can(can) { + return this._can.can(can); + } + generate(resource, action, segment) { const req = { Resource: resource, @@ -42,18 +46,38 @@ export default class PermissionService extends RepositoryService { Resource: 'node', Access: 'read', }, + { + Resource: 'session', + Access: 'read', + }, + { + Resource: 'session', + Access: 'write', + }, { Resource: 'key', Access: 'read', }, + { + Resource: 'key', + Access: 'write', + }, { Resource: 'intention', Access: 'read', }, + { + Resource: 'intention', + Access: 'write', + }, { Resource: 'acl', Access: 'read', }, + { + Resource: 'acl', + Access: 'write', + }, ]; if (!this.env.var('CONSUL_ACLS_ENABLED')) { this.permissions = perms.map(item => { diff --git a/ui/packages/consul-ui/app/styles/base/components/form-elements/skin.scss b/ui/packages/consul-ui/app/styles/base/components/form-elements/skin.scss index 17dcbcd06280..8d552193f197 100644 --- a/ui/packages/consul-ui/app/styles/base/components/form-elements/skin.scss +++ b/ui/packages/consul-ui/app/styles/base/components/form-elements/skin.scss @@ -6,6 +6,11 @@ border: $decor-border-100; outline: none; } +textarea:disabled + .CodeMirror, +%form-element-text-input:disabled, +%form-element-text-input:read-only { + cursor: not-allowed; +} %form h2 { @extend %h200; } diff --git a/ui/packages/consul-ui/app/templates/dc/intentions/index.hbs b/ui/packages/consul-ui/app/templates/dc/intentions/index.hbs index 5341b3c64597..c521b16bda3f 100644 --- a/ui/packages/consul-ui/app/templates/dc/intentions/index.hbs +++ b/ui/packages/consul-ui/app/templates/dc/intentions/index.hbs @@ -40,7 +40,9 @@ as |sort filters items|}} +{{#if (can 'create intentions')}} Create +{{/if}} diff --git a/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs b/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs index 03ea5661e908..cb5c180afe4c 100644 --- a/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs +++ b/ui/packages/consul-ui/app/templates/dc/kv/edit.hbs @@ -24,9 +24,11 @@ - {{#if session}} + {{! if a KV has a session `Session` will always be populated despite any specific session permissions }} + {{#if item.Session}}

    @@ -42,6 +44,7 @@ @onsubmit={{if (eq parent.Key '/') (transition-to 'dc.kv.index') (transition-to 'dc.kv.folder' parent.Key)}} @parent={{parent}} /> + {{! session is slightly different to item.Session as we only have session if you have session:read perms}} {{#if session}} +{{#if (can 'create kvs')}} {{#if (not-eq parent.Key '/') }} Create {{else}} Create {{/if}} +{{/if}}

    {{#if (gt sessions.length 0)}} - + {{else}} - + + +

    + Welcome to Lock Sessions +

    +

    - There are no Lock Sessions for this Node. For more information, view our documentation + Consul provides a session mechanism which can be used to build distributed locks. Sessions act as a binding layer between nodes, health checks, and key/value data. There are currently no lock sessions present, or you may not have permission to view lock sessions.

    + + + +
    {{/if}}
    diff --git a/ui/packages/consul-ui/app/templates/dc/services/show/intentions/index.hbs b/ui/packages/consul-ui/app/templates/dc/services/show/intentions/index.hbs index 3a2135d84205..8b594f32cd59 100644 --- a/ui/packages/consul-ui/app/templates/dc/services/show/intentions/index.hbs +++ b/ui/packages/consul-ui/app/templates/dc/services/show/intentions/index.hbs @@ -38,9 +38,11 @@ as |api|> as |sort filters items|}}
    +{{#if (can 'create intentions')}} Create +{{/if}} {{#if (gt items.length 0) }} { + return JSON.stringify( + Object.assign( + item, + { + Allow: !!JSON.parse(env(`CONSUL_RESOURCE_${item.Resource.toUpperCase()}_${item.Access.toUpperCase()}`, 'true')) + } + ) + ); + }) +} ] diff --git a/ui/packages/consul-ui/tests/acceptance/dc/intentions/index.feature b/ui/packages/consul-ui/tests/acceptance/dc/intentions/index.feature index 014fde2205ab..29db2c9b718b 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/intentions/index.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/intentions/index.feature @@ -10,6 +10,20 @@ Feature: dc / intentions / index Then the url should be /dc-1/intentions And the title should be "Intentions - Consul" Then I see 3 intention models on the intentionList component + Scenario: Viewing intentions with no write access + Given 1 datacenter model with the value "dc-1" + And 3 intention models + And permissions from yaml + --- + intention: + write: false + --- + When I visit the intentions page for yaml + --- + dc: dc-1 + --- + Then the url should be /dc-1/intentions + And I don't see create Scenario: Viewing intentions in the listing live updates Given 1 datacenter model with the value "dc-1" Given 3 intention models diff --git a/ui/packages/consul-ui/tests/acceptance/dc/kvs/edit.feature b/ui/packages/consul-ui/tests/acceptance/dc/kvs/edit.feature index e17e27e89d7b..edec7173058b 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/kvs/edit.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/kvs/edit.feature @@ -25,6 +25,29 @@ Feature: dc / kvs / edit: KV Viewing kv: another-key --- Then I don't see ID on the session + Scenario: Viewing a kv with no write access + Given 1 datacenter model with the value "datacenter" + And 1 kv model from yaml + --- + Key: key + Session: session-id + --- + And permissions from yaml + --- + key: + write: false + session: + read: false + --- + When I visit the kv page for yaml + --- + dc: datacenter + kv: key + --- + Then the url should be /datacenter/kv/key/edit + And I don't see create + And I don't see ID on the session + And I see warning on the session # Make sure we can view KVs that have similar names to sections in the UI Scenario: I have KV called [Page] Given 1 datacenter model with the value "datacenter" diff --git a/ui/packages/consul-ui/tests/acceptance/dc/kvs/index.feature b/ui/packages/consul-ui/tests/acceptance/dc/kvs/index.feature new file mode 100644 index 000000000000..dc4cb041aed7 --- /dev/null +++ b/ui/packages/consul-ui/tests/acceptance/dc/kvs/index.feature @@ -0,0 +1,27 @@ +@setupApplicationTest +Feature: dc / kvs / index + Scenario: Viewing kvs in the listing + Given 1 datacenter model with the value "dc-1" + And 3 kv models + When I visit the kvs page for yaml + --- + dc: dc-1 + --- + Then the url should be /dc-1/kv + And the title should be "Key/Value - Consul" + Then I see 3 kv models + Scenario: Viewing kvs with no write access + Given 1 datacenter model with the value "dc-1" + And 3 kv models + And permissions from yaml + --- + key: + write: false + --- + When I visit the kvs page for yaml + --- + dc: dc-1 + --- + Then the url should be /dc-1/kv + And I don't see create + diff --git a/ui/packages/consul-ui/tests/acceptance/steps/dc/kvs/index-steps.js b/ui/packages/consul-ui/tests/acceptance/steps/dc/kvs/index-steps.js new file mode 100644 index 000000000000..ba1093295f36 --- /dev/null +++ b/ui/packages/consul-ui/tests/acceptance/steps/dc/kvs/index-steps.js @@ -0,0 +1,10 @@ +import steps from '../../steps'; + +// step definitions that are shared between features should be moved to the +// tests/acceptance/steps/steps.js file + +export default function(assert) { + return steps(assert).then('I should find a file', function() { + assert.ok(true, this.step); + }); +} diff --git a/ui/packages/consul-ui/tests/pages.js b/ui/packages/consul-ui/tests/pages.js index 35127630718d..5cd9c66f5505 100644 --- a/ui/packages/consul-ui/tests/pages.js +++ b/ui/packages/consul-ui/tests/pages.js @@ -152,7 +152,9 @@ export default { radiogroup ) ), - service: create(service(visitable, clickable, attribute, collection, text, consulIntentionList, tabgroup)), + service: create( + service(visitable, clickable, attribute, collection, text, consulIntentionList, tabgroup) + ), instance: create( instance( visitable, @@ -179,7 +181,7 @@ export default { ) ), kvs: create(kvs(visitable, creatable, consulKvList)), - kv: create(kv(visitable, attribute, submitable, deletable, cancelable, clickable)), + kv: create(kv(visitable, attribute, isPresent, submitable, deletable, cancelable, clickable)), acls: create(acls(visitable, deletable, creatable, clickable, attribute, collection)), acl: create(acl(visitable, submitable, deletable, cancelable, clickable)), policies: create(policies(visitable, creatable, consulPolicyList, popoverSelect)), diff --git a/ui/packages/consul-ui/tests/pages/dc/kv/edit.js b/ui/packages/consul-ui/tests/pages/dc/kv/edit.js index 96af4d72db78..bb577470dbf3 100644 --- a/ui/packages/consul-ui/tests/pages/dc/kv/edit.js +++ b/ui/packages/consul-ui/tests/pages/dc/kv/edit.js @@ -1,4 +1,4 @@ -export default function(visitable, attribute, submitable, deletable, cancelable) { +export default function(visitable, attribute, present, submitable, deletable, cancelable) { return { visit: visitable(['/:dc/kv/:kv/edit', '/:dc/kv/create'], function(str) { // this will encode the parts of the key path but means you can no longer @@ -12,6 +12,7 @@ export default function(visitable, attribute, submitable, deletable, cancelable) ...cancelable(), ...deletable(), session: { + warning: present('[data-test-session-warning]'), ID: attribute('data-test-session', '[data-test-session]'), ...deletable({}, '[data-test-session]'), }, diff --git a/ui/packages/consul-ui/tests/steps.js b/ui/packages/consul-ui/tests/steps.js index 08866ecbca51..9ba679438594 100644 --- a/ui/packages/consul-ui/tests/steps.js +++ b/ui/packages/consul-ui/tests/steps.js @@ -97,7 +97,7 @@ export default function({ const clipboard = function() { return window.localStorage.getItem('clipboard'); }; - models(library, create); + models(library, create, setCookie); http(library, respondWith, setCookie); visit(library, pages, utils.setCurrentPage, reset); click(library, utils.find, helpers.click); diff --git a/ui/packages/consul-ui/tests/steps/doubles/model.js b/ui/packages/consul-ui/tests/steps/doubles/model.js index ca9e035c89e8..17fcfbdbca96 100644 --- a/ui/packages/consul-ui/tests/steps/doubles/model.js +++ b/ui/packages/consul-ui/tests/steps/doubles/model.js @@ -1,4 +1,4 @@ -export default function(scenario, create, win = window, doc = document) { +export default function(scenario, create, set, win = window, doc = document) { scenario .given(['an external edit results in $number $model model[s]?'], function(number, model) { return create(number, model); @@ -21,6 +21,17 @@ export default function(scenario, create, win = window, doc = document) { }); }) .given(['ui_config from yaml\n$yaml'], function(data) { + // this one doesn't interact with the api therefore you don't need to use + // setCookie/set. Ideally setCookie should probably use doc.cookie also so + // there is no difference between these doc.cookie = `CONSUL_UI_CONFIG=${JSON.stringify(data)}`; + }) + .given(['permissions from yaml\n$yaml'], function(data) { + Object.entries(data).forEach(([key, value]) => { + const resource = `CONSUL_RESOURCE_${key.toUpperCase()}`; + Object.entries(value).forEach(([key, value]) => { + set(`${resource}_${key.toUpperCase()}`, value); + }); + }); }); }