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.