Merge branch 'chore-enhanced-user-info' of https://github.com/hashicorp/boundary-ui into chore-enhanced-user-info

This commit is contained in:
Randall Morey committed 2021-10-11 14:10:34 -04:00
commit d398041e3a
42 files changed
+805 -168

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
import ModelAbility from './model';
/**
* Provides abilities for accounts.
*/
export default class AccountAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canSetPassword() {
return this.hasAuthorizedAction('set-password');
}
}
+22
View File
@@ -0,0 +1,22 @@
import ModelAbility from './model';
/**
* Provides abilities for groups.
*/
export default class GroupAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canAddMembers() {
return this.hasAuthorizedAction('add-members');
}
/**
* @type {boolean}
*/
get canRemoveMembers() {
return this.hasAuthorizedAction('remove-members');
}
}
+22
View File
@@ -0,0 +1,22 @@
import ModelAbility from './model';
/**
* Provides abilities for host sets.
*/
export default class UserAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canAddHosts() {
return this.hasAuthorizedAction('add-hosts');
}
/**
* @type {boolean}
*/
get canRemoveHosts() {
return this.hasAuthorizedAction('remove-hosts');
}
}
+29
View File
@@ -0,0 +1,29 @@
import ModelAbility from './model';
/**
* Provides abilities for roles.
*/
export default class RoleAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canSetGrants() {
return this.hasAuthorizedAction('set-grants');
}
/**
* @type {boolean}
*/
get canAddPrincipals() {
return this.hasAuthorizedAction('add-principals');
}
/**
* @type {boolean}
*/
get canRemovePrincipals() {
return this.hasAuthorizedAction('remove-principals');
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import ModelAbility from './model';
/**
* Provides a `connect` ability for targets.
* Provides abilities for targets.
*/
export default class TargetAbility extends ModelAbility {
// =permissions
+22
View File
@@ -0,0 +1,22 @@
import ModelAbility from './model';
/**
* Provides abilities for users.
*/
export default class UserAbility extends ModelAbility {
// =permissions
/**
* @type {boolean}
*/
get canAddAccounts() {
return this.hasAuthorizedAction('add-accounts');
}
/**
* @type {boolean}
*/
get canRemoveAccounts() {
return this.hasAuthorizedAction('remove-accounts');
}
}
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/account';
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/group';
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/host-set';
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/role';
+1
View File
@@ -0,0 +1 @@
export { default } from 'api/abilities/user';
+1
View File
@@ -9,6 +9,7 @@ export default factory.extend({
'read',
'update',
'delete',
'set-password',
],
attributes() {
switch (this.type) {
+2
View File
@@ -9,6 +9,8 @@ export default factory.extend({
'read',
'update',
'delete',
'add-members',
'remove-members',
],
id: (i) => `group-${i}`,
+2
View File
@@ -8,6 +8,8 @@ export default factory.extend({
'read',
'update',
'delete',
'add-hosts',
'remove-hosts',
],
id: (i) => `host-set-id-${i}`,
});
+3
View File
@@ -9,6 +9,9 @@ export default factory.extend({
'read',
'update',
'delete',
'set-grants',
'add-principals',
'remove-principals',
],
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
grant_strings: () => [
+2
View File
@@ -8,6 +8,8 @@ export default factory.extend({
'read',
'update',
'delete',
'add-accounts',
'remove-accounts',
],
id: (i) => `user-${i}`,
});
+1
View File
@@ -33,6 +33,7 @@
"ember-fetch": "^8.1.1"
},
"devDependencies": {
"@babel/core": "^7.15.8",
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.4.2",
"@glimmer/component": "^1.0.4",
@@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Abilities | Account', function (hooks) {
setupTest(hooks);
test('it reflects when a given account may set password based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['set-password'],
};
assert.ok(service.can('setPassword target', model));
model.authorized_actions = [];
assert.notOk(service.can('setPassword target', model));
});
});
@@ -0,0 +1,28 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Abilities | Group', function (hooks) {
setupTest(hooks);
test('it reflects when a given group resource may add members based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['add-members'],
};
assert.ok(service.can('addMembers group', model));
model.authorized_actions = [];
assert.notOk(service.can('addMembers group', model));
});
test('it reflects when a given group resource may remove members based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['remove-members'],
};
assert.ok(service.can('removeMembers group', model));
model.authorized_actions = [];
assert.notOk(service.can('removeMembers group', model));
});
});
@@ -0,0 +1,28 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Abilities | Host Set', function (hooks) {
setupTest(hooks);
test('it reflects when a given host set resource may add hosts based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['add-hosts'],
};
assert.ok(service.can('addHosts hostSet', model));
model.authorized_actions = [];
assert.notOk(service.can('addHosts hostSet', model));
});
test('it reflects when a given hostSet resource may remove hosts based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['remove-hosts'],
};
assert.ok(service.can('removeHosts hostSet', model));
model.authorized_actions = [];
assert.notOk(service.can('removeHosts hostSet', model));
});
});
@@ -0,0 +1,39 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Abilities | Role', function (hooks) {
setupTest(hooks);
test('it reflects when a given role may set grants based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['set-grants'],
};
assert.ok(service.can('setGrants role', model));
model.authorized_actions = [];
assert.notOk(service.can('setGrants role', model));
});
test('it reflects when a given role may add principals based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['add-principals'],
};
assert.ok(service.can('addPrincipals role', model));
model.authorized_actions = [];
assert.notOk(service.can('addPrincipals role', model));
});
test('it reflects when a given role may remove principals based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['remove-principals'],
};
assert.ok(service.can('removePrincipals role', model));
model.authorized_actions = [];
assert.notOk(service.can('removePrincipals role', model));
});
});
@@ -0,0 +1,28 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Abilities | User', function (hooks) {
setupTest(hooks);
test('it reflects when a given user resource may add accounts based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['add-accounts'],
};
assert.ok(service.can('addAccounts user', model));
model.authorized_actions = [];
assert.notOk(service.can('addAccounts user', model));
});
test('it reflects when a given user resource may remove accounts based on authorized_actions', function (assert) {
assert.expect(2);
const service = this.owner.lookup('service:can');
const model = {
authorized_actions: ['remove-accounts'],
};
assert.ok(service.can('removeAccounts user', model));
model.authorized_actions = [];
assert.notOk(service.can('removeAccounts user', model));
});
});
+5 -1
View File
@@ -29,7 +29,6 @@
"doc:toc": "doctoc README.md"
},
"dependencies": {
"babel-loader": "^8.2.2",
"ember-cli-babel": "^7.26.6",
"ember-cli-htmlbars": "^5.7.1",
"ember-focus-trap": "^0.7.0",
@@ -39,6 +38,7 @@
"emberx-select": "github:adopted-ember-addons/emberx-select#bc5f774"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.4.2",
"@glimmer/component": "^1.0.4",
@@ -47,9 +47,11 @@
"@storybook/addon-actions": "^6.3.8",
"@storybook/addon-docs": "^6.3.8",
"@storybook/addon-knobs": "^6.3.1",
"@storybook/addons": "^6.3.9",
"@storybook/ember": "^6.3.8",
"@storybook/ember-cli-storybook": "^0.4.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"broccoli-asset-rev": "^3.0.0",
"doctoc": "^2.0.1",
"ember-auto-import": "^1.12.0",
@@ -83,6 +85,8 @@
"prettier": "^2.4.0",
"qunit": "^2.17.1",
"qunit-dom": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sass": "^1.41.0",
"sass-lint": "^1.13.1"
},
@@ -1,39 +1,43 @@
<Rose::Form
@onSubmit={{fn this.createGrant @addGrant}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
as |form|
>
<Rose::List::KeyValue as |list|>
{{! Add grant field (only one per form) }}
<list.item as |item|>
<form.input
@contextual={{true}}
@name='grant'
@type='text'
@value={{this.newGrantString}}
as |input|
>
<item.key>
<input.label>{{t 'resources.role.grant.actions.create'}}</input.label>
</item.key>
<item.cell>
<input.field title={{t 'form.grant.help'}} />
</item.cell>
<item.cell>
<Rose::Button
@submit={{true}}
@disabled={{this.cannotSave}}
@style='secondary'
title={{t 'actions.add'}}
>
{{t 'actions.add'}}
</Rose::Button>
</item.cell>
</form.input>
</list.item>
</Rose::List::KeyValue>
</Rose::Form>
{{#if (can 'setGrants role' @model)}}
<Rose::Form
@onSubmit={{fn this.createGrant @addGrant}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
as |form|
>
<Rose::List::KeyValue as |list|>
{{! Add grant field (only one per form) }}
<list.item as |item|>
<form.input
@contextual={{true}}
@name='grant'
@type='text'
@value={{this.newGrantString}}
as |input|
>
<item.key>
<input.label>{{t
'resources.role.grant.actions.create'
}}</input.label>
</item.key>
<item.cell>
<input.field title={{t 'form.grant.help'}} />
</item.cell>
<item.cell>
<Rose::Button
@submit={{true}}
@disabled={{this.cannotSave}}
@style='secondary'
title={{t 'actions.add'}}
>
{{t 'actions.add'}}
</Rose::Button>
</item.cell>
</form.input>
</list.item>
</Rose::List::KeyValue>
</Rose::Form>
{{/if}}
<Rose::Form
@onSubmit={{fn @submit this.grantStrings}}
@@ -61,6 +65,7 @@
@name='grant'
@type='text'
@value={{grant.value}}
@disabled={{if (can 'setGrants role' @model) false true}}
@error={{@model.errors.grant_strings}}
as |input|
>
@@ -72,23 +77,27 @@
</item.cell>
</form.input>
<item.cell>
<Rose::Button
@style='secondary'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
{{on 'click' (fn @removeGrant grant.value)}}
>
{{t 'actions.remove'}}
</Rose::Button>
{{#if (can 'setGrants role' @model)}}
<Rose::Button
@style='secondary'
@iconOnly='flight-icons/svg/trash-16'
title={{t 'actions.remove'}}
{{on 'click' (fn @removeGrant grant.value)}}
>
{{t 'actions.remove'}}
</Rose::Button>
{{/if}}
</item.cell>
</list.item>
{{/each}}
</Rose::List::KeyValue>
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{#if (can 'setGrants role' @model)}}
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{/if}}
</Rose::Form>
@@ -5,10 +5,12 @@
{{t 'titles.details'}}
</nav.link>
{{#if @model.isPassword}}
<nav.link
@route='scopes.scope.auth-methods.auth-method.accounts.account.set-password'
>
{{t 'actions.set-password'}}
</nav.link>
{{#if (can 'setPassword account' @model)}}
<nav.link
@route='scopes.scope.auth-methods.auth-method.accounts.account.set-password'
>
{{t 'actions.set-password'}}
</nav.link>
{{/if}}
{{/if}}
</Rose::Nav::Tabs>
@@ -3,9 +3,11 @@
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link @route='scopes.scope.groups.group.add-members'>
{{t 'resources.group.actions.add-members'}}
</dropdown.link>
{{#if (can 'addMembers group' @model)}}
<dropdown.link @route='scopes.scope.groups.group.add-members'>
{{t 'resources.group.actions.add-members'}}
</dropdown.link>
{{/if}}
{{#if (can 'delete model' @model)}}
<dropdown.separator />
<dropdown.button
@@ -29,21 +29,26 @@
</Copyable>
</row.cell>
<row.cell>
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
<dropdown.button
@style='danger'
{{on 'click' (route-action 'removeMember' @model.group member)}}
{{#if (can 'removeMembers group' @model.group)}}
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
<dropdown.button
@style='danger'
{{on
'click'
(route-action 'removeMember' @model.group member)
}}
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
{{/if}}
</row.cell>
</body.row>
{{/each}}
@@ -3,16 +3,18 @@
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link
@route='scopes.scope.host-catalogs.host-catalog.host-sets.host-set.create-and-add-host'
>
{{t 'resources.host-set.actions.create-and-add-host'}}
</dropdown.link>
<dropdown.link
@route='scopes.scope.host-catalogs.host-catalog.host-sets.host-set.add-hosts'
>
{{t 'resources.host-set.actions.add-hosts'}}
</dropdown.link>
{{#if (can 'addHosts hostSet' @model)}}
<dropdown.link
@route='scopes.scope.host-catalogs.host-catalog.host-sets.host-set.create-and-add-host'
>
{{t 'resources.host-set.actions.create-and-add-host'}}
</dropdown.link>
<dropdown.link
@route='scopes.scope.host-catalogs.host-catalog.host-sets.host-set.add-hosts'
>
{{t 'resources.host-set.actions.add-hosts'}}
</dropdown.link>
{{/if}}
{{#if (can 'delete model' @model)}}
<dropdown.separator />
<dropdown.button
@@ -36,21 +36,23 @@
</Copyable>
</row.cell>
<row.cell>
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
<dropdown.button
@style='danger'
{{on 'click' (route-action 'removeHost' @model.hostSet host)}}
{{#if (can 'removeHosts hostSet' @model.hostSet)}}
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
<dropdown.button
@style='danger'
{{on 'click' (route-action 'removeHost' @model.hostSet host)}}
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
{{/if}}
</row.cell>
</body.row>
{{/each}}
@@ -3,9 +3,11 @@
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link @route='scopes.scope.roles.role.add-principals'>
{{t 'resources.role.principal.actions.add-principals'}}
</dropdown.link>
{{#if (can 'addPrincipals role' @model)}}
<dropdown.link @route='scopes.scope.roles.role.add-principals'>
{{t 'resources.role.principal.actions.add-principals'}}
</dropdown.link>
{{/if}}
{{#if (can 'delete model' @model)}}
<dropdown.separator />
<dropdown.button
@@ -33,21 +33,26 @@
</Copyable>
</row.cell>
<row.cell>
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
<dropdown.button
@style='danger'
{{on 'click' (route-action 'removePrincipal' principal.model)}}
{{#if (can 'removePrincipals role' @model.role)}}
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
<dropdown.button
@style='danger'
{{on
'click'
(route-action 'removePrincipal' principal.model)
}}
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
{{/if}}
</row.cell>
</body.row>
{{/each}}
@@ -3,9 +3,11 @@
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link @route='scopes.scope.users.user.add-accounts'>
{{t 'resources.user.actions.add-accounts'}}
</dropdown.link>
{{#if (can 'addAccounts user' @model)}}
<dropdown.link @route='scopes.scope.users.user.add-accounts'>
{{t 'resources.user.actions.add-accounts'}}
</dropdown.link>
{{/if}}
{{#if (can 'delete model' @model)}}
<dropdown.separator />
<dropdown.button
@@ -33,24 +33,26 @@
</Copyable>
</row.cell>
<row.cell>
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
<dropdown.button
@style='danger'
{{on
'click'
(route-action 'removeAccount' @model.user account)
}}
{{#if (can 'removeAccounts user' @model.user)}}
<Rose::Dropdown
@icon='flight-icons/svg/more-horizontal-16'
@iconOnly={{true}}
@showCaret={{false}}
@dropdownRight={{true}}
@text='{{t 'actions.manage'}}'
as |dropdown|
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
<dropdown.button
@style='danger'
{{on
'click'
(route-action 'removeAccount' @model.user account)
}}
>
{{t 'actions.remove'}}
</dropdown.button>
</Rose::Dropdown>
{{/if}}
</row.cell>
</body.row>
{{/each}}
@@ -49,7 +49,8 @@ module('Acceptance | accounts | set password', function (hooks) {
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
urls.accounts = `${urls.authMethod}/accounts`;
urls.setPassword = `${urls.accounts}/${instances.account.id}/set-password`;
urls.account = `${urls.accounts}/${instances.account.id}`;
urls.setPassword = `${urls.account}/set-password`;
});
test('visiting account set password', async function (assert) {
@@ -59,6 +60,26 @@ module('Acceptance | accounts | set password', function (hooks) {
assert.equal(currentURL(), urls.setPassword);
});
test('can navigate to route with proper authorization', async function (assert) {
assert.expect(1);
await visit(urls.account);
assert.ok(
find(`.rose-layout-page-navigation [href="${urls.setPassword}"]`)
);
});
test('cannot navigate to route without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.account.authorized_actions.filter(
(item) => item !== 'set-password'
);
instances.account.update({ authorized_actions });
await visit(urls.account);
assert.notOk(
find(`.rose-layout-page-navigation [href="${urls.setPassword}"]`)
);
});
test('can set a new password for account', async function (assert) {
assert.expect(1);
this.server.post(
@@ -68,6 +68,16 @@ module('Acceptance | groups | members', function (hooks) {
assert.equal(findAll('tbody tr').length, membersCount - 1);
});
test('cannot remove a member without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.group.authorized_actions.filter(
(item) => item !== 'remove-members'
);
instances.group.update({ authorized_actions });
await visit(urls.members);
assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
});
test('shows error message on member remove', async function (assert) {
assert.expect(2);
this.server.post('/groups/:idMethod', () => {
@@ -95,6 +105,22 @@ module('Acceptance | groups | members', function (hooks) {
assert.equal(currentURL(), urls.addMembers);
});
test('can navigate to add members with proper authorization', async function (assert) {
assert.expect(1);
await visit(urls.group);
assert.ok(find(`[href="${urls.addMembers}"]`));
});
test('cannot navigate to add members without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.group.authorized_actions.filter(
(item) => item !== 'add-members'
);
instances.group.update({ authorized_actions });
await visit(urls.group);
assert.notOk(find(`[href="${urls.addMembers}"]`));
});
test('select and save members to add', async function (assert) {
assert.expect(3);
instances.group.update({ memberIds: [] });
@@ -99,6 +99,16 @@ module('Acceptance | host-catalogs | host-sets | hosts', function (hooks) {
assert.equal(findAll('tbody tr').length, count - 1);
});
test('cannot remove a host without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.hostSet.authorized_actions.filter(
(item) => item !== 'remove-hosts'
);
instances.hostSet.update({ authorized_actions });
await visit(urls.hostSetHosts);
assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
});
test('shows error message on host remove error', async function (assert) {
assert.expect(2);
this.server.post('/host-sets/:idMethod', () => {
@@ -126,6 +136,22 @@ module('Acceptance | host-catalogs | host-sets | hosts', function (hooks) {
assert.equal(currentURL(), urls.addHosts);
});
test('can navigate to add hosts with proper authorization', async function (assert) {
assert.expect(1);
await visit(urls.hostSet);
assert.ok(find(`[href="${urls.addHosts}"]`));
});
test('cannot navigate to add hosts without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.hostSet.authorized_actions.filter(
(item) => item !== 'add-hosts'
);
instances.hostSet.update({ authorized_actions });
await visit(urls.hostSet);
assert.notOk(find(`[href="${urls.addHosts}"]`));
});
test('select and save hosts to add', async function (assert) {
assert.expect(3);
instances.hostSet.update({ hostIds: [] });
@@ -63,6 +63,26 @@ module('Acceptance | roles | grants', function (hooks) {
assert.equal(findAll(`${grantsForm} [name="grant"]`).length, grantsCount);
});
test('cannot set grants without proper authorization', async function (assert) {
assert.expect(4);
const authorized_actions = instances.role.authorized_actions.filter(
(item) => item !== 'set-grants'
);
instances.role.update({ authorized_actions });
await visit(urls.grants);
assert.equal(
findAll('main form').length,
1,
'New grant form is not displayed.'
);
assert.notOk(
find('main form button:not([type="submit"])'),
'Grant delete button is not displayed.'
);
assert.notOk(find('.rose-form-actions'), 'Form actions are not displayed.');
assert.ok(find('main form input[disabled]'), 'Grant fields are disabled.');
});
test('update a grant', async function (assert) {
assert.expect(1);
this.server.post(
@@ -69,6 +69,16 @@ module('Acceptance | roles | principals', function (hooks) {
assert.equal(findAll('tbody tr').length, principalsCount - 1);
});
test('principal cannot be removed from a role without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.role.authorized_actions.filter(
(item) => item !== 'remove-principals'
);
instances.role.update({ authorized_actions });
await visit(urls.rolePrincipals);
assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
});
test('shows error message on principal remove', async function (assert) {
assert.expect(2);
this.server.post('/roles/:idMethod', () => {
@@ -89,6 +99,16 @@ module('Acceptance | roles | principals', function (hooks) {
assert.ok(find('[role="alert"]'));
});
test('cannot navigate to add principals without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.role.authorized_actions.filter(
(item) => item !== 'add-principals'
);
instances.role.update({ authorized_actions });
await visit(urls.rolePrincipals);
assert.notOk(find('.rose-layout-page-actions a'));
});
test('select and save principals to add', async function (assert) {
assert.expect(3);
instances.role.update({ userIds: [], groupIds: [] });
@@ -61,7 +61,7 @@ module('Acceptance | users | accounts', function (hooks) {
assert.equal(findAll('tbody tr').length, accountsCount);
});
test('can remove a account', async function (assert) {
test('can remove an account', async function (assert) {
assert.expect(2);
await visit(urls.accounts);
assert.equal(findAll('tbody tr').length, accountsCount);
@@ -69,6 +69,16 @@ module('Acceptance | users | accounts', function (hooks) {
assert.equal(findAll('tbody tr').length, accountsCount - 1);
});
test('cannot remove an account without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.user.authorized_actions.filter(
(item) => item !== 'remove-accounts'
);
instances.user.update({ authorized_actions });
await visit(urls.accounts);
assert.notOk(find('tbody tr .rose-dropdown-button-danger'));
});
test('shows error message on account remove', async function (assert) {
assert.expect(2);
this.server.post('/users/:idMethod', () => {
@@ -92,10 +102,27 @@ module('Acceptance | users | accounts', function (hooks) {
test('visiting account add accounts', async function (assert) {
assert.expect(1);
await visit(urls.addAccounts);
await visit(urls.addAccounts);
await a11yAudit();
assert.equal(currentURL(), urls.addAccounts);
});
test('can navigate to add accounts with proper authorization', async function (assert) {
assert.expect(1);
await visit(urls.accounts);
assert.ok(find(`[href="${urls.addAccounts}"]`));
});
test('cannot navigate to add accounts without proper authorization', async function (assert) {
assert.expect(1);
const authorized_actions = instances.user.authorized_actions.filter(
(item) => item !== 'add-accounts'
);
instances.user.update({ authorized_actions });
await visit(urls.accounts);
assert.notOk(find(`[href="${urls.addAccounts}"]`));
});
test('select and save accounts to add', async function (assert) {
assert.expect(3);
instances.user.update({ accountIds: [] });
+15 -6
View File
@@ -1,6 +1,6 @@
# Client Connect UI
# Desktop client UI
The client UI for Boundary.
The desktop client UI for Boundary.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
@@ -31,7 +31,7 @@ You will need the following things properly installed on your computer.
## Installation
See boundary-ui README for installation instructions.
See [boundary-ui README](https://github.com/hashicorp/boundary-ui#installation) for installation instructions.
## Yarn Commands
@@ -39,24 +39,32 @@ List of available project commands. `yarn run <command-name>`
| Command | Description |
| ------- | ----------- |
| build:development | Builds the UI in development mode. |
| build | Builds the UI for production. |
| build:development | Builds the UI in development mode. |
| build:desktop:debianOnMacOS | Builds debian based linux artifacts on MacOS. |
| lint | Runs all lint commands. |
| lint:fix | Runs all automatic linting fixes. |
| lint:hbs | Lints `hbs` template files. |
| lint:hbs:fix | Runs the automatic linting fix for `hbs` template files. |
| lint:js | Lints `js` files. |
| lint:js:fix | Runs the automatic linting fix for `js` files. |
| lint:sass | Lints `scss` files. |
| lint:electron | Runs a linter tool to identify misconfigurations and security anti-patterns in Electron. |
| format | Runs all auto-formatters. |
| format:hbs | Auto-formats `hbs` files using Prettier. |
| format:js | Auto-formats `js` files using Prettier. |
| format:sass | Auto-formats `scss` files using Prettier. |
| start | Runs the dummy app local server. |
| start | Runs the dummy app local server as web app. |
| start:desktop | Runs the dummy app as an electron app. |
| clean:coverage | Cleans coverage reporting directory. |
| test | Runs all tests in random order, with coverage reporting. |
| test:ember | Runs ember tests in random order, with coverage reporting. |
| doc:toc | Automatically generates a table of contents for this README file. |
## Running / Development
To run as web app:
* `yarn start`
* Visit your app at [http://localhost:4200](http://localhost:4200).
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
@@ -90,11 +98,12 @@ These environment variables may be used to customized the build.
| `BYPASS_CLI_SETUP` | Disable download and extraction of cli. For development use only. |
| `BYPASS_APP_UPDATER` | Disable app updater feature. For development use only. |
| `DISABLE_WINDOW_CHROME` | Disable window chrome. For internal use only. |
| `ENABLE_MIRAGE` | Enable (`true`) or disable (`false`) mirage. Default value is `true`. |
### Building for Production
Before executing a build, be sure to set any environment variables necessary
for your target [environment](#environment-variables). To build this UI for
for your target [environment](#environment-variables). To build this UI for
production, run the following commands from this folder:
```bash
+1 -1
View File
@@ -23,13 +23,13 @@
"lint:hbs": "ember-template-lint .",
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint .",
"lint:js:fix": "eslint . --fix",
"lint:sass": "stylelint '**/*.scss'",
"lint:electron": "electronegativity -r -i ./electron-app",
"format": "npm-run-all format:*",
"format:hbs": "prettier --write 'app/**/*.hbs'",
"format:js": "prettier --write '{app,config,stories,tests,electron-app/src}/**/*.js' *.js",
"format:sass": "prettier --write app/**/*.scss",
"lint:js:fix": "eslint . --fix",
"start": "ember serve",
"prestart:desktop": "cd electron-app && yarn premake",
"start:desktop": "ember electron",
+210 -25
View File
@@ -23,6 +23,13 @@
dependencies:
"@babel/highlight" "^7.14.5"
"@babel/code-frame@^7.15.8":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503"
integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==
dependencies:
"@babel/highlight" "^7.14.5"
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.7", "@babel/compat-data@^7.15.0":
version "7.15.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
@@ -92,6 +99,27 @@
semver "^6.3.0"
source-map "^0.5.0"
"@babel/core@^7.15.8":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.8.tgz#195b9f2bffe995d2c6c159e72fe525b4114e8c10"
integrity sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==
dependencies:
"@babel/code-frame" "^7.15.8"
"@babel/generator" "^7.15.8"
"@babel/helper-compilation-targets" "^7.15.4"
"@babel/helper-module-transforms" "^7.15.8"
"@babel/helpers" "^7.15.4"
"@babel/parser" "^7.15.8"
"@babel/template" "^7.15.4"
"@babel/traverse" "^7.15.4"
"@babel/types" "^7.15.6"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.1.2"
semver "^6.3.0"
source-map "^0.5.0"
"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.15.0":
version "7.15.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15"
@@ -110,6 +138,15 @@
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/generator@^7.15.8":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1"
integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==
dependencies:
"@babel/types" "^7.15.6"
jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
@@ -302,6 +339,20 @@
"@babel/traverse" "^7.15.4"
"@babel/types" "^7.15.4"
"@babel/helper-module-transforms@^7.15.8":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2"
integrity sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==
dependencies:
"@babel/helper-module-imports" "^7.15.4"
"@babel/helper-replace-supers" "^7.15.4"
"@babel/helper-simple-access" "^7.15.4"
"@babel/helper-split-export-declaration" "^7.15.4"
"@babel/helper-validator-identifier" "^7.15.7"
"@babel/template" "^7.15.4"
"@babel/traverse" "^7.15.4"
"@babel/types" "^7.15.6"
"@babel/helper-optimise-call-expression@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
@@ -395,6 +446,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
"@babel/helper-validator-identifier@^7.15.7":
version "7.15.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
"@babel/helper-validator-option@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@@ -447,6 +503,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.6.tgz#043b9aa3c303c0722e5377fef9197f4cf1796549"
integrity sha512-S/TSCcsRuCkmpUuoWijua0Snt+f3ewU/8spLo+4AXJCZfT0bVCzLD5MuOKdrx0mlAptbKzn5AdgEIIKXxXkz9Q==
"@babel/parser@^7.15.8":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016"
integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.14.5.tgz#4b467302e1548ed3b1be43beae2cc9cf45e0bb7e"
@@ -1278,7 +1339,7 @@
"@babel/helper-validator-identifier" "^7.14.9"
to-fast-properties "^2.0.0"
"@babel/types@^7.15.4":
"@babel/types@^7.15.4", "@babel/types@^7.15.6":
version "7.15.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f"
integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==
@@ -2693,6 +2754,21 @@
global "^4.4.0"
regenerator-runtime "^0.13.7"
"@storybook/addons@^6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.3.9.tgz#e12c8d4351b861d67cb6b8d64ed33b1f6b90994f"
integrity sha512-5tRkeHgdb/I/rp3GBkxonDLVsA45Vpgh/vFrsecrS/98wkSYfPEhqrDGLOosJHFrN3J2pznAuNFaA05158uBsw==
dependencies:
"@storybook/api" "6.3.9"
"@storybook/channels" "6.3.9"
"@storybook/client-logger" "6.3.9"
"@storybook/core-events" "6.3.9"
"@storybook/router" "6.3.9"
"@storybook/theming" "6.3.9"
core-js "^3.8.2"
global "^4.4.0"
regenerator-runtime "^0.13.7"
"@storybook/api@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.3.8.tgz#251bcf6cc3a4e0b908bea7fb0aa9e48d6c48d720"
@@ -2719,6 +2795,32 @@
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/api@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.3.9.tgz#f36c8f01cac602c67b8f22b144e8bd6478039abb"
integrity sha512-lwen3jcY4YbnD8spAZrmXcToed/pwad9QpxkG0GNf6ctcOumN6HIK93fKeJ0vvPYc3v/uq1qKeLyTZ3NrgHQRg==
dependencies:
"@reach/router" "^1.3.4"
"@storybook/channels" "6.3.9"
"@storybook/client-logger" "6.3.9"
"@storybook/core-events" "6.3.9"
"@storybook/csf" "0.0.1"
"@storybook/router" "6.3.9"
"@storybook/semver" "^7.3.2"
"@storybook/theming" "6.3.9"
"@types/reach__router" "^1.3.7"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
lodash "^4.17.20"
memoizerific "^1.11.3"
qs "^6.10.0"
regenerator-runtime "^0.13.7"
store2 "^2.12.0"
telejson "^5.3.2"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/builder-webpack4@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.3.8.tgz#9ffe866fbd50a94b3cf3ecde1fe4d5f29dd337e8"
@@ -2817,6 +2919,15 @@
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/channels@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.3.9.tgz#40907734460fbcb08fe018c794890ffe668e873d"
integrity sha512-ZeHXLFJ43Wn6HJMiGgKUWUMtKcXDoWxL50Qr5Wwbsnmtp2BX7R8aak/Vw9TVT46J86QXkdI3CAKAEvb6esiLRQ==
dependencies:
core-js "^3.8.2"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
"@storybook/client-api@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.3.8.tgz#60e608a5c2afbe6d63a4a063e616bedde8466584"
@@ -2849,6 +2960,14 @@
core-js "^3.8.2"
global "^4.4.0"
"@storybook/client-logger@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.3.9.tgz#ef8c89e96f79927d46dd1dacddb58f3a43154b9a"
integrity sha512-oww+P062SaOQfsTphAQBL6xe5DCv78Po/f/ROk7iYGAbV8HcCCscpzyJSeLfus2CunFYS2ngPcllbvEnqWk7dQ==
dependencies:
core-js "^3.8.2"
global "^4.4.0"
"@storybook/components@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.3.8.tgz#cb84b0245d8784d41e7e6be25a0d5774363e5b87"
@@ -2963,6 +3082,13 @@
dependencies:
core-js "^3.8.2"
"@storybook/core-events@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.3.9.tgz#17127905a2d7773b6e02f15bfbddb1fd1b0bce75"
integrity sha512-6ELOkroH0Oz7+OR1SqGMKAC1+ufituqSxDp08AyvrHPSYqK/db+P2kSCJBdqyUXTvt8lPvqlCOidkRhGrNB/+A==
dependencies:
core-js "^3.8.2"
"@storybook/core-server@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.3.8.tgz#0d141e6f315b735a442aebc0f424f911456c769c"
@@ -3143,6 +3269,22 @@
qs "^6.10.0"
ts-dedent "^2.0.0"
"@storybook/router@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.3.9.tgz#a299091426349b9dc0bb8610773918712b453dc4"
integrity sha512-uXNrZS9tsZr6fStIv/MHQfy3xSsc7RLYWbY4wkgZH+y5K97RtuwXgtbx7uyEpsQwse1Z4PikKu/ejN46F0oPGQ==
dependencies:
"@reach/router" "^1.3.4"
"@storybook/client-logger" "6.3.9"
"@types/reach__router" "^1.3.7"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
lodash "^4.17.20"
memoizerific "^1.11.3"
qs "^6.10.0"
ts-dedent "^2.0.0"
"@storybook/semver@^7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0"
@@ -3185,6 +3327,24 @@
resolve-from "^5.0.0"
ts-dedent "^2.0.0"
"@storybook/theming@6.3.9":
version "6.3.9"
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.3.9.tgz#8e4500dacd298a2ca6bf47b5729449688338283c"
integrity sha512-vyMSLvEXrTC4rnUdWLUNmBNeOdBCl0Nt3R6y/laY+LQZ9Ljz/poRTrIYTkmenYieq4N7787s9zHmxvym/ZvKtw==
dependencies:
"@emotion/core" "^10.1.1"
"@emotion/is-prop-valid" "^0.8.6"
"@emotion/styled" "^10.0.27"
"@storybook/client-logger" "6.3.9"
core-js "^3.8.2"
deep-object-diff "^1.1.0"
emotion-theming "^10.0.27"
global "^4.4.0"
memoizerific "^1.11.3"
polished "^4.0.5"
resolve-from "^5.0.0"
ts-dedent "^2.0.0"
"@storybook/ui@6.3.8":
version "6.3.8"
resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.3.8.tgz#9fb71befbe76f06c478925610ce998c23260a3f3"
@@ -6333,15 +6493,15 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@4.14.2, browserslist@^3.2.6, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.16.7:
version "4.17.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.1.tgz#a98d104f54af441290b7d592626dd541fa642eb9"
integrity sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==
version "4.17.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.3.tgz#2844cd6eebe14d12384b0122d217550160d2d624"
integrity sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==
dependencies:
caniuse-lite "^1.0.30001259"
electron-to-chromium "^1.3.846"
caniuse-lite "^1.0.30001264"
electron-to-chromium "^1.3.857"
escalade "^3.1.1"
nanocolors "^0.1.5"
node-releases "^1.1.76"
node-releases "^1.1.77"
picocolors "^0.2.1"
bser@2.1.1:
version "2.1.1"
@@ -6675,10 +6835,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001259:
version "1.0.30001259"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001259.tgz#ae21691d3da9c4be6144403ac40f71d9f6efd790"
integrity sha512-V7mQTFhjITxuk9zBpI6nYsiTXhcPe05l+364nZjK7MFK/E7ibvYBSAXr4YcA6oPR8j3ZLM/LN+lUqUVAQEUZFg==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001264:
version "1.0.30001265"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3"
integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==
capture-exit@^2.0.0:
version "2.0.0"
@@ -8525,10 +8685,10 @@ electron-rebuild@^2.0.3:
tar "^6.0.5"
yargs "^16.0.0"
electron-to-chromium@^1.3.846:
version "1.3.846"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.846.tgz#a55fd59613dbcaed609e965e3e88f42b08c401d3"
integrity sha512-2jtSwgyiRzybHRxrc2nKI+39wH3AwQgn+sogQ+q814gv8hIFwrcZbV07Ea9f8AmK0ufPVZUvvAG1uZJ+obV4Jw==
electron-to-chromium@^1.3.857:
version "1.3.860"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.860.tgz#d612e54ed75fa524c12af8da3ad8121ebfe2802b"
integrity sha512-gWwGZ+Wv4Mou2SJRH6JQzhTPjL5f95SX7n6VkLTQ/Q/INsZLZNQ1vH2GlZjozKyvT0kkFuCmWTwIoCj+/hUDPw==
element-resize-detector@^1.2.2:
version "1.2.3"
@@ -15695,11 +15855,6 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
nanocolors@^0.1.5:
version "0.1.6"
resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.6.tgz#bc2350d3edfdbfadd7ac018c855ae7c13905a6ad"
integrity sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ==
nanoid@3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
@@ -15923,10 +16078,10 @@ node-pre-gyp@^0.11.0:
semver "^5.3.0"
tar "^4"
node-releases@^1.1.76:
version "1.1.76"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e"
integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==
node-releases@^1.1.77:
version "1.1.77"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e"
integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==
node-uuid@~1.4.7:
version "1.4.8"
@@ -17135,6 +17290,11 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
picocolors@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@@ -17918,6 +18078,15 @@ react-dom@16.14.0:
prop-types "^15.6.2"
scheduler "^0.19.1"
react-dom@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-draggable@^4.4.3:
version "4.4.3"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3"
@@ -18066,6 +18235,14 @@ react@16.14.0:
object-assign "^4.1.1"
prop-types "^15.6.2"
react@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
read-cmd-shim@^1.0.1, read-cmd-shim@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16"
@@ -19100,6 +19277,14 @@ scheduler@^0.19.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"