From 7ff3bae26d2052e13f99ca67b7cc7a75217048b2 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 12 Aug 2020 17:16:41 -0400 Subject: [PATCH 01/17] chore: Add auth-method index route --- .../app/components/form/auth-method/index.hbs | 47 ++++++++++++++++ ui/core/app/router.js | 4 +- .../app/routes/scopes/scope/auth-methods.js | 56 +++++++++++++++++++ .../scopes/scope/auth-methods/auth-method.js | 16 ++++++ .../scope/auth-methods/auth-method/index.js | 4 ++ .../scopes/scope/auth-methods/auth-method.hbs | 1 + .../scope/auth-methods/auth-method/index.hbs | 24 ++++++++ .../scopes/scope/auth-methods/index.hbs | 7 ++- .../components/form/auth-method/index-test.js | 26 +++++++++ .../scope/auth-methods/auth-method-test.js | 11 ++++ .../auth-methods/auth-method/index-test.js | 11 ++++ ui/core/translations/en-us.yaml | 1 + 12 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/components/form/auth-method/index.hbs create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/auth-method.js create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs create mode 100644 ui/core/tests/integration/components/form/auth-method/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs new file mode 100644 index 000000000..158f80db2 --- /dev/null +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -0,0 +1,47 @@ + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + \ No newline at end of file diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 1471216ea..e23fad074 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -35,7 +35,9 @@ Router.map(function () { }); this.route('new'); }); - this.route('auth-methods', function() {}); + this.route('auth-methods', function() { + this.route('auth-method', { path: ':auth_method_id' }, function() {}); + }); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index 112c40248..9c747ba6f 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -1,6 +1,13 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; export default class ScopesScopeAuthMethodsRoute extends Route { + // =services + + @service intl; + @service notify; + // =methods /** @@ -11,4 +18,53 @@ export default class ScopesScopeAuthMethodsRoute extends Route { const { id: scopeID} = this.modelFor('scopes.scope'); return this.store.findAll('auth-method', { adapterOptions: { scopeID } }); } + + // =actions + /** + * Rollback changes to an auth-method. + * @param {AuthMethodModel} authMethod + */ + @action + cancel(authMethod) { + const { isNew } = authMethod; + authMethod.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.auth-methods'); + } + + /** + * Save an auth-method in current scope. + * @param {AuthMethodModel} authMethod + */ + @action + async save(authMethod) { + const { isNew } = authMethod; + try { + await authMethod.save(); + this.refresh(); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); + this.transitionTo('scopes.scope.auth-methods.auth-method'); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete an auth-method in current scope and redirect to auth-methods + * @param {AuthMethodModel} authMethod + */ + @action + async delete(authMethod) { + try { + await authMethod.destroyRecord(); + this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); + this.transitionTo('scopes.scope.auth-methods'); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } } diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js new file mode 100644 index 000000000..5cc752eac --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js @@ -0,0 +1,16 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { + // =methods + + /** + * Load a single auth-method in current scope. + * @param {object} params + * @param {string} params.auth_method_id + * @return {authMethodModel} + */ + async model({ auth_method_id}) { + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('auth-method', auth_method_id, { adapterOptions: { scopeID }}); + } +} diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js new file mode 100644 index 000000000..5cef508e0 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsAuthMethodIndexRoute extends Route { +} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs new file mode 100644 index 000000000..c24cd6895 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs new file mode 100644 index 000000000..3fbc14eca --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -0,0 +1,24 @@ + + +

{{t "resources.auth-method"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + {{@model.type}} + + + +
\ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 1b805f939..3286dff79 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -18,7 +18,12 @@ {{#each @model as |authMethod|}} - {{authMethod.name}} + + {{authMethod.name}} + {{authMethod.description}} diff --git a/ui/core/tests/integration/components/form/auth-method/index-test.js b/ui/core/tests/integration/components/form/auth-method/index-test.js new file mode 100644 index 000000000..df00881db --- /dev/null +++ b/ui/core/tests/integration/components/form/auth-method/index-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/auth-method/index', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js new file mode 100644 index 000000000..36b2e2f2d --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/auth-method', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/auth-method'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js new file mode 100644 index 000000000..5a51858df --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/auth-method/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/auth-method/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 1002147e2..996073949 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -31,6 +31,7 @@ resources: project: Project host-catalog: Host Catalog host-catalogs: Host Catalogs + auth-method: Auth Method auth-methods: Auth Methods titles: iam: IAM From 3524b7c3ff51a1e1daeae08b8e43a90340e6d0c2 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 10:47:51 -0400 Subject: [PATCH 02/17] chore: Remove unused delete method --- .../app/components/form/auth-method/index.hbs | 6 +++++ .../app/routes/scopes/scope/auth-methods.js | 17 ------------ .../scope/auth-methods/auth-method/index.hbs | 3 --- .../components/form/auth-method-test.js | 21 +++++++++++++++ .../components/form/auth-method/index-test.js | 26 ------------------- 5 files changed, 27 insertions(+), 46 deletions(-) create mode 100644 ui/core/tests/integration/components/form/auth-method-test.js delete mode 100644 ui/core/tests/integration/components/form/auth-method/index-test.js diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs index 158f80db2..c2dccd6b6 100644 --- a/ui/core/app/components/form/auth-method/index.hbs +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -5,6 +5,12 @@ @error={{@model.errors.base}} as |form| > + + - - {{@model.type}} - {}; + this.cancel = () => {}; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/integration/components/form/auth-method/index-test.js b/ui/core/tests/integration/components/form/auth-method/index-test.js deleted file mode 100644 index df00881db..000000000 --- a/ui/core/tests/integration/components/form/auth-method/index-test.js +++ /dev/null @@ -1,26 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/auth-method/index', function(hooks) { - setupRenderingTest(hooks); - - test('it renders', async function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.set('myAction', function(val) { ... }); - - await render(hbs``); - - assert.equal(this.element.textContent.trim(), ''); - - // Template block usage: - await render(hbs` - - template block text - - `); - - assert.equal(this.element.textContent.trim(), 'template block text'); - }); -}); From 0d73c81ca533a4ddc1cb8a9f73f3f284ad192fa0 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 15:12:31 -0400 Subject: [PATCH 03/17] chore: Add auth-method type badge to page title --- .../scopes/scope/auth-methods/auth-method/index.hbs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index 4fa1f7ea3..0d9a5ca5e 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -1,6 +1,9 @@ -

{{t "resources.auth-method"}}

+

+ {{t "resources.auth-method"}} + {{@model.type}} +

From 89384a29de061baa3d6306c83d3110c8819e4888 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 15:41:33 -0400 Subject: [PATCH 04/17] test: Add authmethods acceptance test --- ui/core/tests/acceptance/auth-methods-test.js | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ui/core/tests/acceptance/auth-methods-test.js diff --git a/ui/core/tests/acceptance/auth-methods-test.js b/ui/core/tests/acceptance/auth-methods-test.js new file mode 100644 index 000000000..fe4dc5c0e --- /dev/null +++ b/ui/core/tests/acceptance/auth-methods-test.js @@ -0,0 +1,82 @@ +import { module, test } from 'qunit'; +import { visit, currentURL } 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 { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | auth methods', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null + }, + authMethod: null + }; + const urls = { + orgScope: null, + authMethods: null + }; + + hooks.beforeEach(function () { + // Setup Mirage mock resources for this test + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { type: 'org', + scope: { id: instances.scopes.global.id, type: instances.scopes.global.type } + }); + instances.authMethod = this.server.create('auth-method', { + scope: { + id: instances.scopes.org.id, + type: instances.scopes.org.type + } + }); + + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + }); + + test('visiting auth methods', async function (assert) { + assert.expect(1); + authenticateSession(); + await visit(urls.authMethods); + await a11yAudit(); + assert.equal(currentURL(), urls.authMethods); + }); + + test('can navigate to an auth method form', async function (assert) { + assert.expect(0); + }); + + test('can update an auth method and save changes', async function (assert) { + assert.expect(0); + }); + + test('can update an auth method and cancel changes', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when save on auth method fails', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when delete on an auth method fails', async function (assert) { + assert.expect(0); + }); + + test('saving a new auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('saving an existing auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); +}); From 8dd13a0936979f7032abfa9d06d0ec674dcc1f22 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 16:08:45 -0400 Subject: [PATCH 05/17] chore: Create auth-method route --- ui/core/app/router.js | 1 + ui/core/app/routes/scopes/scope/auth-methods.js | 2 +- .../app/routes/scopes/scope/auth-methods/new.js | 14 ++++++++++++++ .../templates/scopes/scope/auth-methods/index.hbs | 8 ++++++++ .../templates/scopes/scope/auth-methods/new.hbs | 15 +++++++++++++++ ui/core/tests/acceptance/auth-methods-test.js | 12 ++++++++++++ .../routes/scopes/scope/auth-methods/new-test.js | 11 +++++++++++ 7 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/new.js create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/new.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index da0c7dcce..af71f6132 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -38,6 +38,7 @@ Router.map(function () { }); this.route('auth-methods', function() { this.route('auth-method', { path: ':auth_method_id' }, function() {}); + this.route('new'); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index 91c6ac1f4..ffad801fa 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -44,7 +44,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route { this.notify.success( this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') ); - this.transitionTo('scopes.scope.auth-methods.auth-method'); + this.transitionTo('scopes.scope.auth-methods.auth-method', authMethod); } catch (error) { //TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods/new.js b/ui/core/app/routes/scopes/scope/auth-methods/new.js new file mode 100644 index 000000000..ceeb6a5d7 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/new.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsNewRoute extends Route { + // =methods + + /** + * Create a new unsaved auth-method. + * @return {AuthMethodModel} + */ + model() { + const scopeModel = this.modelFor('scopes.scope'); + return this.store.createRecord('auth-method', { type: 'password', scopeModel }); + } +} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 9d741b6cd..61c7a08e4 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -3,6 +3,14 @@

{{t "resources.auth-methods"}}

+ + {{#if @model}} + + {{t "titles.new"}} + + {{/if}} + + {{#if @model}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs new file mode 100644 index 000000000..315156454 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs @@ -0,0 +1,15 @@ +{{title (t "titles.new")}} + + + +

{{t "titles.new"}}

+
+ + + + +
\ No newline at end of file diff --git a/ui/core/tests/acceptance/auth-methods-test.js b/ui/core/tests/acceptance/auth-methods-test.js index fe4dc5c0e..5b816f253 100644 --- a/ui/core/tests/acceptance/auth-methods-test.js +++ b/ui/core/tests/acceptance/auth-methods-test.js @@ -64,6 +64,18 @@ module('Acceptance | auth methods', function (hooks) { assert.expect(0); }); + test('can create new auth method', async function (assert) { + assert.expect(0); + }); + + test('can cancel new auth method creation', async function (assert) { + assert.expect(0); + }); + + test('saving a new auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + test('errors are displayed when save on auth method fails', async function (assert) { assert.expect(0); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js new file mode 100644 index 000000000..6eea2751c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/new'); + assert.ok(route); + }); +}); From e790d5871738ad9239bfc17bf7efead96d9c529b Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 17:20:10 -0400 Subject: [PATCH 06/17] chore: Use translated string for auth-method type --- .../app/components/form/auth-method/index.hbs | 2 +- .../scope/auth-methods/auth-method/index.hbs | 8 ++++---- .../scopes/scope/auth-methods/index.hbs | 16 +++++++++------- .../components/form/auth-method-test.js | 2 ++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs index c2dccd6b6..7c1bc5162 100644 --- a/ui/core/app/components/form/auth-method/index.hbs +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -6,7 +6,7 @@ as |form| > diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index 0d9a5ca5e..580249d53 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -1,9 +1,9 @@ -

- {{t "resources.auth-method"}} - {{@model.type}} -

+

{{t "resources.auth-method"}}

+ + {{t (concat 'auth.method-types.' @model.type)}} +
diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 61c7a08e4..3cf07c9af 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -27,17 +27,19 @@ {{#each @model as |authMethod|}} - - {{authMethod.name}} - +

+ + {{authMethod.name}} + +

{{authMethod.description}}
- {{authMethod.type}} + {{t (concat 'auth.method-types.' authMethod.type)}} {{authMethod.id}} diff --git a/ui/core/tests/integration/components/form/auth-method-test.js b/ui/core/tests/integration/components/form/auth-method-test.js index e7320e927..267e516b5 100644 --- a/ui/core/tests/integration/components/form/auth-method-test.js +++ b/ui/core/tests/integration/components/form/auth-method-test.js @@ -9,9 +9,11 @@ module('Integration | Component | form/auth-method', function (hooks) { test('it renders', async function (assert) { this.submit = () => {}; this.cancel = () => {}; + this.model = { type: 'password' }; await render(hbs` `); From 99169e7ff982e1bf80b8f49e944b9b8f3b84a555 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 17:54:02 -0400 Subject: [PATCH 07/17] chore: Add 'New' button to auth-methods welcome message --- ui/core/app/templates/scopes/scope/auth-methods/index.hbs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 3cf07c9af..5b4fffc2a 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -55,6 +55,10 @@ {{t 'descriptions.auth-methods-welcome'}} + + + {{t 'titles.new'}} + From 252e5764f1b417ac67fecc09a41c29ef77c78893 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 18:04:27 -0400 Subject: [PATCH 08/17] chore: Add breadcrumbs to auth-methods views --- .../controllers/scopes/scope/auth-methods.js | 18 ++++++++++++++++++ .../scopes/scope/auth-methods/auth-method.js | 13 +++++++++++++ .../scopes/scope/auth-methods/new.js | 18 ++++++++++++++++++ .../scope/auth-methods/auth-method/index.hbs | 4 ++++ .../scopes/scope/auth-methods/index.hbs | 4 ++++ .../scopes/scope/auth-methods/new.hbs | 4 ++++ .../scopes/scope/auth-methods-test.js | 12 ++++++++++++ .../scope/auth-methods/auth-method-test.js | 12 ++++++++++++ .../scopes/scope/auth-methods/new-test.js | 12 ++++++++++++ 9 files changed, 97 insertions(+) create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods.js create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods/new.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods/auth-method-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods/new-test.js diff --git a/ui/core/app/controllers/scopes/scope/auth-methods.js b/ui/core/app/controllers/scopes/scope/auth-methods.js new file mode 100644 index 000000000..ab152cf62 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated auth-methods breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.auth-methods'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js b/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js new file mode 100644 index 000000000..1005f0216 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeAuthMethodsAuthMethodController extends Controller { + // =attributes + + /** + * An auth-method breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } +} diff --git a/ui/core/app/controllers/scopes/scope/auth-methods/new.js b/ui/core/app/controllers/scopes/scope/auth-methods/new.js new file mode 100644 index 000000000..ba550f456 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods/new.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated new auth-method breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index 580249d53..c08c3fd4a 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -6,6 +6,10 @@ + + + + diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 5b4fffc2a..2d772bcd6 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -3,6 +3,10 @@

{{t "resources.auth-methods"}}

+ + + + {{#if @model}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs index 315156454..44c4f35d6 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs @@ -5,6 +5,10 @@

{{t "titles.new"}}

+ + + + Date: Fri, 14 Aug 2020 10:49:22 -0400 Subject: [PATCH 09/17] chore: Wrap auth-method description in a paragraph tag --- .../scopes/scope/auth-methods/index.hbs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 5b4fffc2a..e49f040ba 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -27,15 +27,13 @@ {{#each @model as |authMethod|}} -

- - {{authMethod.name}} - -

- {{authMethod.description}} + + {{authMethod.name}} + +

{{authMethod.description}}

From aa867f54d8df803b0068e0d8541f2327302842af Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 14 Aug 2020 17:40:31 -0400 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implement=20initia?= =?UTF-8?q?l=20orgs=20routes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/controllers/scopes/scope/orgs.js | 17 ++++++ .../app/controllers/scopes/scope/orgs/new.js | 17 ++++++ ui/core/app/router.js | 37 +++++++----- ui/core/app/routes/scopes/scope/orgs.js | 15 +++++ ui/core/app/routes/scopes/scope/orgs/index.js | 4 ++ ui/core/app/routes/scopes/scope/orgs/new.js | 18 ++++++ ui/core/app/templates/scopes/scope/orgs.hbs | 3 + .../app/templates/scopes/scope/orgs/index.hbs | 56 +++++++++++++++++++ .../app/templates/scopes/scope/orgs/new.hbs | 17 ++++++ .../controllers/scopes/scope/orgs-test.js | 12 ++++ .../controllers/scopes/scope/orgs/new-test.js | 12 ++++ .../unit/routes/scopes/scope/orgs-test.js | 11 ++++ .../routes/scopes/scope/orgs/index-test.js | 11 ++++ .../unit/routes/scopes/scope/orgs/new-test.js | 11 ++++ ui/core/translations/en-us.yaml | 2 + 15 files changed, 230 insertions(+), 13 deletions(-) create mode 100644 ui/core/app/controllers/scopes/scope/orgs.js create mode 100644 ui/core/app/controllers/scopes/scope/orgs/new.js create mode 100644 ui/core/app/routes/scopes/scope/orgs.js create mode 100644 ui/core/app/routes/scopes/scope/orgs/index.js create mode 100644 ui/core/app/routes/scopes/scope/orgs/new.js create mode 100644 ui/core/app/templates/scopes/scope/orgs.hbs create mode 100644 ui/core/app/templates/scopes/scope/orgs/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/orgs/new.hbs create mode 100644 ui/core/tests/unit/controllers/scopes/scope/orgs-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/orgs/new-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/orgs-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/orgs/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/orgs/new-test.js diff --git a/ui/core/app/controllers/scopes/scope/orgs.js b/ui/core/app/controllers/scopes/scope/orgs.js new file mode 100644 index 000000000..9e553d5a3 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/orgs.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeOrgsController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.orgs'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/orgs/new.js b/ui/core/app/controllers/scopes/scope/orgs/new.js new file mode 100644 index 000000000..d41b81a96 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/orgs/new.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeOrgsNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/core/app/router.js b/ui/core/app/router.js index cf5cea5d6..4521a8b75 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -9,24 +9,15 @@ export default class Router extends EmberRouter { Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { + this.route('authenticate', function () { this.route('method', { path: ':auth_method_id' }); }); - this.route('users', function () { - this.route('user', { path: ':user_id' }, function () {}); - this.route('new'); - }); - this.route('groups', function () { - this.route('group', { path: ':group_id' }, function () {}); - this.route('new'); - }); - this.route('roles', function () { - this.route('role', { path: ':role_id' }, function () { - this.route('grants'); - this.route('principals'); - }); + + this.route('orgs', function() { this.route('new'); }); + this.route('projects', function() { this.route('project', { path: ':project_id' }, function() { this.route('host-catalogs', function() { @@ -36,7 +27,27 @@ Router.map(function () { }); this.route('new'); }); + + this.route('users', function () { + this.route('user', { path: ':user_id' }, function () {}); + this.route('new'); + }); + + this.route('groups', function () { + this.route('group', { path: ':group_id' }, function () {}); + this.route('new'); + }); + + this.route('roles', function () { + this.route('role', { path: ':role_id' }, function () { + this.route('grants'); + this.route('principals'); + }); + this.route('new'); + }); + this.route('auth-methods', function() {}); + }); }); }); diff --git a/ui/core/app/routes/scopes/scope/orgs.js b/ui/core/app/routes/scopes/scope/orgs.js new file mode 100644 index 000000000..e8b0375b9 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/orgs.js @@ -0,0 +1,15 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeOrgsRoute extends Route { + + // =methods + + /** + * Loads all scopes under the global scope. + * @return {Promise{[ScopeModel]}} + */ + async model() { + return this.store.query('scope', { scope_id: 'global' }); + } + +} diff --git a/ui/core/app/routes/scopes/scope/orgs/index.js b/ui/core/app/routes/scopes/scope/orgs/index.js new file mode 100644 index 000000000..4a344e13a --- /dev/null +++ b/ui/core/app/routes/scopes/scope/orgs/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeOrgsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/orgs/new.js b/ui/core/app/routes/scopes/scope/orgs/new.js new file mode 100644 index 000000000..0337f2dce --- /dev/null +++ b/ui/core/app/routes/scopes/scope/orgs/new.js @@ -0,0 +1,18 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeOrgsNewRoute extends Route { + + // =methods + + /** + * Creates a new unsaved org scope belonging to the global scope. + * @return {ScopeModel} + */ + model() { + return this.store.createRecord('scope', { + type: 'org', + scopeID: 'global', + }); + } + +} diff --git a/ui/core/app/templates/scopes/scope/orgs.hbs b/ui/core/app/templates/scopes/scope/orgs.hbs new file mode 100644 index 000000000..35fbae66b --- /dev/null +++ b/ui/core/app/templates/scopes/scope/orgs.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.orgs")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/orgs/index.hbs b/ui/core/app/templates/scopes/scope/orgs/index.hbs new file mode 100644 index 000000000..9abc5722e --- /dev/null +++ b/ui/core/app/templates/scopes/scope/orgs/index.hbs @@ -0,0 +1,56 @@ + + + + + + + +

{{t "resources.orgs"}}

+
+ + + {{#if @model}} + + {{t 'titles.new'}} + + {{/if}} + + + + + {{#if @model}} + + + {{#each @model as |org|}} + + {{/each}} + + + {{else}} + + + + + {{t 'descriptions.orgs-welcome'}} + + + + {{t 'titles.new'}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/orgs/new.hbs b/ui/core/app/templates/scopes/scope/orgs/new.hbs new file mode 100644 index 000000000..0e12b352e --- /dev/null +++ b/ui/core/app/templates/scopes/scope/orgs/new.hbs @@ -0,0 +1,17 @@ +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
diff --git a/ui/core/tests/unit/controllers/scopes/scope/orgs-test.js b/ui/core/tests/unit/controllers/scopes/scope/orgs-test.js new file mode 100644 index 000000000..f89afd0fc --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/orgs-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/orgs', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/orgs'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/orgs/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/orgs/new-test.js new file mode 100644 index 000000000..c6631a1ab --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/orgs/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/orgs/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/orgs/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/orgs-test.js b/ui/core/tests/unit/routes/scopes/scope/orgs-test.js new file mode 100644 index 000000000..4b9d477c3 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/orgs-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/orgs', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/orgs'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/orgs/index-test.js b/ui/core/tests/unit/routes/scopes/scope/orgs/index-test.js new file mode 100644 index 000000000..c62bbc625 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/orgs/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/orgs/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/orgs/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/orgs/new-test.js b/ui/core/tests/unit/routes/scopes/scope/orgs/new-test.js new file mode 100644 index 000000000..3cfdb8522 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/orgs/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/orgs/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/orgs/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 2fe1ad5f8..c64e37ceb 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -27,6 +27,8 @@ resources: roles: Roles group: Group groups: Groups + orgs: Orgs + org: Org projects: Projects project: Project host-catalog: Host Catalog From 11e94aa0c35129caa17b659c21fd8ed397228469 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 14 Aug 2020 17:53:49 -0400 Subject: [PATCH 11/17] =?UTF-8?q?chore:=20=F0=9F=A4=96=20hide=20sidebar=20?= =?UTF-8?q?when=20in=20global=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/templates/scopes/scope.hbs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 37530455c..5f60d2607 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -1,6 +1,7 @@ {{#if this.session.isAuthenticated}} + {{#unless @model.isGlobal}} @@ -26,6 +27,7 @@ {{outlet "sidebar"}} + {{/unless}} {{/if}} From 2a49d4601f23077354a4e751b841821ff94e87c0 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 17 Aug 2020 12:25:07 -0400 Subject: [PATCH 12/17] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20new=20org=20?= =?UTF-8?q?functionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/components/form/org/index.hbs | 49 ++++++++++++ ui/core/app/routes/scopes/scope/orgs.js | 41 ++++++++++ .../app/templates/scopes/scope/orgs/new.hbs | 6 +- ui/core/tests/acceptance/orgs-test.js | 76 +++++++++++++++++++ .../integration/components/form/org-test.js | 23 ++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/components/form/org/index.hbs create mode 100644 ui/core/tests/acceptance/orgs-test.js create mode 100644 ui/core/tests/integration/components/form/org-test.js diff --git a/ui/core/app/components/form/org/index.hbs b/ui/core/app/components/form/org/index.hbs new file mode 100644 index 000000000..45adc26d0 --- /dev/null +++ b/ui/core/app/components/form/org/index.hbs @@ -0,0 +1,49 @@ + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + diff --git a/ui/core/app/routes/scopes/scope/orgs.js b/ui/core/app/routes/scopes/scope/orgs.js index e8b0375b9..f25a91cf9 100644 --- a/ui/core/app/routes/scopes/scope/orgs.js +++ b/ui/core/app/routes/scopes/scope/orgs.js @@ -1,7 +1,14 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; export default class ScopesScopeOrgsRoute extends Route { + // =services + + @service intl; + @service notify; + // =methods /** @@ -12,4 +19,38 @@ export default class ScopesScopeOrgsRoute extends Route { return this.store.query('scope', { scope_id: 'global' }); } + // =actions + + /** + * Rollback changes on org. + * @param {ScopeModel} org + */ + @action + cancel(org) { + const { isNew } = org; + org.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.orgs'); + } + + /** + * Handle save org. + * @param {ScopeModel} org + * @param {Event} e + */ + @action + async save(org) { + const { isNew } = org; + try { + await org.save(); + this.refresh(); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); + this.transitionTo('scopes.scope', org); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + } diff --git a/ui/core/app/templates/scopes/scope/orgs/new.hbs b/ui/core/app/templates/scopes/scope/orgs/new.hbs index 0e12b352e..d9fe8b361 100644 --- a/ui/core/app/templates/scopes/scope/orgs/new.hbs +++ b/ui/core/app/templates/scopes/scope/orgs/new.hbs @@ -11,7 +11,11 @@ - +
diff --git a/ui/core/tests/acceptance/orgs-test.js b/ui/core/tests/acceptance/orgs-test.js new file mode 100644 index 000000000..5871fd404 --- /dev/null +++ b/ui/core/tests/acceptance/orgs-test.js @@ -0,0 +1,76 @@ +import { module, test } from 'qunit'; +//import { visit, currentURL, click, fillIn, find } 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, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | org', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + // let org; + // let orgID; + // let orgsURL; + // let newOrgURL; + // let existingOrg; + // let existingOrgURL; + // let getOrgScopesCount; + // let getFirstOrgScope; + // let initialOrgScopesCount; + // + // hooks.beforeEach(function () { + // authenticateSession({}); + // + // }); + + test('visiting orgs', async function (assert) { + assert.expect(0); + }); + + test('visiting orgs within a org scope redirects to the parent org scope', async function (assert) { + assert.expect(0); + }); + + test('can create new orgs', async function (assert) { + assert.expect(0); + }); + + test('can cancel create new orgs', async function (assert) { + assert.expect(0); + }); + + test('saving a new org with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('can save changes to existing org', async function (assert) { + assert.expect(0); + }); + + test('can cancel changes to existing org', async function (assert) { + assert.expect(0); + }); + + test('can delete org', async function (assert) { + assert.expect(0); + }); + + test('saving an existing org with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when save org fails', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when delete org fails', async function (assert) { + assert.expect(0); + }); +}); diff --git a/ui/core/tests/integration/components/form/org-test.js b/ui/core/tests/integration/components/form/org-test.js new file mode 100644 index 000000000..76c3d9b8f --- /dev/null +++ b/ui/core/tests/integration/components/form/org-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/org', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + this.submit = () => {}; + this.cancel = () => {}; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); From 4be7dc7003b29890018b87074976b11ca12009fa Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 17 Aug 2020 14:43:12 -0400 Subject: [PATCH 13/17] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20feature=20to?= =?UTF-8?q?=20display=20and=20authenticate=20with=20global?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/index.js | 6 +-- ui/core/app/routes/scopes/scope.js | 19 ++++++-- .../app/routes/scopes/scope/authenticate.js | 13 +++--- .../scopes/scope/authenticate/method.js | 10 ++--- ui/core/mirage/scenarios/default.js | 7 ++- .../tests/acceptance/authentication-test.js | 44 +++++++++++-------- ui/core/translations/en-us.yaml | 1 + 7 files changed, 63 insertions(+), 37 deletions(-) diff --git a/ui/core/app/routes/scopes/index.js b/ui/core/app/routes/scopes/index.js index 562a27a15..b1ec77f7f 100644 --- a/ui/core/app/routes/scopes/index.js +++ b/ui/core/app/routes/scopes/index.js @@ -15,15 +15,15 @@ export default class ScopesIndexRoute extends Route { * @param {[ScopeModel]} model * @param {?ScopeModel} model.firstObject */ - redirect({ firstObject }) { + redirect() { const authenticatedScopeID = get( this.session, 'data.authenticated.scope.id' ); if (authenticatedScopeID) { this.transitionTo('scopes.scope', authenticatedScopeID); - } else if (firstObject) { - this.transitionTo('scopes.scope', firstObject.id); + } else { + this.transitionTo('scopes.scope', 'global'); } } } diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 2707a238b..06158aa4d 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -1,6 +1,12 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; export default class ScopesScopeRoute extends Route { + + // =services + + @service intl; + // =methods /** @@ -14,11 +20,18 @@ export default class ScopesScopeRoute extends Route { * @return {Promise{ScopeModel}} */ model({ scope_id: id }) { + // Since only global and org scopes are authenticatable, we can infer type + // from ID because global has a fixed ID. + const scopeType = id === 'global' ? 'global' : 'org'; return this.store.findRecord('scope', id).catch(() => { const maybeExistingScope = this.store.peekRecord('scope', id); - // TODO it's unclear if creating a temporary unsaved scope is reliable - // we may need to revert to a POJO. - return maybeExistingScope || this.store.createRecord('scope', { id }); + const scopeOptions = { id, scopeType }; + if (scopeType === 'global') { + scopeOptions.name = this.intl.t('titles.global'); + } + return maybeExistingScope || + this.store.createRecord('scope', scopeOptions); }); } + } diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js index d7dae560d..ef08a1566 100644 --- a/ui/core/app/routes/scopes/scope/authenticate.js +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -7,12 +7,6 @@ export default class ScopesScopeAuthenticateRoute extends Route { @service session; - // =attributes - - // TODO: this assumes we're in an org scope, but the "landing" - // for global scope is probably different. - routeIfAlreadyAuthenticated = 'scopes.scope.projects'; - // =methods /** @@ -37,7 +31,12 @@ export default class ScopesScopeAuthenticateRoute extends Route { redirect() { if (this.session.isAuthenticated) { - this.transitionTo(this.routeIfAlreadyAuthenticated); + const scope = this.modelFor('scopes.scope'); + if (scope.isGlobal) { + this.transitionTo('scopes.scope.orgs'); + } else { + this.transitionTo('scopes.scope.projects'); + } } } } diff --git a/ui/core/app/routes/scopes/scope/authenticate/method.js b/ui/core/app/routes/scopes/scope/authenticate/method.js index 5208b152d..ed1ebc026 100644 --- a/ui/core/app/routes/scopes/scope/authenticate/method.js +++ b/ui/core/app/routes/scopes/scope/authenticate/method.js @@ -9,10 +9,6 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { @service notify; @service intl; - // =attributes - - routeAfterAuthentication = 'scopes.scope.projects'; - // =methods /** @@ -55,7 +51,11 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { requestCookies, { scope, authMethod } ); - this.transitionTo(this.routeAfterAuthentication); + if (scope.isGlobal) { + this.transitionTo('scopes.scope.orgs'); + } else { + this.transitionTo('scopes.scope.projects'); + } } catch (e) { const errorMessage = this.intl.t('errors.titles.authentication-failed'); this.notify.error(errorMessage, { closeAfter: null }); diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index 6f8e29775..e6bfe856c 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -2,7 +2,12 @@ export default function(server) { // Scope resources - const globalScope = server.create('scope', { id: 'global' }); // creates a global scope + // creates a global scope + const globalScope = server.create('scope', { + id: 'global', + type: 'global', + name: 'Global' + }); const orgScope = server.createList('scope', 3, { type: 'org', scope: { id: globalScope.id, type: globalScope.type } diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js index 478e2f1dd..04107a7a9 100644 --- a/ui/core/tests/acceptance/authentication-test.js +++ b/ui/core/tests/acceptance/authentication-test.js @@ -23,8 +23,11 @@ module('Acceptance | authentication', function (hooks) { let scope; let authMethod; let authMethodID; + let globalAuthenticateURL; let authenticateURL; let authMethodAuthenticateURL; + let authMethodGlobalAuthenticateURL; + let orgsURL; let projectsURL; hooks.beforeEach(function () { @@ -45,21 +48,14 @@ module('Acceptance | authentication', function (hooks) { authMethodID = authMethod.id; scopesURL = `/scopes`; orgScopeURL = `/scopes/${orgScopeID}`; + globalAuthenticateURL = `/scopes/global/authenticate`; authenticateURL = `/scopes/${orgScopeID}/authenticate`; + authMethodGlobalAuthenticateURL = `/scopes/global/authenticate/${authMethodID}`; authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`; + orgsURL = `/scopes/global/orgs`; projectsURL = `/scopes/${orgScopeID}/projects`; }); - - test('visiting scopes when there are none shows a message', async function (assert) { - assert.expect(2); - this.server.get('/scopes', () => new Response(404)); - await visit(scopesURL); - await a11yAudit(); - assert.equal(currentURL(), scopesURL); - assert.ok(find('.rose-message')); - }); - test('visiting auth methods authenticate route redirects to first auth method', async function (assert) { assert.expect(1); await visit(authenticateURL); @@ -96,19 +92,19 @@ module('Acceptance | authentication', function (hooks) { assert.equal(currentURL(), authMethodAuthenticateURL); }); - test('visiting any authentication parent route while unauthenticated redirects to first authenticate method', async function (assert) { + test('visiting any authentication parent route while unauthenticated redirects to first global authenticate method', async function (assert) { assert.expect(3); await visit(indexURL); - assert.equal(currentURL(), authMethodAuthenticateURL); + assert.equal(currentURL(), authMethodGlobalAuthenticateURL); await visit(scopesURL); - assert.equal(currentURL(), authMethodAuthenticateURL); + assert.equal(currentURL(), authMethodGlobalAuthenticateURL); assert.notOk(currentSession().isAuthenticated); }); - test('visiting projects while unauthenticated redirects to first authenticate method', async function (assert) { + test('visiting projects while unauthenticated redirects to first global authenticate method', async function (assert) { assert.expect(2); await visit(projectsURL); - assert.equal(currentURL(), authMethodAuthenticateURL); + assert.equal(currentURL(), authMethodGlobalAuthenticateURL); assert.notOk(currentSession().isAuthenticated); }); @@ -119,7 +115,7 @@ module('Acceptance | authentication', function (hooks) { assert.equal(currentURL(), authMethodAuthenticateURL); }); - test('visiting any authentication parent route while already authenticated redirects to projects', async function (assert) { + test('visiting any authentication parent route while already authenticated with an org redirects to projects', async function (assert) { assert.expect(6); authenticateSession({ scope }); await visit(indexURL); @@ -135,6 +131,18 @@ module('Acceptance | authentication', function (hooks) { assert.ok(currentSession().isAuthenticated); }); + test('visiting index or scopes routes while already authenticated with global redirects to orgs', async function (assert) { + assert.expect(4); + authenticateSession({ scope: { id: globalScope.id, type: globalScope.type } }); + await visit(indexURL); + assert.equal(currentURL(), orgsURL); + await visit(scopesURL); + assert.equal(currentURL(), orgsURL); + await visit(globalAuthenticateURL); + assert.equal(currentURL(), orgsURL); + assert.ok(currentSession().isAuthenticated); + }); + test('failed authentication shows a notification message', async function (assert) { assert.expect(3); await visit(authMethodAuthenticateURL); @@ -157,7 +165,7 @@ module('Acceptance | authentication', function (hooks) { assert.ok(currentSession().isAuthenticated); }); - test('deauthentication redirects to first authenticate method', async function (assert) { + test('deauthentication redirects to first global authenticate method', async function (assert) { assert.expect(3); await visit(authMethodAuthenticateURL); await fillIn('[name="identification"]', 'test'); @@ -166,6 +174,6 @@ module('Acceptance | authentication', function (hooks) { assert.ok(currentSession().isAuthenticated); await click('.rose-button-header-dropdown'); assert.notOk(currentSession().isAuthenticated); - assert.equal(currentURL(), authMethodAuthenticateURL); + assert.equal(currentURL(), authMethodGlobalAuthenticateURL); }); }); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index c64e37ceb..0758222ee 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -52,6 +52,7 @@ titles: grants: Permissions new-grant: New Grant principals: Principals + global: Global descriptions: host-catalogs: Overview of collections of hosts and host sets. empty-set: There are no items to display yet. You may be able to add items or try back later. From d9d429cb9d3ca715821b95331d21314ec02cf410 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 17 Aug 2020 14:56:19 -0400 Subject: [PATCH 14/17] =?UTF-8?q?chore:=20=F0=9F=A4=96=20global=20should?= =?UTF-8?q?=20be=20sorted=20to=20first=20place?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/scopes/scope/authenticate.js | 21 +++++++++++++++++++ .../templates/scopes/scope/authenticate.hbs | 2 +- .../scopes/scope/authenticate-test.js | 12 +++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/controllers/scopes/scope/authenticate.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/authenticate-test.js diff --git a/ui/core/app/controllers/scopes/scope/authenticate.js b/ui/core/app/controllers/scopes/scope/authenticate.js new file mode 100644 index 000000000..b982a3c1c --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/authenticate.js @@ -0,0 +1,21 @@ +import Controller from '@ember/controller'; +import { sort } from '@ember/object/computed'; + +export default class ScopesScopeAuthenticateController extends Controller { + + // =attributes + + /** + * @type {Array} + */ + @sort('model.scopes', (a, b) => { + if (a.isGlobal) { + return -1; + } else if (b.isGlobal) { + return 1; + } + return 0; + }) + sortedScopes; + +} diff --git a/ui/core/app/templates/scopes/scope/authenticate.hbs b/ui/core/app/templates/scopes/scope/authenticate.hbs index 43b329188..75a60893f 100644 --- a/ui/core/app/templates/scopes/scope/authenticate.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate.hbs @@ -14,7 +14,7 @@ @text={{t "actions.choose-different-org"}} @icon="user-organization" as |dropdown|> - {{#each @model.scopes as |scope|}} + {{#each this.sortedScopes as |scope|}} {{scope.displayName}} diff --git a/ui/core/tests/unit/controllers/scopes/scope/authenticate-test.js b/ui/core/tests/unit/controllers/scopes/scope/authenticate-test.js new file mode 100644 index 000000000..41bd58b67 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/authenticate-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/authenticate', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/authenticate'); + assert.ok(controller); + }); +}); From e7875849ecee2faeb27dacb623801009c5bb327a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 18 Aug 2020 16:20:32 -0400 Subject: [PATCH 15/17] =?UTF-8?q?fix:=20=F0=9F=90=9B=20move=20headers=20to?= =?UTF-8?q?=20prototype=20and=20send=20with=20scopes=20requests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/authenticators/password.js | 45 ++++++++++++++++- ui/core/app/session-stores/application.js | 34 +------------ .../unit/authenticators/password-test.js | 50 +++++++++++++++++++ .../unit/session-stores/application-test.js | 41 --------------- 4 files changed, 95 insertions(+), 75 deletions(-) create mode 100644 ui/core/tests/unit/authenticators/password-test.js delete mode 100644 ui/core/tests/unit/session-stores/application-test.js diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js index a70c89ac7..88bf63c0a 100644 --- a/ui/core/app/authenticators/password.js +++ b/ui/core/app/authenticators/password.js @@ -37,9 +37,52 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} scopeID * @return {string} */ - buildDeauthEndpointURL({ org_id: scopeID }) { + buildDeauthEndpointURL({ scope: { id: scopeID } }) { const adapter = this.store.adapterFor('application'); const options = { adapterOptions: { method: 'deauthenticate' } }; return adapter.buildURL('scope', scopeID, options, 'findRecord'); } + + /** + * Intercepts the authenticate response, if any, and assigns the returned + * token to all future requests via `addTokenToAuthorization`. + * Returns the response data as normal. + * @override + */ + authenticate() { + return super.authenticate(...arguments) + .then(data => { + const token = data?.token; + if (token) this.addTokenToAuthorization(token); + return data; + }); + } + + /** + * When restoring the session (say, on load), assigns the session token + * to all future requests via `addTokenToAuthorization`. + * @override + * @param {object} data + * @return {object} + */ + restore(data) { + const token = data?.token; + if (token) this.addTokenToAuthorization(token); + return super.restore(data); + } + + /** + * Assigns a token string all future requests via the `Authorization` header + * on the application adapter prototype. Only application and adapters that + * extend the application adapter receive this header (which is most). + * @param {string} token + */ + addTokenToAuthorization(token) { + const adapterPrototype = + this.store.adapterFor('application').constructor.prototype; + const headers = adapterPrototype?.headers; + if (!headers) adapterPrototype.headers = {}; + adapterPrototype.headers.Authorization = null; + if (token) adapterPrototype.headers.Authorization = `Bearer ${token}`; + } } diff --git a/ui/core/app/session-stores/application.js b/ui/core/app/session-stores/application.js index 401a98b44..681b7b12e 100644 --- a/ui/core/app/session-stores/application.js +++ b/ui/core/app/session-stores/application.js @@ -1,38 +1,6 @@ import LocalStorageSessionStore from 'auth/session-stores/local-storage'; -import { inject as service } from '@ember/service'; -import { get } from '@ember/object'; -//import config from '../config/environment'; -export default class ApplicationSessionStore extends LocalStorageSessionStore { - @service store; - - /** - * If the auth sessionData includes a token, apply it as a bearer token in the - * application adapter's Authorization header. Otherwise clear the header. - * @param {object} sessionData - * @return {object} - */ - persist(sessionData) { - this.addTokenToAuthorization(sessionData); - //this.addScopeToSession(sessionData); - return super.persist(...arguments); - } - - /** - * If the session has a full token, it is added as a bearer token to the - * `Authorization` header for all API requests. - * @param {object} sessionData - */ - addTokenToAuthorization(sessionData) { - const token = get(sessionData, 'authenticated.token'); - const adapter = this.store.adapterFor('application'); - const headers = get(adapter, 'headers'); - if (!headers) adapter.headers = {}; - adapter.headers.Authorization = null; - if (token) adapter.headers.Authorization = `Bearer ${token}`; - } - -} +export default class ApplicationSessionStore extends LocalStorageSessionStore {} // import CookieSessionStore from 'auth/session-stores/cookie'; // import config from '../config/environment'; diff --git a/ui/core/tests/unit/authenticators/password-test.js b/ui/core/tests/unit/authenticators/password-test.js new file mode 100644 index 000000000..21786ec86 --- /dev/null +++ b/ui/core/tests/unit/authenticators/password-test.js @@ -0,0 +1,50 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Authenticator | password', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + let store; + let applicationAdapter; + let session; + let authenticator; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:store'); + applicationAdapter = store.adapterFor('application'); + session = this.owner.lookup('service:session'); + authenticator = this.owner.lookup('authenticator:password'); + session.invalidate(); + }); + + hooks.afterEach(function () { + session.invalidate(); + }); + + test('it does not add an authorization header to application adapter on restore if no token is present', async function (assert) { + assert.expect(1); + authenticator.restore({ token: null }); + assert.notOk(applicationAdapter?.headers?.Authorization); + }); + + test('it adds an authorization header to application adapter on restore', async function (assert) { + assert.expect(1); + authenticator.restore({ token: 'token1234' }); + assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); + }); + + test('it adds an authorization header to application adapter on authenticate', async function (assert) { + assert.expect(1); + const creds = { identification: 'foo', password: 'bar' }; + const scope = { id: 'global' }; + const authMethod = { id: 'paum_1234' }; + await authenticator.authenticate( + creds, + null, + { scope, authMethod } + ); + assert.equal(applicationAdapter.headers.Authorization, 'Bearer thetokenstring'); + }); +}); diff --git a/ui/core/tests/unit/session-stores/application-test.js b/ui/core/tests/unit/session-stores/application-test.js deleted file mode 100644 index 6a3e908bc..000000000 --- a/ui/core/tests/unit/session-stores/application-test.js +++ /dev/null @@ -1,41 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Session Store | application', function (hooks) { - setupTest(hooks); - - let store; - let applicationAdapter; - let sessionStore; - - hooks.beforeEach(function () { - store = this.owner.lookup('service:store'); - applicationAdapter = store.adapterFor('application'); - sessionStore = this.owner.lookup('session-store:application'); - sessionStore.clear(); - }); - - hooks.afterEach(function () { - sessionStore.clear(); - }); - - test('it does not add an authorization header to application adapter on persist if no token is present', async function (assert) { - assert.expect(1); - sessionStore.persist({ authenticated: { token: null } }); - assert.notOk(applicationAdapter.headers.Authorization); - }); - - test('it adds an authorization header to application adapter on persist', async function (assert) { - assert.expect(1); - sessionStore.persist({ authenticated: { token: 'token1234' } }); - assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); - }); - - test('it adds an authorization header to application adapter which already has headers on persist', async function (assert) { - assert.expect(2); - applicationAdapter.headers = { foo: 'bar' }; - sessionStore.persist({ authenticated: { token: 'token1234' } }); - assert.equal(applicationAdapter.headers.foo, 'bar'); - assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); - }); -}); From 74b399b122ef830ad93c8d45dfe1fd7b3f068a19 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 18 Aug 2020 16:49:45 -0400 Subject: [PATCH 16/17] =?UTF-8?q?fix:=20=F0=9F=90=9B=20all=20top=20level?= =?UTF-8?q?=20routes=20should=20redirect=20in=20unauthenticated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/auth-methods.js | 8 ++++++++ ui/core/app/routes/scopes/scope/groups.js | 8 ++++++++ ui/core/app/routes/scopes/scope/orgs.js | 8 ++++++++ ui/core/app/routes/scopes/scope/roles.js | 8 ++++++++ ui/core/app/routes/scopes/scope/users.js | 8 ++++++++ ui/core/tests/unit/authenticators/password-test.js | 6 ------ 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index ffad801fa..0e35b2c28 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -7,9 +7,17 @@ export default class ScopesScopeAuthMethodsRoute extends Route { @service intl; @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Load all auth-methods under current scope * @return {Promise[AuthMethodModel]} diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 7eed4406e..c2c75c8fb 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -7,9 +7,17 @@ export default class ScopesScopeGroupsRoute extends Route { @service intl; @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Load all groups under current scope * @return {Promise[GroupModel]} diff --git a/ui/core/app/routes/scopes/scope/orgs.js b/ui/core/app/routes/scopes/scope/orgs.js index f25a91cf9..c2fa56ffc 100644 --- a/ui/core/app/routes/scopes/scope/orgs.js +++ b/ui/core/app/routes/scopes/scope/orgs.js @@ -8,9 +8,17 @@ export default class ScopesScopeOrgsRoute extends Route { @service intl; @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Loads all scopes under the global scope. * @return {Promise{[ScopeModel]}} diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index 84cf2ae0f..b0418e399 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -7,9 +7,17 @@ export default class ScopesScopeRolesRoute extends Route { @service intl; @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Load all roles under current scope. * @return {Promise{[RoleModel]}} diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index f1982b769..20731c90f 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -7,9 +7,17 @@ export default class ScopesScopeUsersRoute extends Route { @service intl; @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Load all users under current scope. * @return {Promise{[UserModel]}} diff --git a/ui/core/tests/unit/authenticators/password-test.js b/ui/core/tests/unit/authenticators/password-test.js index 21786ec86..8bf59c9ee 100644 --- a/ui/core/tests/unit/authenticators/password-test.js +++ b/ui/core/tests/unit/authenticators/password-test.js @@ -23,12 +23,6 @@ module('Unit | Authenticator | password', function (hooks) { session.invalidate(); }); - test('it does not add an authorization header to application adapter on restore if no token is present', async function (assert) { - assert.expect(1); - authenticator.restore({ token: null }); - assert.notOk(applicationAdapter?.headers?.Authorization); - }); - test('it adds an authorization header to application adapter on restore', async function (assert) { assert.expect(1); authenticator.restore({ token: 'token1234' }); From d101870253cf1bf09472e966db75dfcca81c1886 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 18 Aug 2020 16:58:17 -0400 Subject: [PATCH 17/17] =?UTF-8?q?fix:=20=F0=9F=90=9B=20fix=20issue=20where?= =?UTF-8?q?=20global=20doesn't=20redirect=20to=20orgs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 06158aa4d..08cd2d207 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -22,11 +22,11 @@ export default class ScopesScopeRoute extends Route { model({ scope_id: id }) { // Since only global and org scopes are authenticatable, we can infer type // from ID because global has a fixed ID. - const scopeType = id === 'global' ? 'global' : 'org'; + const type = id === 'global' ? 'global' : 'org'; return this.store.findRecord('scope', id).catch(() => { const maybeExistingScope = this.store.peekRecord('scope', id); - const scopeOptions = { id, scopeType }; - if (scopeType === 'global') { + const scopeOptions = { id, type }; + if (type === 'global') { scopeOptions.name = this.intl.t('titles.global'); } return maybeExistingScope ||