Merge pull request #121 from hashicorp/feature-org-scope-crud
Feature org scope crud
This commit is contained in:
36 files changed
+647
-113
No files matched your search
@@ -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}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@error={{@model.errors.base}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<form.input
|
||||
@name="name"
|
||||
@type="name"
|
||||
@value={{@model.name}}
|
||||
@label={{t "form.label.name"}}
|
||||
@error={{@model.errors.name}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.textarea
|
||||
@name="description"
|
||||
@type="description"
|
||||
@value={{@model.description}}
|
||||
@label={{t "form.label.description"}}
|
||||
@error={{@model.errors.description}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.errors.description}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
<form.actions
|
||||
@disabled={{if @model.cannotSave @model.cannotSave}}
|
||||
@submitText={{t "actions.save"}}
|
||||
@cancelText={{t "actions.cancel"}} />
|
||||
|
||||
</Rose::Form>
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -9,17 +9,35 @@ 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('orgs', function() {
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
this.route('projects', function() {
|
||||
this.route('project', { path: ':project_id' }, function() {
|
||||
this.route('host-catalogs', function() {
|
||||
this.route('new');
|
||||
this.route('host-catalog', { path: ':host_catalog_id' }, 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');
|
||||
@@ -27,6 +45,7 @@ Router.map(function () {
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
this.route('projects', function() {
|
||||
this.route('project', { path: ':project_id' }, function() {
|
||||
this.route('host-catalogs', function() {
|
||||
@@ -40,6 +59,7 @@ Router.map(function () {
|
||||
this.route('auth-method', { path: ':auth_method_id' }, function() {});
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 type = 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, type };
|
||||
if (type === 'global') {
|
||||
scopeOptions.name = this.intl.t('titles.global');
|
||||
}
|
||||
return maybeExistingScope ||
|
||||
this.store.createRecord('scope', scopeOptions);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
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;
|
||||
@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]}}
|
||||
*/
|
||||
async model() {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeOrgsIndexRoute extends Route {
|
||||
}
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]}}
|
||||
|
||||
@@ -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]}}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<Rose::Layout::Sidebar as |sidebarLayout|>
|
||||
|
||||
{{#if this.session.isAuthenticated}}
|
||||
{{#unless @model.isGlobal}}
|
||||
<sidebarLayout.sidebar>
|
||||
<Rose::Nav::Sidebar @title={{t "titles.general"}} as |nav|>
|
||||
<nav.link @route="scopes.scope.projects">
|
||||
@@ -26,6 +27,7 @@
|
||||
{{outlet "sidebar"}}
|
||||
|
||||
</sidebarLayout.sidebar>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
<sidebarLayout.body>
|
||||
|
||||
@@ -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|}}
|
||||
<dropdown.link @route="scopes.scope.authenticate" @model={{scope}}>
|
||||
{{scope.displayName}}
|
||||
</dropdown.link>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{{title (t "resources.orgs")}}
|
||||
|
||||
{{outlet}}
|
||||
@@ -0,0 +1,56 @@
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.orgs"}}</h2>
|
||||
</page.header>
|
||||
|
||||
<page.actions>
|
||||
{{#if @model}}
|
||||
<Rose::LinkButton
|
||||
@route="scopes.scope.orgs.new"
|
||||
@style="primary"
|
||||
>
|
||||
{{t 'titles.new'}}
|
||||
</Rose::LinkButton>
|
||||
{{/if}}
|
||||
</page.actions>
|
||||
|
||||
<page.body>
|
||||
|
||||
{{#if @model}}
|
||||
|
||||
<Rose::Cards as |cards|>
|
||||
{{#each @model as |org|}}
|
||||
<cards.link
|
||||
@title={{org.name}}
|
||||
@subtitle={{org.id}}
|
||||
@description={{org.description}}
|
||||
@route="scopes.scope"
|
||||
@model={{org}}
|
||||
/>
|
||||
{{/each}}
|
||||
</Rose::Cards>
|
||||
|
||||
{{else}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t 'titles.orgs-welcome'}} as |message|>
|
||||
<message.description>
|
||||
{{t 'descriptions.orgs-welcome'}}
|
||||
</message.description>
|
||||
<message.link @route="scopes.scope.orgs.new">
|
||||
<Rose::Icon @name="plus-circle-outline" />
|
||||
{{t 'titles.new'}}
|
||||
</message.link>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
{{/if}}
|
||||
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -0,0 +1,21 @@
|
||||
{{title (t 'titles.new')}}
|
||||
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "titles.new"}}</h2>
|
||||
</page.header>
|
||||
|
||||
<page.body>
|
||||
<Form::Org
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
/>
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -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 }
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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`
|
||||
<Form::Org
|
||||
@submit={{this.submit}}
|
||||
@cancel={{this.cancel}}
|
||||
/>`);
|
||||
|
||||
assert.ok(find('form'));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
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 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');
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -27,6 +27,8 @@ resources:
|
||||
roles: Roles
|
||||
group: Group
|
||||
groups: Groups
|
||||
orgs: Orgs
|
||||
org: Org
|
||||
projects: Projects
|
||||
project: Project
|
||||
host-catalog: Host Catalog
|
||||
@@ -51,6 +53,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.
|
||||
|
||||
Reference in new issue
Block a user