From c971d6d2ceb1137ca3d5f1a4a6a5868e59a2e53d Mon Sep 17 00:00:00 2001 From: dharini Date: Mon, 30 Aug 2021 19:16:58 -0700 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20auth=20methods=20c?= =?UTF-8?q?apability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/abilities/collection.js | 3 +- addons/api/addon/models/base.js | 5 + addons/api/mirage/factories/scope.js | 5 + addons/core/translations/en-us.yaml | 2 + .../app/routes/scopes/scope/auth-methods.js | 10 +- ui/admin/app/templates/scopes/scope.hbs | 10 +- .../scopes/scope/auth-methods/index.hbs | 66 ++++++++---- .../create-test.js} | 100 +++++++++--------- .../acceptance/auth-methods/list-test.js | 87 +++++++++++++++ 9 files changed, 210 insertions(+), 78 deletions(-) rename ui/admin/tests/acceptance/{auth-methods-test.js => auth-methods/create-test.js} (69%) create mode 100644 ui/admin/tests/acceptance/auth-methods/list-test.js diff --git a/addons/api/addon/abilities/collection.js b/addons/api/addon/abilities/collection.js index ad52afd18..92f0e4342 100644 --- a/addons/api/addon/abilities/collection.js +++ b/addons/api/addon/abilities/collection.js @@ -29,7 +29,8 @@ export default class CollectionAbility extends Ability { * @return {boolean} */ hasAuthorizedCollectionAction(action) { - const { authorized_collection_actions } = this.model; + const authorized_collection_actions = + (this.model || {}).authorized_collection_actions || {}; const collection = this.collection || {}; return authorized_collection_actions[collection]?.includes(action); } diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index f99e0351a..7fd41ec64 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -42,6 +42,11 @@ export default class BaseModel extends Model { }) authorized_actions; + @attr({ + readOnly: true, + }) + authorized_collection_actions; + set scopeModel(model) { if (model) { const json = model.serialize(); diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index fe41f3866..a3081887b 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -4,6 +4,11 @@ import { trait } from 'ember-cli-mirage'; export default factory.extend({ type: 'global', + authorized_collection_actions: () => { + return { + 'auth-methods': ['create', 'list'], + }; + }, /** * Generates realistic-ish IDs while still being deterministic. */ diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 5370c7e75..8f98f633c 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -32,6 +32,8 @@ titles: descriptions: empty-set: There are no items to display yet. You may be able to add items or try back later. origin-initialization: To get started, please enter your origin URL + create-but-not-list: You do not have permission to list {resource}, but you can create new instances. + neither-list-nor-create: You do not have permission to list or create {resource}. questions: delete-confirm: Are you sure you want to delete this resource? remove-confirm: Are you sure you want to remove this association? diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js index 31c0778a5..938ce6d16 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods.js @@ -11,6 +11,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route { @service intl; @service notify; @service session; + @service can; // =methods @@ -26,8 +27,13 @@ export default class ScopesScopeAuthMethodsRoute extends Route { * @return {Promise[AuthMethodModel]} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('auth-method', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if ( + this.can.can('list collection', scope, { collection: 'auth-methods' }) + ) { + return this.store.query('auth-method', { scope_id }); + } } // =actions diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index 65e456c51..30e63cc29 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -106,10 +106,12 @@ {{t 'resources.role.title_plural'}} - - - {{t 'resources.auth-method.title_plural'}} - + {{#if (can 'navigate collection' @model collection='auth-methods')}} + + + {{t 'resources.auth-method.title_plural'}} + + {{/if}} {{/unless}} diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs index 4bd1bd2a4..a795d6449 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -12,25 +12,30 @@ + {{#if @model}} - - - {{t 'resources.auth-method.types.password'}} - - - {{t 'resources.auth-method.types.oidc'}} - - + {{#if (can 'create collection' this.scope collection='auth-methods')}} + {{#if @model}} + + + {{t 'resources.auth-method.types.password'}} + + + {{t 'resources.auth-method.types.oidc'}} + + + {{/if}} + {{/if}} {{/if}} @@ -165,7 +170,24 @@ as |message| > - {{t 'resources.auth-method.messages.welcome.description'}} + {{#if (can 'list collection' this.scope collection='auth-methods')}} + {{! can list (at least): show default welcome message}} + {{t 'resources.auth-method.messages.welcome.description'}} + {{else if (can + 'create collection' this.scope collection='auth-methods' + )}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.auth-method.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.auth-method.title_plural') + }} + {{/if}} {{t 'titles.new'}} + {{#if (can 'create collection' this.scope collection='auth-methods')}} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/tests/acceptance/auth-methods-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js similarity index 69% rename from ui/admin/tests/acceptance/auth-methods-test.js rename to ui/admin/tests/acceptance/auth-methods/create-test.js index 61d8071a3..55a76243b 100644 --- a/ui/admin/tests/acceptance/auth-methods-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -2,7 +2,6 @@ import { module, test } from 'qunit'; import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; import { authenticateSession, @@ -12,7 +11,7 @@ import { } from 'ember-simple-auth/test-support'; import { enableFeature } from 'ember-feature-flags/test-support'; -module('Acceptance | auth methods', function (hooks) { +module('Acceptance | auth-methods | create ', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -22,66 +21,35 @@ module('Acceptance | auth methods', function (hooks) { org: null, }, authMethod: null, + orgScope: null, }; + const urls = { orgScope: null, authMethods: null, newAuthMethod: null, authMethod: null, + orgURL: null, }; hooks.beforeEach(function () { - // Setup Mirage mock resources for this test authenticateSession({}); - instances.scopes.global = this.server.create('scope', { id: 'global' }); - instances.scopes.org = this.server.create('scope', { + instances.orgScope = this.server.create('scope', { type: 'org', scope: { id: 'global', type: 'global' }, }); + instances.authMethod = this.server.create('auth-method', { - scope: instances.scopes.org, + scope: instances.orgScope, }); // Generate route URLs for resources - urls.orgScope = `/scopes/${instances.scopes.org.id}`; - urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.orgURL = `/scopes/${instances.orgScope.id}`; + urls.authMethods = `${urls.orgURL}/auth-methods`; urls.newAuthMethod = `${urls.authMethods}/new?type=password`; urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; }); - test('visiting auth methods', async function (assert) { - assert.expect(1); - await visit(urls.authMethods); - await a11yAudit(); - assert.equal(currentURL(), urls.authMethods); - }); - - test('can navigate to an auth method form', async function (assert) { - assert.expect(1); - await visit(urls.authMethods); - await click('main tbody .rose-table-header-cell:nth-child(1) a'); - await a11yAudit(); - assert.equal(currentURL(), urls.authMethod); - }); - - test('can update an auth method and save changes', async function (assert) { - assert.expect(1); - await visit(urls.authMethod); - await click('form [type="button"]', 'Activate edit mode'); - await fillIn('[name="name"]', 'update name'); - await click('form [type="submit"]:not(:disabled)'); - assert.equal(this.server.db.authMethods[0].name, 'update name'); - }); - - test('can update an auth method and cancel changes', async function (assert) { - assert.expect(1); - await visit(urls.authMethod); - await click('form [type="button"]', 'Activate edit mode'); - await fillIn('[name="name"]', 'update name'); - await click('form button:not([type="submit"])'); - assert.notEqual(this.server.db.authMethods[0].name, 'update name'); - }); - test('can create new auth method', async function (assert) { assert.expect(1); const authMethodsCount = this.server.db.authMethods.length; @@ -92,6 +60,34 @@ module('Acceptance | auth methods', function (hooks) { assert.equal(this.server.db.authMethods.length, authMethodsCount + 1); }); + test('can navigate to new auth-methods route with proper authorization', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions['auth-methods'] = [ + 'create', + 'list', + ]; + await visit(urls.orgURL); + assert.ok( + instances.orgScope.authorized_collection_actions['auth-methods'].includes( + 'create' + ) + ); + console.log(urls.orgURL); + assert.ok(find(`[href="${urls.authMethods}"]`)); + }); + + test('cannot navigate to new auth-methods route without proper authorization', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions['auth-methods'] = []; + await visit(urls.orgURL); + assert.notOk( + instances.orgScope.authorized_collection_actions['auth-methods'].includes( + 'create' + ) + ); + assert.notOk(find(`[href="${urls.authMethods}"]`)); + }); + test('can cancel new auth method creation', async function (assert) { assert.expect(2); const authMethodsCount = this.server.db.authMethods.length; @@ -106,7 +102,7 @@ module('Acceptance | auth methods', function (hooks) { test('user can make primary an auth method', async function (assert) { assert.expect(2); assert.notOk( - instances.scopes.org.primaryAuthMethodId, + instances.orgScope.primaryAuthMethodId, 'Primary auth method is not yet set.' ); enableFeature('primary-auth-method'); @@ -114,7 +110,7 @@ module('Acceptance | auth methods', function (hooks) { await click( '.rose-layout-page-actions .rose-dropdown-content [type="button"]:first-child' ); - const scope = this.server.schema.scopes.find(instances.scopes.org.id); + const scope = this.server.schema.scopes.find(instances.orgScope.id); assert.equal( scope.primaryAuthMethodId, instances.authMethod.id, @@ -136,7 +132,7 @@ module('Acceptance | auth methods', function (hooks) { ); }); assert.notOk( - instances.scopes.org.primaryAuthMethodId, + instances.orgScope.primaryAuthMethodId, 'Primary auth method is not yet set.' ); enableFeature('primary-auth-method'); @@ -149,11 +145,11 @@ module('Acceptance | auth methods', function (hooks) { test('user can remove as primary an auth method', async function (assert) { assert.expect(2); - instances.scopes.org.update({ + instances.orgScope.update({ primaryAuthMethodId: instances.authMethod.id, }); assert.ok( - instances.scopes.org.primaryAuthMethodId, + instances.orgScope.primaryAuthMethodId, 'Primary auth method is set.' ); enableFeature('primary-auth-method'); @@ -161,7 +157,7 @@ module('Acceptance | auth methods', function (hooks) { await click( '.rose-layout-page-actions .rose-dropdown-content [type="button"]:first-child' ); - const scope = this.server.schema.scopes.find(instances.scopes.org.id); + const scope = this.server.schema.scopes.find(instances.orgScope.id); assert.notOk(scope.primaryAuthMethodId, 'Primary auth method is unset.'); }); @@ -178,11 +174,11 @@ module('Acceptance | auth methods', function (hooks) { } ); }); - instances.scopes.org.update({ + instances.orgScope.update({ primaryAuthMethodId: instances.authMethod.id, }); assert.ok( - instances.scopes.org.primaryAuthMethodId, + instances.orgScope.primaryAuthMethodId, 'Primary auth method is set.' ); enableFeature('primary-auth-method'); @@ -196,7 +192,7 @@ module('Acceptance | auth methods', function (hooks) { test('user can make and remove primary auth methods from index', async function (assert) { assert.expect(3); assert.notOk( - instances.scopes.org.primaryAuthMethodId, + instances.orgScope.primaryAuthMethodId, 'Primary auth method is not yet set.' ); enableFeature('primary-auth-method'); @@ -204,7 +200,7 @@ module('Acceptance | auth methods', function (hooks) { await click( '.rose-table-body .rose-table-row:first-child .rose-dropdown-content [type="button"]:first-child' ); - let scope = this.server.schema.scopes.find(instances.scopes.org.id); + let scope = this.server.schema.scopes.find(instances.orgScope.id); assert.equal( scope.primaryAuthMethodId, instances.authMethod.id, @@ -213,7 +209,7 @@ module('Acceptance | auth methods', function (hooks) { await click( '.rose-table-body .rose-table-row:first-child .rose-dropdown-content [type="button"]:first-child' ); - scope = this.server.schema.scopes.find(instances.scopes.org.id); + scope = this.server.schema.scopes.find(instances.orgScope.id); assert.notOk(scope.primaryAuthMethodId, 'Primary auth method is unset.'); }); }); diff --git a/ui/admin/tests/acceptance/auth-methods/list-test.js b/ui/admin/tests/acceptance/auth-methods/list-test.js new file mode 100644 index 000000000..bd9a8dfcd --- /dev/null +++ b/ui/admin/tests/acceptance/auth-methods/list-test.js @@ -0,0 +1,87 @@ +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 | auth-methods | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + let orgURL; + + const instances = { + scopes: { + global: null, + org: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + }, + authMethods: 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.org = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); + + orgURL = `/scopes/${instances.orgScope.id}`; + urls.authMethods = `/scopes/${instances.orgScope.id}/auth-methods`; + 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['auth-methods'].includes( + 'list' + ) + ); + assert.ok(find(`[href="${urls.authMethods}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.orgScope.authorized_collection_actions['auth-methods'] = []; + await visit(orgURL); + assert.notOk( + instances.orgScope.authorized_collection_actions['auth-methods'].includes( + 'list' + ) + ); + assert.notOk(find(`[href="${urls.authMethods}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.orgScope.authorized_collection_actions['auth-methods'] = [ + 'create', + ]; + await visit(orgURL); + assert.ok(find(`[href="${urls.authMethods}"]`)); + }); +}); From ab1a642f6637f0e2504ba86fc323eb76c70b85d8 Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 10:56:54 -0700 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20=F0=9F=90=9B=20show=20dropdown=20l?= =?UTF-8?q?ink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routes/scopes/scope/auth-methods/index.js | 5 +++ .../scopes/scope/auth-methods/index.hbs | 40 +++++++++---------- ui/admin/jsconfig.json | 1 + .../acceptance/auth-methods/list-test.js | 2 +- 4 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 ui/admin/jsconfig.json diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/index.js b/ui/admin/app/routes/scopes/scope/auth-methods/index.js index 913b4f8ff..0d727524a 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/index.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/index.js @@ -12,4 +12,9 @@ export default class ScopesScopeAuthMethodsIndexRoute extends Route { const scopeModel = this.modelFor('scopes.scope'); controller.scopeModel = scopeModel; } + // setupController(controller) { + // const scope = this.modelFor('scopes.scope'); + // super.setupController(...arguments); + // controller.setProperties({ scope }); + // } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs index a795d6449..566c844e8 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -13,28 +13,26 @@ - {{#if @model}} - {{#if (can 'create collection' this.scope collection='auth-methods')}} - {{#if @model}} - + - - {{t 'resources.auth-method.types.password'}} - - - {{t 'resources.auth-method.types.oidc'}} - - - {{/if}} + {{t 'resources.auth-method.types.password'}} + + + {{t 'resources.auth-method.types.oidc'}} + + {{/if}} {{/if}} diff --git a/ui/admin/jsconfig.json b/ui/admin/jsconfig.json new file mode 100644 index 000000000..f408cac8c --- /dev/null +++ b/ui/admin/jsconfig.json @@ -0,0 +1 @@ +{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} \ No newline at end of file diff --git a/ui/admin/tests/acceptance/auth-methods/list-test.js b/ui/admin/tests/acceptance/auth-methods/list-test.js index bd9a8dfcd..1dbe3f23f 100644 --- a/ui/admin/tests/acceptance/auth-methods/list-test.js +++ b/ui/admin/tests/acceptance/auth-methods/list-test.js @@ -53,7 +53,7 @@ module('Acceptance | auth-methods | list', function (hooks) { authenticateSession({}); }); - test('can navigate to users with proper authorization', async function (assert) { + test('can navigate to auth methods with proper authorization', async function (assert) { assert.expect(2); await visit(orgURL); assert.ok( From 3ae2423aa412c663e6aba291093a29c79b57479e Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 11:06:16 -0700 Subject: [PATCH 03/16] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20unnecessar?= =?UTF-8?q?y=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/app/routes/scopes/scope/auth-methods/index.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/index.js b/ui/admin/app/routes/scopes/scope/auth-methods/index.js index 0d727524a..913b4f8ff 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/index.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/index.js @@ -12,9 +12,4 @@ export default class ScopesScopeAuthMethodsIndexRoute extends Route { const scopeModel = this.modelFor('scopes.scope'); controller.scopeModel = scopeModel; } - // setupController(controller) { - // const scope = this.modelFor('scopes.scope'); - // super.setupController(...arguments); - // controller.setProperties({ scope }); - // } } From 09d9432825a52c2bd4987ece77069970868cd4ce Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 11:13:27 -0700 Subject: [PATCH 04/16] =?UTF-8?q?fix:=20=F0=9F=90=9B=20del=20jsconfig=20fi?= =?UTF-8?q?le?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/jsconfig.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 ui/admin/jsconfig.json diff --git a/ui/admin/jsconfig.json b/ui/admin/jsconfig.json deleted file mode 100644 index f408cac8c..000000000 --- a/ui/admin/jsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} \ No newline at end of file From f344d73f32d9e269e85522482141521f98a2d80d Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 15:17:57 -0700 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20host=20catalogs=20?= =?UTF-8?q?create/list=20capability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/abilities/collection.js | 3 +- addons/api/addon/models/base.js | 7 +- addons/api/mirage/factories/scope.js | 5 + addons/core/translations/en-us.yaml | 2 + .../app/routes/scopes/scope/host-catalogs.js | 11 ++- .../scopes/scope/host-catalogs/index.js | 8 +- ui/admin/app/templates/scopes/scope.hbs | 10 +- .../scopes/scope/host-catalogs/index.hbs | 47 ++++++--- .../create-test.js} | 41 +++++++- .../acceptance/host-catalogs/list-test.js | 98 +++++++++++++++++++ 10 files changed, 207 insertions(+), 25 deletions(-) rename ui/admin/tests/acceptance/{host-catalogs-test.js => host-catalogs/create-test.js} (74%) create mode 100644 ui/admin/tests/acceptance/host-catalogs/list-test.js diff --git a/addons/api/addon/abilities/collection.js b/addons/api/addon/abilities/collection.js index ad52afd18..92f0e4342 100644 --- a/addons/api/addon/abilities/collection.js +++ b/addons/api/addon/abilities/collection.js @@ -29,7 +29,8 @@ export default class CollectionAbility extends Ability { * @return {boolean} */ hasAuthorizedCollectionAction(action) { - const { authorized_collection_actions } = this.model; + const authorized_collection_actions = + (this.model || {}).authorized_collection_actions || {}; const collection = this.collection || {}; return authorized_collection_actions[collection]?.includes(action); } diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index f99e0351a..5ce5d3a8c 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -42,6 +42,11 @@ export default class BaseModel extends Model { }) authorized_actions; + @attr({ + readOnly: true, + }) + authorized_collection_actions; + set scopeModel(model) { if (model) { const json = model.serialize(); @@ -52,7 +57,7 @@ export default class BaseModel extends Model { /** * Convenience for getting and setting the parent scope ID. - * @type {string + * @type {string} */ @computed('scope.scope_id') get scopeID() { diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index fe41f3866..cfed04b66 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -3,6 +3,11 @@ import { trait } from 'ember-cli-mirage'; export default factory.extend({ type: 'global', + authorized_collection_actions: () => { + return { + 'host-catalogs': ['create', 'list'], + }; + }, /** * Generates realistic-ish IDs while still being deterministic. diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 5370c7e75..8f98f633c 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -32,6 +32,8 @@ titles: descriptions: empty-set: There are no items to display yet. You may be able to add items or try back later. origin-initialization: To get started, please enter your origin URL + create-but-not-list: You do not have permission to list {resource}, but you can create new instances. + neither-list-nor-create: You do not have permission to list or create {resource}. questions: delete-confirm: Are you sure you want to delete this resource? remove-confirm: Are you sure you want to remove this association? diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs.js b/ui/admin/app/routes/scopes/scope/host-catalogs.js index 53fd92f7a..65da4edc1 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs.js @@ -11,7 +11,7 @@ export default class ScopesScopeHostCatalogsRoute extends Route { @service intl; @service notify; @service session; - + @service can; // =methods /** @@ -26,8 +26,13 @@ export default class ScopesScopeHostCatalogsRoute extends Route { * @return {Promise{[HostCatalogModel]}} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('host-catalog', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if ( + this.can.can('list collection', scope, { collection: 'host-catalogs' }) + ) { + return this.store.query('host-catalog', { scope_id }); + } } // =actions diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/index.js b/ui/admin/app/routes/scopes/scope/host-catalogs/index.js index 90fba54e3..9261d003a 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/index.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/index.js @@ -1,3 +1,9 @@ import Route from '@ember/routing/route'; -export default class ScopesScopeHostCatalogsIndexRoute extends Route {} +export default class ScopesScopeHostCatalogsIndexRoute extends Route { + 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 65e456c51..da31bfa24 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -79,10 +79,12 @@ {{t 'resources.target.title_plural'}} - - - {{t 'resources.host-catalog.title_plural'}} - + {{#if (can 'navigate collection' @model collection='host-catalogs')}} + + + {{t 'resources.host-catalog.title_plural'}} + + {{/if}} {{#if (feature-flag 'credential-store')}} diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs index 734825303..db06323c7 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs @@ -15,13 +15,13 @@ - {{#if @model}} - - {{t 'titles.new'}} - + {{#if (can 'create collection' this.scope collection='host-catalogs')}} + {{#if @model}} + {{t 'titles.new'}} + {{/if}} {{/if}} @@ -82,12 +82,35 @@ as |message| > - {{t 'resources.host-catalog.description'}} + {{#if + (can 'list collection' this.scope collection='host-catalogs') + }} + {{! can list (at least): show default welcome message}} + {{t 'resources.host-catalog.description'}} + {{else if (can + 'create collection' this.scope collection='host-catalogs' + )}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.host-catalog.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.host-catalog.title_plural') + }} + {{/if}} - - - {{t 'titles.new'}} - + {{#if + (can 'create collection' this.scope collection='host-catalogs') + }} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/tests/acceptance/host-catalogs-test.js b/ui/admin/tests/acceptance/host-catalogs/create-test.js similarity index 74% rename from ui/admin/tests/acceptance/host-catalogs-test.js rename to ui/admin/tests/acceptance/host-catalogs/create-test.js index fea885978..8661e1eff 100644 --- a/ui/admin/tests/acceptance/host-catalogs-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/create-test.js @@ -10,7 +10,7 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | host-catalogs', function (hooks) { +module('Acceptance | host-catalogs | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -22,6 +22,7 @@ module('Acceptance | host-catalogs', function (hooks) { org: null, project: null, }, + orgScope: null, }; const urls = { globalScope: null, @@ -35,6 +36,14 @@ module('Acceptance | host-catalogs', function (hooks) { hooks.beforeEach(function () { // Generate resources instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); instances.scopes.org = this.server.create('scope', { type: 'org', scope: { id: 'global', type: 'global' }, @@ -46,17 +55,19 @@ module('Acceptance | host-catalogs', function (hooks) { instances.hostCatalog = this.server.create('host-catalog', { scope: instances.scopes.project, }); + // Generate route URLs for resources urls.globalScope = `/scopes/global/scopes`; - urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`; + urls.orgScope = `/scopes/${instances.orgScope.id}/scopes`; urls.projectScope = `/scopes/${instances.scopes.project.id}`; urls.hostCatalogs = `${urls.projectScope}/host-catalogs`; + urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`; - urls.unknownHostCatalog = `${urls.hostCatalogs}/foo`; urls.newHostCatalog = `${urls.hostCatalogs}/new`; // Generate resource couner gethostCatalogCount = () => this.server.schema.hostCatalogs.all().models.length; + authenticateSession({}); }); @@ -79,6 +90,30 @@ module('Acceptance | host-catalogs', function (hooks) { assert.equal(gethostCatalogCount(), count); }); + test('can navigate to new host catalogs route with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.projectScope); + assert.ok( + instances.scopes.project.authorized_collection_actions[ + 'host-catalogs' + ].includes('create') + ); + assert.ok(find(`[href="${urls.hostCatalogs}"]`)); + }); + + test('cannot navigate to new host catalogs route without proper authorization', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions['host-catalogs'] = + []; + await visit(urls.projectScope); + assert.notOk( + instances.scopes.project.authorized_collection_actions[ + 'host-catalogs' + ].includes('create') + ); + assert.notOk(find(`[href="${urls.hostCatalogs}"]`)); + }); + test('saving a new host catalog with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.post('/host-catalogs', () => { diff --git a/ui/admin/tests/acceptance/host-catalogs/list-test.js b/ui/admin/tests/acceptance/host-catalogs/list-test.js new file mode 100644 index 000000000..127784f4a --- /dev/null +++ b/ui/admin/tests/acceptance/host-catalogs/list-test.js @@ -0,0 +1,98 @@ +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 | host-catalogs | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + project: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + }, + projectScope: null, + hostCatalogs: 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.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.scopes.project = this.server.create('scope', { + type: 'project', + scope: { id: instances.scopes.org.id, type: 'org' }, + }); + instances.hostCatalog = this.server.create('host-catalog', { + scope: instances.scopes.project, + }); + + // orgURL = `/scopes/${instances.orgScope.id}`; + urls.hostCatalogs = `/scopes/${instances.orgScope.id}/host-catalogs`; + urls.globalScope = `/scopes/global/scopes`; + urls.orgScope = `/scopes/${instances.orgScope.id}/scopes`; + urls.projectScope = `/scopes/${instances.scopes.project.id}`; + urls.hostCatalogs = `${urls.projectScope}/host-catalogs`; + + urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`; + authenticateSession({}); + }); + + test('can navigate to host catalogs with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.projectScope); + assert.ok( + instances.scopes.project.authorized_collection_actions[ + 'host-catalogs' + ].includes('list') + ); + assert.ok(find(`[href="${urls.hostCatalogs}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions['host-catalogs'] = + []; + await visit(urls.projectScope); + assert.notOk( + instances.scopes.project.authorized_collection_actions[ + 'host-catalogs' + ].includes('list') + ); + assert.notOk(find(`[href="${urls.hostCatalogs}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.scopes.project.authorized_collection_actions['host-catalogs'] = [ + 'create', + ]; + await visit(urls.projectScope); + assert.ok(find(`[href="${urls.hostCatalogs}"]`)); + }); +}); From 04af1a431fbffbae824e5486ec7bdf3f830e2734 Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 19:41:11 -0700 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20sessions=20create?= =?UTF-8?q?=20list=20capabilities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/factories/scope.js | 2 +- ui/admin/app/templates/scopes/scope.hbs | 10 +++-- ui/admin/jsconfig.json | 1 + ui/admin/tests/acceptance/sessions-test.js | 43 +++++++++++++++++++++- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 ui/admin/jsconfig.json diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index e9c5ac09f..afe31e5cd 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -10,7 +10,7 @@ export default factory.extend({ // groups: ['create', 'list'], // roles: ['create', 'list'], // 'auth-methods': ['create', 'list'], - // sessopms: ['list'], + sessions: ['list'], // targets: ['create', 'list'], // 'host-catalogs': ['create', 'list'], // 'credential-stores': ['create', 'list'], diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index fd20ca2a2..c0cb0dca7 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -71,10 +71,12 @@ --}} - - - {{t 'resources.session.title_plural'}} - + {{#if (can 'navigate collection' @model collection='sessions')}} + + + {{t 'resources.session.title_plural'}} + + {{/if}} {{t 'resources.target.title_plural'}} diff --git a/ui/admin/jsconfig.json b/ui/admin/jsconfig.json new file mode 100644 index 000000000..f408cac8c --- /dev/null +++ b/ui/admin/jsconfig.json @@ -0,0 +1 @@ +{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} \ No newline at end of file diff --git a/ui/admin/tests/acceptance/sessions-test.js b/ui/admin/tests/acceptance/sessions-test.js index d5f68ffeb..a5dfa25ff 100644 --- a/ui/admin/tests/acceptance/sessions-test.js +++ b/ui/admin/tests/acceptance/sessions-test.js @@ -15,13 +15,13 @@ import { module('Acceptance | sessions', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - const instances = { scopes: { global: null, org: null, }, sessions: null, + orgScope: null, }; const urls = { orgScope: null, @@ -31,6 +31,14 @@ module('Acceptance | sessions', function (hooks) { hooks.beforeEach(function () { authenticateSession({}); instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: 'global', type: 'global' }, + }, + 'withChildren' + ); instances.scopes.org = this.server.create('scope', { type: 'org', scope: { id: 'global', type: 'global' }, @@ -66,6 +74,7 @@ module('Acceptance | sessions', function (hooks) { }, 'withAssociations' ); + urls.orgScope = `/scopes/${instances.scopes.project.id}`; urls.sessions = `/scopes/${instances.scopes.project.id}/sessions`; }); @@ -81,6 +90,38 @@ module('Acceptance | sessions', function (hooks) { await visit(urls.sessions); }); + test('Users cannot navigate to sessions without proper authorization', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions.sessions = []; + authenticateSession({}); + later(async () => { + run.cancelTimers(); + assert.notOk( + instances.scopes.project.authorized_collection_actions.sessions.includes( + 'list' + ) + ); + assert.notOk(find(`[href="${urls.sessions}"]`)); + }, 750); + await visit(urls.orgScope); + }); + + test('Users can navigate to sessions with proper authorization', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions.sessions = ['list']; + authenticateSession({}); + later(async () => { + run.cancelTimers(); + assert.ok( + instances.scopes.project.authorized_collection_actions.sessions.includes( + 'list' + ) + ); + assert.ok(find(`[href="${urls.sessions}"]`)); + }, 750); + await visit(urls.orgScope); + }); + test('visiting sessions without users or targets is OK', async function (assert) { assert.expect(2); instances.sessions[0].update({ From 32c69624f90bc3b1aca0e0c9b206dfd414c38e17 Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 19:49:56 -0700 Subject: [PATCH 07/16] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20jsconfig?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/jsconfig.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 ui/admin/jsconfig.json diff --git a/ui/admin/jsconfig.json b/ui/admin/jsconfig.json deleted file mode 100644 index f408cac8c..000000000 --- a/ui/admin/jsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} \ No newline at end of file From 2a8d42eabad44db188aad3b3971e6a14d5fece45 Mon Sep 17 00:00:00 2001 From: dharini Date: Tue, 31 Aug 2021 23:34:32 -0700 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20target=20create=20?= =?UTF-8?q?list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/factories/scope.js | 2 +- ui/admin/app/routes/scopes/scope/targets.js | 8 +- .../app/routes/scopes/scope/targets/index.js | 9 +- ui/admin/app/templates/scopes/scope.hbs | 10 ++- .../templates/scopes/scope/targets/index.hbs | 43 +++++++--- .../create-test.js} | 26 +++++- .../tests/acceptance/targets/list-test.js | 84 +++++++++++++++++++ 7 files changed, 162 insertions(+), 20 deletions(-) rename ui/admin/tests/acceptance/{targets-test.js => targets/create-test.js} (80%) create mode 100644 ui/admin/tests/acceptance/targets/list-test.js diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index e9c5ac09f..3e375fb59 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -11,7 +11,7 @@ export default factory.extend({ // roles: ['create', 'list'], // 'auth-methods': ['create', 'list'], // sessopms: ['list'], - // targets: ['create', 'list'], + targets: ['create', 'list'], // 'host-catalogs': ['create', 'list'], // 'credential-stores': ['create', 'list'], }; diff --git a/ui/admin/app/routes/scopes/scope/targets.js b/ui/admin/app/routes/scopes/scope/targets.js index 40cd811bb..2adb5ca84 100644 --- a/ui/admin/app/routes/scopes/scope/targets.js +++ b/ui/admin/app/routes/scopes/scope/targets.js @@ -11,6 +11,7 @@ export default class ScopesScopeTargetsRoute extends Route { @service intl; @service notify; @service session; + @service can; // =methods @@ -26,8 +27,11 @@ export default class ScopesScopeTargetsRoute extends Route { * @return {Promise{[TargetModel]}} */ async model() { - const { id: scope_id } = this.modelFor('scopes.scope'); - return this.store.query('target', { scope_id }); + const scope = this.modelFor('scopes.scope'); + const { id: scope_id } = scope; + if (this.can.can('list collection', scope, { collection: 'targets' })) { + return this.store.query('target', { scope_id }); + } } // =actions diff --git a/ui/admin/app/routes/scopes/scope/targets/index.js b/ui/admin/app/routes/scopes/scope/targets/index.js index 2b4412f53..8e92f73d5 100644 --- a/ui/admin/app/routes/scopes/scope/targets/index.js +++ b/ui/admin/app/routes/scopes/scope/targets/index.js @@ -1,3 +1,10 @@ import Route from '@ember/routing/route'; +export default class ScopesScopeTargetsIndexRoute extends Route { + // =methods -export default class ScopesScopeTargetsIndexRoute extends Route {} + 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 fd20ca2a2..cf4d2e977 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -75,10 +75,12 @@ {{t 'resources.session.title_plural'}} - - - {{t 'resources.target.title_plural'}} - + {{#if (can 'navigate collection' @model collection='targets')}} + + + {{t 'resources.target.title_plural'}} + + {{/if}} {{t 'resources.host-catalog.title_plural'}} diff --git a/ui/admin/app/templates/scopes/scope/targets/index.hbs b/ui/admin/app/templates/scopes/scope/targets/index.hbs index f8dcec9b4..bfb9821d0 100644 --- a/ui/admin/app/templates/scopes/scope/targets/index.hbs +++ b/ui/admin/app/templates/scopes/scope/targets/index.hbs @@ -15,13 +15,14 @@ - {{#if @model}} - - {{t 'titles.new'}} - + {{#if (can 'create collection' this.scope collection='targets')}} + {{#if @model}} + {{t + 'titles.new' + }} + {{/if}} {{/if}} - {{#if @model}} @@ -79,12 +80,34 @@ as |message| > - {{t 'resources.target.description'}} + + {{#if (can 'list collection' this.scope collection='targets')}} + {{! can list (at least): show default welcome message}} + {{t 'resources.target.description'}} + {{else if (can + 'create collection' this.scope collection='targets' + )}} + {{! can create (only): show create-but-not-list welcome message}} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.target.title_plural') + }} + {{else}} + {{! can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.target.title_plural') + }} + + {{/if}} - - - {{t 'titles.new'}} - + + {{#if (can 'create collection' this.scope collection='targets')}} + + + {{t 'titles.new'}} + + {{/if}} diff --git a/ui/admin/tests/acceptance/targets-test.js b/ui/admin/tests/acceptance/targets/create-test.js similarity index 80% rename from ui/admin/tests/acceptance/targets-test.js rename to ui/admin/tests/acceptance/targets/create-test.js index cf202c049..16215cb7a 100644 --- a/ui/admin/tests/acceptance/targets-test.js +++ b/ui/admin/tests/acceptance/targets/create-test.js @@ -10,7 +10,7 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | targets', function (hooks) { +module('Acceptance | targets | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -33,7 +33,6 @@ module('Acceptance | targets', function (hooks) { }; hooks.beforeEach(function () { - // Generate resources instances.scopes.global = this.server.create('scope', { id: 'global' }); instances.scopes.org = this.server.create('scope', { type: 'org', @@ -68,6 +67,29 @@ module('Acceptance | targets', function (hooks) { assert.equal(getTargetCount(), count + 1); }); + test('can navigate to new targets route with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.projectScope); + assert.ok( + instances.scopes.project.authorized_collection_actions.targets.includes( + 'create' + ) + ); + assert.ok(find(`[href="${urls.targets}"]`)); + }); + + test('cannot navigate to new targets route without proper authorization', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions.targets = []; + await visit(urls.projectScope); + assert.notOk( + instances.scopes.project.authorized_collection_actions.targets.includes( + 'create' + ) + ); + assert.notOk(find(`[href="${urls.targets}"]`)); + }); + test('can cancel create new targets', async function (assert) { assert.expect(2); const count = getTargetCount(); diff --git a/ui/admin/tests/acceptance/targets/list-test.js b/ui/admin/tests/acceptance/targets/list-test.js new file mode 100644 index 000000000..19bd4ee64 --- /dev/null +++ b/ui/admin/tests/acceptance/targets/list-test.js @@ -0,0 +1,84 @@ +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 | targets | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + orgScope: null, + }; + + const urls = { + scopes: { + org: null, + project: null, + }, + targets: null, + projectScope: 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.scopes.project = this.server.create('scope', { + type: 'project', + scope: { id: instances.orgScope.id, type: 'org' }, + }); + instances.target = this.server.create('target', { + scope: instances.scopes.project, + }); + urls.projectScope = `/scopes/${instances.scopes.project.id}`; + urls.targets = `${urls.projectScope}/targets`; + authenticateSession({}); + }); + + test('can navigate to targets with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.projectScope); + assert.ok( + instances.scopes.project.authorized_collection_actions.targets.includes( + 'list' + ) + ); + assert.ok(find(`[href="${urls.targets}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.scopes.project.authorized_collection_actions.targets = []; + await visit(urls.projectScope); + assert.notOk( + instances.scopes.project.authorized_collection_actions.targets.includes( + 'list' + ) + ); + assert.notOk(find(`[href="${urls.targets}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(1); + instances.scopes.project.authorized_collection_actions.targets = ['create']; + await visit(urls.projectScope); + assert.ok(find(`[href="${urls.targets}"]`)); + }); +}); From b27fe14dbca2ec2acba743e9b8df52796070eda8 Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Wed, 1 Sep 2021 08:47:34 -0700 Subject: [PATCH 09/16] Update create-test.js --- ui/admin/tests/acceptance/users/create-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/admin/tests/acceptance/users/create-test.js b/ui/admin/tests/acceptance/users/create-test.js index fbff7fbee..99b715b9b 100644 --- a/ui/admin/tests/acceptance/users/create-test.js +++ b/ui/admin/tests/acceptance/users/create-test.js @@ -45,14 +45,14 @@ module('Acceptance | users | create', function (hooks) { assert.equal(this.server.db.users.length, usersCount + 1); }); - test('can navigate to new users route with proper authorization', async function (assert) { + test('Users 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) { + test('Users cannot navigate to new users route without proper authorization', async function (assert) { assert.expect(2); orgScope.authorized_collection_actions.users = []; await visit(orgURL); From e7b553efda26b8b0992bb25110a0b0e65f2345d8 Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Wed, 1 Sep 2021 08:48:08 -0700 Subject: [PATCH 10/16] Update list-test.js --- ui/admin/tests/acceptance/users/list-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/tests/acceptance/users/list-test.js b/ui/admin/tests/acceptance/users/list-test.js index 72a6e35eb..d453e5b04 100644 --- a/ui/admin/tests/acceptance/users/list-test.js +++ b/ui/admin/tests/acceptance/users/list-test.js @@ -47,7 +47,7 @@ module('Acceptance | users | list', function (hooks) { authenticateSession({}); }); - test('can navigate to users with proper authorization', async function (assert) { + test('Users can navigate to users with proper authorization', async function (assert) { assert.expect(2); await visit(orgURL); assert.ok( From 166b35860ace66e79233ab62b8dbebfab4b0686e Mon Sep 17 00:00:00 2001 From: dharini Date: Wed, 1 Sep 2021 15:29:09 -0700 Subject: [PATCH 11/16] =?UTF-8?q?chore:=20=F0=9F=A4=96=20merge=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/templates/scopes/scope/host-catalogs/index.hbs | 4 +++- ui/admin/tests/acceptance/host-catalogs/create-test.js | 8 ++++---- ui/admin/tests/acceptance/host-catalogs/list-test.js | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs index db06323c7..d5c7ab53a 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs @@ -20,7 +20,9 @@ {{t 'titles.new'}} + > + {{t 'titles.new'}} + {{/if}} {{/if}} diff --git a/ui/admin/tests/acceptance/host-catalogs/create-test.js b/ui/admin/tests/acceptance/host-catalogs/create-test.js index 8661e1eff..e95ecbde0 100644 --- a/ui/admin/tests/acceptance/host-catalogs/create-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/create-test.js @@ -71,7 +71,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { authenticateSession({}); }); - test('can create new host catalogs', async function (assert) { + test('Users can create new host catalogs', async function (assert) { assert.expect(1); const count = gethostCatalogCount(); await visit(urls.newHostCatalog); @@ -80,7 +80,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { assert.equal(gethostCatalogCount(), count + 1); }); - test('can cancel create new host catalogs', async function (assert) { + test('Users can cancel create new host catalogs', async function (assert) { assert.expect(2); const count = gethostCatalogCount(); await visit(urls.newHostCatalog); @@ -90,7 +90,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { assert.equal(gethostCatalogCount(), count); }); - test('can navigate to new host catalogs route with proper authorization', async function (assert) { + test('Users can navigate to new host catalogs route with proper authorization', async function (assert) { assert.expect(2); await visit(urls.projectScope); assert.ok( @@ -101,7 +101,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { assert.ok(find(`[href="${urls.hostCatalogs}"]`)); }); - test('cannot navigate to new host catalogs route without proper authorization', async function (assert) { + test('Users cannot navigate to new host catalogs route without proper authorization', async function (assert) { assert.expect(2); instances.scopes.project.authorized_collection_actions['host-catalogs'] = []; diff --git a/ui/admin/tests/acceptance/host-catalogs/list-test.js b/ui/admin/tests/acceptance/host-catalogs/list-test.js index 127784f4a..6cf508e20 100644 --- a/ui/admin/tests/acceptance/host-catalogs/list-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/list-test.js @@ -52,7 +52,6 @@ module('Acceptance | host-catalogs | list', function (hooks) { scope: instances.scopes.project, }); - // orgURL = `/scopes/${instances.orgScope.id}`; urls.hostCatalogs = `/scopes/${instances.orgScope.id}/host-catalogs`; urls.globalScope = `/scopes/global/scopes`; urls.orgScope = `/scopes/${instances.orgScope.id}/scopes`; @@ -63,7 +62,7 @@ module('Acceptance | host-catalogs | list', function (hooks) { authenticateSession({}); }); - test('can navigate to host catalogs with proper authorization', async function (assert) { + test('Users can navigate to host catalogs with proper authorization', async function (assert) { assert.expect(2); await visit(urls.projectScope); assert.ok( From 7c0366c790cb27d35fccca27212f71150587687d Mon Sep 17 00:00:00 2001 From: dharini Date: Thu, 2 Sep 2021 08:12:10 -0700 Subject: [PATCH 12/16] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20to=20orgur?= =?UTF-8?q?l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/tests/acceptance/auth-methods/create-test.js | 1 - ui/admin/tests/acceptance/auth-methods/list-test.js | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ui/admin/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index 55a76243b..a9c9dea2d 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -72,7 +72,6 @@ module('Acceptance | auth-methods | create ', function (hooks) { 'create' ) ); - console.log(urls.orgURL); assert.ok(find(`[href="${urls.authMethods}"]`)); }); diff --git a/ui/admin/tests/acceptance/auth-methods/list-test.js b/ui/admin/tests/acceptance/auth-methods/list-test.js index 1dbe3f23f..3ca7925f7 100644 --- a/ui/admin/tests/acceptance/auth-methods/list-test.js +++ b/ui/admin/tests/acceptance/auth-methods/list-test.js @@ -12,7 +12,6 @@ import { module('Acceptance | auth-methods | list', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - let orgURL; const instances = { scopes: { @@ -27,6 +26,7 @@ module('Acceptance | auth-methods | list', function (hooks) { org: null, }, authMethods: null, + orgScope: null, }; hooks.beforeEach(function () { @@ -48,14 +48,14 @@ module('Acceptance | auth-methods | list', function (hooks) { 'withChildren' ); - orgURL = `/scopes/${instances.orgScope.id}`; + urls.orgScope = `/scopes/${instances.orgScope.id}`; urls.authMethods = `/scopes/${instances.orgScope.id}/auth-methods`; authenticateSession({}); }); test('can navigate to auth methods with proper authorization', async function (assert) { assert.expect(2); - await visit(orgURL); + await visit(urls.orgScope); assert.ok( instances.orgScope.authorized_collection_actions['auth-methods'].includes( 'list' @@ -67,7 +67,7 @@ module('Acceptance | auth-methods | list', function (hooks) { test('User cannot navigate to index without either list or create actions', async function (assert) { assert.expect(2); instances.orgScope.authorized_collection_actions['auth-methods'] = []; - await visit(orgURL); + await visit(urls.orgScope); assert.notOk( instances.orgScope.authorized_collection_actions['auth-methods'].includes( 'list' @@ -81,7 +81,7 @@ module('Acceptance | auth-methods | list', function (hooks) { instances.orgScope.authorized_collection_actions['auth-methods'] = [ 'create', ]; - await visit(orgURL); + await visit(urls.orgScope); assert.ok(find(`[href="${urls.authMethods}"]`)); }); }); From 42283596b26f39707282efb1d21c1a20a92b0c12 Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Thu, 2 Sep 2021 08:13:47 -0700 Subject: [PATCH 13/16] Update list-test.js --- ui/admin/tests/acceptance/auth-methods/list-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/admin/tests/acceptance/auth-methods/list-test.js b/ui/admin/tests/acceptance/auth-methods/list-test.js index 3ca7925f7..c943c01e8 100644 --- a/ui/admin/tests/acceptance/auth-methods/list-test.js +++ b/ui/admin/tests/acceptance/auth-methods/list-test.js @@ -53,7 +53,7 @@ module('Acceptance | auth-methods | list', function (hooks) { authenticateSession({}); }); - test('can navigate to auth methods with proper authorization', async function (assert) { + test('Users can navigate to auth methods with proper authorization', async function (assert) { assert.expect(2); await visit(urls.orgScope); assert.ok( @@ -64,7 +64,7 @@ module('Acceptance | auth-methods | list', function (hooks) { assert.ok(find(`[href="${urls.authMethods}"]`)); }); - test('User cannot navigate to index without either list or create actions', async function (assert) { + test('Users cannot navigate to index without either list or create actions', async function (assert) { assert.expect(2); instances.orgScope.authorized_collection_actions['auth-methods'] = []; await visit(urls.orgScope); From 66b40e531848883804872745e2b8c4832db57a9d Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Thu, 2 Sep 2021 08:15:04 -0700 Subject: [PATCH 14/16] Update create-test.js --- ui/admin/tests/acceptance/auth-methods/create-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/admin/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index a9c9dea2d..8554157e1 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -50,7 +50,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; }); - test('can create new auth method', async function (assert) { + test('Users can create new auth method', async function (assert) { assert.expect(1); const authMethodsCount = this.server.db.authMethods.length; await visit(urls.newAuthMethod); @@ -60,7 +60,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { assert.equal(this.server.db.authMethods.length, authMethodsCount + 1); }); - test('can navigate to new auth-methods route with proper authorization', async function (assert) { + test('Users can navigate to new auth-methods route with proper authorization', async function (assert) { assert.expect(2); instances.orgScope.authorized_collection_actions['auth-methods'] = [ 'create', @@ -75,7 +75,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { assert.ok(find(`[href="${urls.authMethods}"]`)); }); - test('cannot navigate to new auth-methods route without proper authorization', async function (assert) { + test('Users cannot navigate to new auth-methods route without proper authorization', async function (assert) { assert.expect(2); instances.orgScope.authorized_collection_actions['auth-methods'] = []; await visit(urls.orgURL); From 5e6a3e9d9a4ce98d8cbd00980d837823b46bb6ad Mon Sep 17 00:00:00 2001 From: dharini Date: Thu, 2 Sep 2021 09:48:28 -0700 Subject: [PATCH 15/16] =?UTF-8?q?chore:=20=F0=9F=A4=96=20merge=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/factories/scope.js | 2 +- ui/admin/app/routes/scopes/scope/roles.js | 8 +- .../app/routes/scopes/scope/roles/index.js | 9 ++- ui/admin/app/templates/scopes/scope.hbs | 13 +++- .../templates/scopes/scope/roles/index.hbs | 37 ++++++--- .../acceptance/auth-methods/create-test.js | 25 +++++-- .../acceptance/auth-methods/list-test.js | 8 -- .../{roles-test.js => roles/create-test.js} | 45 ++++++++--- ui/admin/tests/acceptance/roles/list-test.js | 75 +++++++++++++++++++ 9 files changed, 179 insertions(+), 43 deletions(-) rename ui/admin/tests/acceptance/{roles-test.js => roles/create-test.js} (70%) create mode 100644 ui/admin/tests/acceptance/roles/list-test.js diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index c98b1fa23..0e7056957 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -8,7 +8,7 @@ export default factory.extend({ return { users: ['create', 'list'], groups: ['create', 'list'], - // roles: ['create', 'list'], + roles: ['create', 'list'], 'auth-methods': ['create', 'list'], // sessopms: ['list'], // targets: ['create', 'list'], 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/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index cabac1f4a..d8704d9c8 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -106,10 +106,15 @@ {{t 'resources.group.title_plural'}} {{/if}} - - - {{t 'resources.role.title_plural'}} - + {{#if (can 'navigate collection' @model collection='roles')}} + + + {{t 'resources.role.title_plural'}} + + {{/if}} {{#if (can 'navigate collection' @model collection='auth-methods')}} 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/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index 8554157e1..684984999 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -19,6 +19,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { scopes: { global: null, org: null, + project: null, }, authMethod: null, orgScope: null, @@ -29,14 +30,22 @@ module('Acceptance | auth-methods | create ', function (hooks) { authMethods: null, newAuthMethod: null, authMethod: null, - orgURL: null, }; hooks.beforeEach(function () { authenticateSession({}); - instances.orgScope = 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.scopes.project = this.server.create('scope', { + type: 'project', + scope: { id: instances.orgScope.id, type: instances.orgScope.type }, }); instances.authMethod = this.server.create('auth-method', { @@ -44,8 +53,8 @@ module('Acceptance | auth-methods | create ', function (hooks) { }); // Generate route URLs for resources - urls.orgURL = `/scopes/${instances.orgScope.id}`; - urls.authMethods = `${urls.orgURL}/auth-methods`; + urls.orgScope = `/scopes/${instances.orgScope.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; urls.newAuthMethod = `${urls.authMethods}/new?type=password`; urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; }); @@ -66,7 +75,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { 'create', 'list', ]; - await visit(urls.orgURL); + await visit(urls.orgScope); assert.ok( instances.orgScope.authorized_collection_actions['auth-methods'].includes( 'create' @@ -78,7 +87,7 @@ module('Acceptance | auth-methods | create ', function (hooks) { test('Users cannot navigate to new auth-methods route without proper authorization', async function (assert) { assert.expect(2); instances.orgScope.authorized_collection_actions['auth-methods'] = []; - await visit(urls.orgURL); + await visit(urls.orgScope); assert.notOk( instances.orgScope.authorized_collection_actions['auth-methods'].includes( 'create' diff --git a/ui/admin/tests/acceptance/auth-methods/list-test.js b/ui/admin/tests/acceptance/auth-methods/list-test.js index c943c01e8..385c8cb1a 100644 --- a/ui/admin/tests/acceptance/auth-methods/list-test.js +++ b/ui/admin/tests/acceptance/auth-methods/list-test.js @@ -39,14 +39,6 @@ module('Acceptance | auth-methods | list', function (hooks) { }, 'withChildren' ); - instances.org = this.server.create( - 'scope', - { - type: 'org', - scope: { id: 'global', type: 'global' }, - }, - 'withChildren' - ); urls.orgScope = `/scopes/${instances.orgScope.id}`; urls.authMethods = `/scopes/${instances.orgScope.id}/auth-methods`; 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}"]`)); + }); +}); From 0ed506a111e5abfc5cf1d95d23eb9af701b88874 Mon Sep 17 00:00:00 2001 From: dharini Date: Thu, 2 Sep 2021 10:22:47 -0700 Subject: [PATCH 16/16] =?UTF-8?q?chore:=20=F0=9F=A4=96=20merge=20main=20+?= =?UTF-8?q?=20resolve=20conflicts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/factories/scope.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/api/mirage/factories/scope.js b/addons/api/mirage/factories/scope.js index 523693a4e..f4c7073f7 100644 --- a/addons/api/mirage/factories/scope.js +++ b/addons/api/mirage/factories/scope.js @@ -3,11 +3,6 @@ import { trait } from 'ember-cli-mirage'; export default factory.extend({ type: 'global', - authorized_collection_actions: () => { - return { - 'host-catalogs': ['create', 'list'], - }; - }, authorized_collection_actions: () => { return { @@ -17,7 +12,7 @@ export default factory.extend({ // 'auth-methods': ['create', 'list'], // sessopms: ['list'], // targets: ['create', 'list'], - // 'host-catalogs': ['create', 'list'], + 'host-catalogs': ['create', 'list'], // 'credential-stores': ['create', 'list'], }; },