diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index cfed04b66..523693a4e 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -9,6 +9,19 @@ export default factory.extend({ }; }, + authorized_collection_actions: () => { + return { + users: ['create', 'list'], + groups: ['create', 'list'], + roles: ['create', 'list'], + // 'auth-methods': ['create', 'list'], + // sessopms: ['list'], + // targets: ['create', 'list'], + // 'host-catalogs': ['create', 'list'], + // 'credential-stores': ['create', 'list'], + }; + }, + /** * Generates realistic-ish IDs while still being deterministic. */ diff --git a/package.json b/package.json index 66eff112b..d5070c4b8 100755 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "minimist": "^1.2.3", "normalize-url": "^4.5.1", "glob-parent": "^5.1.2", - "tar": "^6.1.2", + "tar": "^6.1.9", "path-parse": "^1.0.7" }, "config": { diff --git a/ui/admin/app/routes/scopes/scope/groups.js b/ui/admin/app/routes/scopes/scope/groups.js index 7fa556182..1784799cc 100644 --- a/ui/admin/app/routes/scopes/scope/groups.js +++ b/ui/admin/app/routes/scopes/scope/groups.js @@ -11,7 +11,7 @@ export default class ScopesScopeGroupsRoute extends Route { @service intl; @service notify; @service session; - + @service can; // =methods /** @@ -26,8 +26,11 @@ export default class ScopesScopeGroupsRoute extends Route { * @return {Promise[GroupModel]} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('group', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if (this.can.can('list collection', scope, { collection: 'groups' })) { + return this.store.query('group', { scope_id }); + } } // =actions diff --git a/ui/admin/app/routes/scopes/scope/groups/index.js b/ui/admin/app/routes/scopes/scope/groups/index.js index 7d2a20fb0..22e3d3593 100644 --- a/ui/admin/app/routes/scopes/scope/groups/index.js +++ b/ui/admin/app/routes/scopes/scope/groups/index.js @@ -1,3 +1,10 @@ import Route from '@ember/routing/route'; +export default class ScopesScopeGroupsIndexRoute extends Route { + // =methods -export default class ScopesScopeGroupsIndexRoute extends Route {} + setupController(controller) { + const scope = this.modelFor('scopes.scope'); + super.setupController(...arguments); + controller.setProperties({ scope }); + } +} diff --git a/ui/admin/app/routes/scopes/scope/roles.js b/ui/admin/app/routes/scopes/scope/roles.js index 88d05373b..cbc1dfcc5 100644 --- a/ui/admin/app/routes/scopes/scope/roles.js +++ b/ui/admin/app/routes/scopes/scope/roles.js @@ -11,6 +11,7 @@ export default class ScopesScopeRolesRoute extends Route { @service intl; @service notify; @service session; + @service can; // =methods @@ -26,8 +27,11 @@ export default class ScopesScopeRolesRoute extends Route { * @return {Promise{[RoleModel]}} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('role', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if (this.can.can('list collection', scope, { collection: 'roles' })) { + return this.store.query('role', { scope_id }); + } } // =actions diff --git a/ui/admin/app/routes/scopes/scope/roles/index.js b/ui/admin/app/routes/scopes/scope/roles/index.js index f2a8d7af1..56c143beb 100644 --- a/ui/admin/app/routes/scopes/scope/roles/index.js +++ b/ui/admin/app/routes/scopes/scope/roles/index.js @@ -1,3 +1,10 @@ import Route from '@ember/routing/route'; +export default class ScopesScopeRolesIndexRoute extends Route { + // =methods -export default class ScopesScopeRolesIndexRoute extends Route {} + setupController(controller) { + const scope = this.modelFor('scopes.scope'); + super.setupController(...arguments); + controller.setProperties({ scope }); + } +} diff --git a/ui/admin/app/routes/scopes/scope/users.js b/ui/admin/app/routes/scopes/scope/users.js index 42f9c8832..aa3dde96a 100644 --- a/ui/admin/app/routes/scopes/scope/users.js +++ b/ui/admin/app/routes/scopes/scope/users.js @@ -11,6 +11,7 @@ export default class ScopesScopeUsersRoute extends Route { @service intl; @service notify; @service session; + @service can; // =methods @@ -26,8 +27,11 @@ export default class ScopesScopeUsersRoute extends Route { * @return {Promise{[UserModel]}} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('user', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if (this.can.can('list collection', scope, { collection: 'users' })) { + return this.store.query('user', { scope_id }); + } } // =actions diff --git a/ui/admin/app/routes/scopes/scope/users/index.js b/ui/admin/app/routes/scopes/scope/users/index.js index f59170106..2e92b693d 100644 --- a/ui/admin/app/routes/scopes/scope/users/index.js +++ b/ui/admin/app/routes/scopes/scope/users/index.js @@ -1,3 +1,11 @@ import Route from '@ember/routing/route'; -export default class ScopesScopeUsersIndexRoute extends Route {} +export default class ScopesScopeUsersIndexRoute extends Route { + // =methods + + setupController(controller) { + const scope = this.modelFor('scopes.scope'); + super.setupController(...arguments); + controller.setProperties({ scope }); + } +} diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index 1f82180fc..0d47d1a2c 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -96,18 +96,27 @@ {{#unless @model.isProject}} - - - {{t 'resources.user.title_plural'}} - - - - {{t 'resources.group.title_plural'}} - - - - {{t 'resources.role.title_plural'}} - + {{#if (can 'navigate collection' @model collection='users')}} + + + {{t 'resources.user.title_plural'}} + + {{/if}} + {{#if (can 'navigate collection' @model collection='groups')}} + + + {{t 'resources.group.title_plural'}} + + {{/if}} + {{#if (can 'navigate collection' @model collection='roles')}} + + + {{t 'resources.role.title_plural'}} + + {{/if}} {{t 'resources.auth-method.title_plural'}} diff --git a/ui/admin/app/templates/scopes/scope/groups/index.hbs b/ui/admin/app/templates/scopes/scope/groups/index.hbs index 7c25937c0..1e5f038db 100644 --- a/ui/admin/app/templates/scopes/scope/groups/index.hbs +++ b/ui/admin/app/templates/scopes/scope/groups/index.hbs @@ -13,10 +13,12 @@ - {{#if @model}} - {{t - 'titles.new' - }} + {{#if (can 'create collection' this.scope collection='groups')}} + {{#if @model}} + {{t + 'titles.new' + }} + {{/if}} {{/if}} @@ -70,11 +72,29 @@ > {{t 'resources.group.description'}} + {{#if (can 'list collection' this.scope collection='groups')}} + {{! can list (at least): show default welcome message}} + {{t 'resources.group.description'}} + {{else if (can 'create collection' this.scope collection='groups')}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.group.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.group.title_plural') + }} + {{/if}} - - - {{t 'titles.new'}} - + {{#if (can 'create collection' this.scope collection='groups')}} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/app/templates/scopes/scope/roles/index.hbs b/ui/admin/app/templates/scopes/scope/roles/index.hbs index ae8157f05..241093ea4 100644 --- a/ui/admin/app/templates/scopes/scope/roles/index.hbs +++ b/ui/admin/app/templates/scopes/scope/roles/index.hbs @@ -15,10 +15,12 @@ - {{#if @model}} - {{t - 'titles.new' - }} + {{#if (can 'create collection' this.scope collection='roles')}} + {{#if @model}} + {{t + 'titles.new' + }} + {{/if}} {{/if}} @@ -68,12 +70,29 @@ as |message| > - {{t 'resources.role.description'}} + {{#if (can 'list collection' this.scope collection='roles')}} + {{! can list (at least): show default welcome message}} + {{t 'resources.roles.description'}} + {{else if (can 'create collection' this.scope collection='roles')}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.role.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.role.title_plural') + }} + {{/if}} - - - {{t 'titles.new'}} - + {{#if (can 'create collection' this.scope collection='roles')}} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/app/templates/scopes/scope/users/index.hbs b/ui/admin/app/templates/scopes/scope/users/index.hbs index b0cf3e83b..fdc93dbb3 100644 --- a/ui/admin/app/templates/scopes/scope/users/index.hbs +++ b/ui/admin/app/templates/scopes/scope/users/index.hbs @@ -15,10 +15,12 @@ - {{#if @model}} - {{t - 'titles.new' - }} + {{#if (can 'create collection' this.scope collection='users')}} + {{#if @model}} + {{t + 'titles.new' + }} + {{/if}} {{/if}} @@ -68,12 +70,29 @@ as |message| > - {{t 'resources.user.description'}} + {{#if (can 'list collection' this.scope collection='users')}} + {{! can list (at least): show default welcome message}} + {{t 'resources.user.description'}} + {{else if (can 'create collection' this.scope collection='users')}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.user.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.user.title_plural') + }} + {{/if}} - - - {{t 'titles.new'}} - + {{#if (can 'create collection' this.scope collection='users')}} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/tests/acceptance/groups-test.js b/ui/admin/tests/acceptance/groups/create-test.js similarity index 71% rename from ui/admin/tests/acceptance/groups-test.js rename to ui/admin/tests/acceptance/groups/create-test.js index 38b95d60d..815e4a760 100644 --- a/ui/admin/tests/acceptance/groups-test.js +++ b/ui/admin/tests/acceptance/groups/create-test.js @@ -10,9 +10,10 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | groups', function (hooks) { +module('Acceptance | groups | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + let orgURL; const instances = { scopes: { @@ -20,9 +21,9 @@ module('Acceptance | groups', function (hooks) { org: null, }, group: null, + orgScope: null, }; const urls = { - orgScope: null, groups: null, group: null, newGroup: null, @@ -30,17 +31,21 @@ module('Acceptance | groups', function (hooks) { hooks.beforeEach(function () { authenticateSession({}); - instances.scopes.global = this.server.create('scope', { id: 'global' }); - instances.scopes.org = this.server.create('scope', { - type: 'org', - scope: { id: 'global', type: 'global' }, - }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); instances.group = this.server.create('group', { - scope: instances.scopes.org, + scope: instances.orgScope, }); - urls.groups = `/scopes/${instances.scopes.org.id}/groups`; + urls.groups = `/scopes/${instances.orgScope.id}/groups`; urls.group = `${urls.groups}/${instances.group.id}`; urls.newGroup = `${urls.groups}/new`; + orgURL = `/scopes/${instances.orgScope.id}`; }); test('can create new group', async function (assert) { @@ -52,6 +57,24 @@ module('Acceptance | groups', function (hooks) { assert.equal(this.server.db.groups.length, groupsCount + 1); }); + test('can navigate to new groups route with proper authorization', async function (assert) { + assert.expect(2); + await visit(orgURL); + assert.ok( + instances.orgScope.authorized_collection_actions.groups.includes('create') + ); + assert.ok(find(`[href="${urls.groups}"]`)); + }); + + test('cannot navigate to new groups route without proper authorization', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions.groups = []; + await visit(orgURL); + assert.notOk( + instances.orgScope.authorized_collection_actions.groups.includes('create') + ); + assert.notOk(find(`[href="${urls.groups}"]`)); + }); test('can cancel new group creation', async function (assert) { assert.expect(2); const groupsCount = this.server.db.groups.length; diff --git a/ui/admin/tests/acceptance/groups/list-test.js b/ui/admin/tests/acceptance/groups/list-test.js new file mode 100644 index 000000000..f9229c19d --- /dev/null +++ b/ui/admin/tests/acceptance/groups/list-test.js @@ -0,0 +1,76 @@ +import { module, test } from 'qunit'; +import { visit, find } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | groups | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + let orgURL; + + const instances = { + scopes: { + global: null, + org: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + }, + groups: null, + }; + + hooks.beforeEach(function () { + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); + instances.group = this.server.create('group', { + scope: instances.orgScope, + }); + orgURL = `/scopes/${instances.orgScope.id}`; + + urls.groups = `/scopes/${instances.orgScope.id}/groups`; + authenticateSession({}); + }); + + test('can navigate to groups with proper authorization', async function (assert) { + assert.expect(2); + await visit(orgURL); + assert.ok( + instances.orgScope.authorized_collection_actions.groups.includes('list') + ); + assert.ok(find(`[href="${urls.groups}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions.groups = []; + await visit(orgURL); + assert.notOk( + instances.orgScope.authorized_collection_actions.groups.includes('list') + ); + assert.notOk(find(`[href="${urls.groups}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.orgScope.authorized_collection_actions.groups = ['create']; + await visit(orgURL); + assert.ok(find(`[href="${urls.groups}"]`)); + }); +}); diff --git a/ui/admin/tests/acceptance/roles-test.js b/ui/admin/tests/acceptance/roles/create-test.js similarity index 70% rename from ui/admin/tests/acceptance/roles-test.js rename to ui/admin/tests/acceptance/roles/create-test.js index 77b971f46..a0564f67d 100644 --- a/ui/admin/tests/acceptance/roles-test.js +++ b/ui/admin/tests/acceptance/roles/create-test.js @@ -11,7 +11,7 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | roles', function (hooks) { +module('Acceptance | roles | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -22,38 +22,44 @@ module('Acceptance | roles', function (hooks) { project: null, }, role: null, + orgScope: null, }; const urls = { - orgScope: null, roles: null, role: null, newRole: null, + orgScope: null, }; hooks.beforeEach(function () { authenticateSession({}); - instances.scopes.global = this.server.create('scope', { id: 'global' }); - instances.scopes.org = this.server.create('scope', { - type: 'org', - scope: { id: 'global', type: 'global' }, - }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); + // The project scope is not yet used for role tests (though it will be // in the future). This is created simply to test the grant scope loading // mechanism. instances.scopes.project = this.server.create('scope', { type: 'project', - scope: { id: instances.scopes.org.id, type: instances.scopes.org.type }, + scope: { id: instances.orgScope.id, type: instances.orgScope.type }, }); instances.role = this.server.create( 'role', { - scope: instances.scopes.org, + scope: instances.orgScope, }, 'withPrincipals' ); - urls.roles = `/scopes/${instances.scopes.org.id}/roles`; + urls.roles = `/scopes/${instances.orgScope.id}/roles`; urls.role = `${urls.roles}/${instances.role.id}`; urls.newRole = `${urls.roles}/new`; + urls.orgScope = `/scopes/${instances.orgScope.id}`; }); test('can create new role', async function (assert) { @@ -65,6 +71,25 @@ module('Acceptance | roles', function (hooks) { assert.equal(this.server.db.roles.length, rolesCount + 1); }); + test('Users can navigate to new roles route with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.orgScope); + assert.ok( + instances.orgScope.authorized_collection_actions.roles.includes('create') + ); + assert.ok(find(`[href="${urls.roles}"]`)); + }); + + test('Users cannot navigate to new roles route without proper authorization', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions.roles = []; + await visit(urls.orgScope); + assert.notOk( + instances.orgScope.authorized_collection_actions.roles.includes('create') + ); + assert.notOk(find(`[href="${urls.roles}"]`)); + }); + test('can cancel new role creation', async function (assert) { assert.expect(2); const rolesCount = this.server.db.roles.length; diff --git a/ui/admin/tests/acceptance/roles/list-test.js b/ui/admin/tests/acceptance/roles/list-test.js new file mode 100644 index 000000000..ff170aeb7 --- /dev/null +++ b/ui/admin/tests/acceptance/roles/list-test.js @@ -0,0 +1,75 @@ +import { module, test } from 'qunit'; +import { visit, find } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | roles | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + }, + roles: null, + orgScope: null, + }; + + hooks.beforeEach(function () { + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); + instances.role = this.server.create('role', { + scope: instances.orgScope, + }); + urls.orgScope = `/scopes/${instances.orgScope.id}`; + + urls.roles = `/scopes/${instances.orgScope.id}/roles`; + authenticateSession({}); + }); + + test('Users can navigate to roles with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.orgScope); + assert.ok( + instances.orgScope.authorized_collection_actions.roles.includes('list') + ); + assert.ok(find(`[href="${urls.roles}"]`)); + }); + + test('Users cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions.roles = []; + await visit(urls.orgScope); + assert.notOk( + instances.orgScope.authorized_collection_actions.roles.includes('list') + ); + assert.notOk(find(`[href="${urls.roles}"]`)); + }); + + test('Users can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.orgScope.authorized_collection_actions.roles = ['create']; + await visit(urls.orgScope); + assert.ok(find(`[href="${urls.roles}"]`)); + }); +}); diff --git a/ui/admin/tests/acceptance/users-test.js b/ui/admin/tests/acceptance/users/create-test.js similarity index 75% rename from ui/admin/tests/acceptance/users-test.js rename to ui/admin/tests/acceptance/users/create-test.js index b047a0d2a..fbff7fbee 100644 --- a/ui/admin/tests/acceptance/users-test.js +++ b/ui/admin/tests/acceptance/users/create-test.js @@ -10,11 +10,12 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | users', function (hooks) { +module('Acceptance | users | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); let orgScope; + let orgURL; let usersURL; let newUserURL; @@ -23,10 +24,12 @@ module('Acceptance | users', function (hooks) { 'scope', { type: 'org', + scope: { id: 'global', type: 'global' }, }, 'withChildren' ); + orgURL = `/scopes/${orgScope.id}`; usersURL = `/scopes/${orgScope.id}/users`; newUserURL = `${usersURL}/new`; @@ -42,6 +45,23 @@ module('Acceptance | users', function (hooks) { assert.equal(this.server.db.users.length, usersCount + 1); }); + test('can navigate to new users route with proper authorization', async function (assert) { + assert.expect(2); + await visit(orgURL); + assert.ok(orgScope.authorized_collection_actions.users.includes('create')); + assert.ok(find(`[href="${orgURL}/users"]`)); + }); + + test('cannot navigate to new users route without proper authorization', async function (assert) { + assert.expect(2); + orgScope.authorized_collection_actions.users = []; + await visit(orgURL); + assert.notOk( + orgScope.authorized_collection_actions.users.includes('create') + ); + assert.notOk(find(`[href="${orgURL}/users"]`)); + }); + test('can cancel creation of a new user', async function (assert) { assert.expect(2); const usersCount = this.server.db.users.length; diff --git a/ui/admin/tests/acceptance/users/list-test.js b/ui/admin/tests/acceptance/users/list-test.js new file mode 100644 index 000000000..72a6e35eb --- /dev/null +++ b/ui/admin/tests/acceptance/users/list-test.js @@ -0,0 +1,75 @@ +import { module, test } from 'qunit'; +import { visit, find } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | users | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + let orgURL; + + const instances = { + scopes: { + global: null, + org: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + }, + users: null, + }; + + hooks.beforeEach(function () { + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); + instances.user = this.server.create('user', { + scope: instances.orgScope, + }); + orgURL = `/scopes/${instances.orgScope.id}`; + urls.users = `/scopes/${instances.orgScope.id}/users`; + authenticateSession({}); + }); + + test('can navigate to users with proper authorization', async function (assert) { + assert.expect(2); + await visit(orgURL); + assert.ok( + instances.orgScope.authorized_collection_actions.users.includes('list') + ); + assert.ok(find(`[href="${urls.users}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions.users = []; + await visit(orgURL); + assert.notOk( + instances.orgScope.authorized_collection_actions.users.includes('list') + ); + assert.notOk(find(`[href="${urls.users}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.orgScope.authorized_collection_actions.users = ['create']; + await visit(orgURL); + assert.ok(find(`[href="${urls.users}"]`)); + }); +}); diff --git a/ui/desktop/electron-app/package.json b/ui/desktop/electron-app/package.json index c0e9b5672..a91471f79 100644 --- a/ui/desktop/electron-app/package.json +++ b/ui/desktop/electron-app/package.json @@ -46,6 +46,7 @@ "trim-newlines": "^3.0.1", "normalize-url": "^4.5.1", "jszip": "^3.7.0", - "path-parse": "^1.0.7" + "path-parse": "^1.0.7", + "tar": "^6.1.9" } } diff --git a/ui/desktop/electron-app/yarn.lock b/ui/desktop/electron-app/yarn.lock index ed7fb79aa..c4bd48ac9 100644 --- a/ui/desktop/electron-app/yarn.lock +++ b/ui/desktop/electron-app/yarn.lock @@ -694,11 +694,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -1503,13 +1498,6 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -2334,14 +2322,6 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1. resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" @@ -2349,13 +2329,6 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -2364,7 +2337,7 @@ minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4: +mkdirp@^0.5.1, mkdirp@^0.5.4: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -3494,23 +3467,10 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" -tar@^4: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - -tar@^6.0.2, tar@^6.0.5: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== +tar@^4, tar@^6.0.2, tar@^6.0.5, tar@^6.1.9: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -3779,7 +3739,7 @@ xmlbuilder@^9.0.7: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xmldom@^0.5.0, xmldom@~0.5.0: +xmldom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== @@ -3806,11 +3766,6 @@ yaku@^0.16.6: resolved "https://registry.yarnpkg.com/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e" integrity sha1-HRlceKqbW/hHnIlblQT9TwhHmE4= -yallist@^3.0.0, yallist@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" diff --git a/yarn.lock b/yarn.lock index 74e7d3c67..436f898c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20015,10 +20015,10 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4, tar@^4.4.10, tar@^4.4.12, tar@^4.4.15, tar@^5.0.5, tar@^6.0.2, tar@^6.0.5, tar@^6.1.0, tar@^6.1.2: - version "6.1.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.8.tgz#4fc50cfe56511c538ce15b71e05eebe66530cbd4" - integrity sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A== +tar@^4, tar@^4.4.10, tar@^4.4.12, tar@^4.4.15, tar@^5.0.5, tar@^6.0.2, tar@^6.0.5, tar@^6.1.0, tar@^6.1.9: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0"