Icu 12592 add filtering pagination support for auth methods (#2209)
* feat: 🎸 add filtering & pagination to auth methods ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12592 --------- Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com> Co-authored-by: Cameron Perera <cameron.perera@hashicorp.com>
This commit is contained in:
1 parent
13da35c06b
commit
ded8bdadd1
22 files changed
+623
-327
No files matched your search
@@ -4,11 +4,33 @@
|
||||
*/
|
||||
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { debounce } from 'core/decorators/debounce';
|
||||
import { TYPES_AUTH_METHOD } from 'api/models/auth-method';
|
||||
|
||||
export default class ScopesScopeAuthMethodsIndexController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
queryParams = [
|
||||
'search',
|
||||
{ types: { type: 'array' } },
|
||||
{ primary: { type: 'array' } },
|
||||
'page',
|
||||
'pageSize',
|
||||
];
|
||||
|
||||
@tracked search;
|
||||
@tracked types = [];
|
||||
@tracked primary = [];
|
||||
@tracked page = 1;
|
||||
@tracked pageSize = 10;
|
||||
|
||||
/**
|
||||
* True if the current scope has a primary auth method set.
|
||||
* @type {boolean}
|
||||
@@ -17,8 +39,59 @@ export default class ScopesScopeAuthMethodsIndexController extends Controller {
|
||||
return Boolean(this.scopeModel.primary_auth_method_id);
|
||||
}
|
||||
|
||||
get primaryOptions() {
|
||||
return [
|
||||
{ id: 'true', name: this.intl.t('actions.yes') },
|
||||
{ id: 'false', name: this.intl.t('actions.no') },
|
||||
];
|
||||
}
|
||||
|
||||
get authMethodTypeOptions() {
|
||||
return TYPES_AUTH_METHOD.map((type) => ({
|
||||
id: type,
|
||||
name: this.intl.t(`resources.auth-method.types.${type}`),
|
||||
}));
|
||||
}
|
||||
|
||||
get filters() {
|
||||
return {
|
||||
allFilters: {
|
||||
primary: this.primaryOptions,
|
||||
types: this.authMethodTypeOptions,
|
||||
},
|
||||
selectedFilters: {
|
||||
primary: this.primary,
|
||||
types: this.types,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handles input on each keystroke and the search queryParam
|
||||
* @param {object} event
|
||||
*/
|
||||
@action
|
||||
@debounce(250)
|
||||
handleSearchInput(event) {
|
||||
const { value } = event.target;
|
||||
this.search = value;
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a query param to the value of selectedItems
|
||||
* and resets the page to 1.
|
||||
* @param {string} paramKey
|
||||
* @param {[string]} selectedItems
|
||||
*/
|
||||
@action
|
||||
applyFilter(paramKey, selectedItems) {
|
||||
this[paramKey] = [...selectedItems];
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from array `property` at `index` on the
|
||||
* passed `authMethod`. This is used to manage entries in fragment array
|
||||
|
||||
@@ -30,8 +30,14 @@ export default class ScopesScopeTargetsIndexController extends Controller {
|
||||
|
||||
get availableSessionOptions() {
|
||||
return [
|
||||
{ id: 'yes', name: this.intl.t('actions.yes') },
|
||||
{ id: 'no', name: this.intl.t('actions.no') },
|
||||
{
|
||||
id: 'yes',
|
||||
name: this.intl.t('resources.target.filter.active-sessions.yes'),
|
||||
},
|
||||
{
|
||||
id: 'no',
|
||||
name: this.intl.t('resources.target.filter.active-sessions.no'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,23 +9,14 @@ import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
import { resourceFilter } from 'core/decorators/resource-filter';
|
||||
import { TYPES_AUTH_METHOD } from 'api/models/auth-method';
|
||||
|
||||
export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
@service session;
|
||||
@service can;
|
||||
@service resourceFilterStore;
|
||||
@service router;
|
||||
|
||||
// =attributes
|
||||
|
||||
@resourceFilter({ allowed: TYPES_AUTH_METHOD }) type;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
@@ -35,24 +26,8 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
if (!this.session.isAuthenticated) this.router.transitionTo('index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all auth-methods under current scope
|
||||
* @return {Promise[AuthMethodModel]}
|
||||
*/
|
||||
async model() {
|
||||
const scope = this.modelFor('scopes.scope');
|
||||
const { id: scope_id } = scope;
|
||||
if (this.can.can('list model', scope, { collection: 'auth-methods' })) {
|
||||
const { type } = this;
|
||||
return this.resourceFilterStore.queryBy(
|
||||
'auth-method',
|
||||
{ type },
|
||||
{ scope_id },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Rollback changes to an auth-method.
|
||||
* @param {AuthMethodModel} authMethod
|
||||
@@ -102,7 +77,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
// Reload the scope, since this is where the primary_auth_method_id is
|
||||
// stored. An auth method deletion could affect this field.
|
||||
await scopeModel.reload();
|
||||
await this.router.replaceWith('scopes.scope.auth-methods');
|
||||
this.router.replaceWith('scopes.scope.auth-methods');
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
@@ -203,22 +178,4 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
const array = [...existingArray, { from, to }];
|
||||
authMethod.set(field, array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified resource filter field to the specified value.
|
||||
* @param {string} field
|
||||
* @param value
|
||||
*/
|
||||
@action
|
||||
filterBy(field, value) {
|
||||
this[field] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears and filter selections.
|
||||
*/
|
||||
@action
|
||||
clearAllFilters() {
|
||||
this.type = [];
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,109 @@
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeAuthMethodsIndexRoute extends Route {
|
||||
// =services
|
||||
|
||||
@service store;
|
||||
@service can;
|
||||
@service router;
|
||||
|
||||
// =attributes
|
||||
|
||||
queryParams = {
|
||||
search: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
types: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
primary: {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
pageSize: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads queried auth-methods and the number of auth-methods under current scope.
|
||||
* @returns {Promise<{totalItems: number, authMethods: [AuthMethodModel], authMethodsExist: boolean }> }
|
||||
*/
|
||||
async model({ search, types, primary, page, pageSize }) {
|
||||
const scope = this.modelFor('scopes.scope');
|
||||
const { id: scope_id } = scope;
|
||||
const filters = {
|
||||
scope_id: [{ equals: scope_id }],
|
||||
type: [],
|
||||
is_primary: [],
|
||||
};
|
||||
|
||||
types.forEach((type) => {
|
||||
filters.type.push({ equals: type });
|
||||
});
|
||||
if (primary.length == 1) {
|
||||
primary.forEach((val) => {
|
||||
filters.is_primary.push({ equals: val });
|
||||
});
|
||||
}
|
||||
|
||||
let authMethods;
|
||||
let totalItems = 0;
|
||||
let authMethodsExist = false;
|
||||
if (this.can.can('list model', scope, { collection: 'auth-methods' })) {
|
||||
// TODO: Remove storeToken option as this is a temporary fix for auth-methods.
|
||||
const options = { storeToken: false };
|
||||
authMethods = await this.store.query(
|
||||
'auth-method',
|
||||
{
|
||||
scope_id,
|
||||
query: { search, filters },
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
options,
|
||||
);
|
||||
totalItems = authMethods.meta?.totalItems;
|
||||
authMethodsExist = await this.getAuthMethodsExist(scope_id, totalItems);
|
||||
}
|
||||
|
||||
return { authMethods, authMethodsExist, totalItems };
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets authMethodsExist to true if there exists any auth methods.
|
||||
* @param {string} scope_id
|
||||
* @param {number} totalItems
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async getAuthMethodsExist(scope_id, totalItems) {
|
||||
if (totalItems > 0) {
|
||||
return true;
|
||||
}
|
||||
const options = { pushToStore: false, peekIndexedDB: true };
|
||||
const authMethod = await this.store.query(
|
||||
'auth-method',
|
||||
{
|
||||
scope_id,
|
||||
query: { filters: { scope_id: [{ equals: scope_id }] } },
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
},
|
||||
options,
|
||||
);
|
||||
return authMethod.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the scope to the controller context.
|
||||
* @param {Controller} controller
|
||||
|
||||
@@ -19,7 +19,6 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
|
||||
|
||||
@service router;
|
||||
@service store;
|
||||
@service resourceFilterStore;
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -64,20 +63,15 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route {
|
||||
query,
|
||||
});
|
||||
//query authmethods from all the scopes
|
||||
const authMethods = scopeIDs?.length
|
||||
? await this.resourceFilterStore.queryBy(
|
||||
'auth-method',
|
||||
{
|
||||
scope_id: scopeIDs,
|
||||
type: [TYPE_AUTH_METHOD_OIDC, TYPE_AUTH_METHOD_LDAP],
|
||||
},
|
||||
{ scope_id: 'global', recursive: true },
|
||||
)
|
||||
: await this.resourceFilterStore.queryBy(
|
||||
'auth-method',
|
||||
{ type: [TYPE_AUTH_METHOD_OIDC, TYPE_AUTH_METHOD_LDAP] },
|
||||
{ scope_id: 'global', recursive: true },
|
||||
);
|
||||
query.filters.type = [
|
||||
{ equals: TYPE_AUTH_METHOD_OIDC },
|
||||
{ equals: TYPE_AUTH_METHOD_LDAP },
|
||||
];
|
||||
const authMethods = await this.store.query('auth-method', {
|
||||
scope_id: 'global',
|
||||
recursive: true,
|
||||
query,
|
||||
});
|
||||
//query all the managed groups for each auth method id
|
||||
const managedGroups = await Promise.all(
|
||||
authMethods.map(({ id: auth_method_id }) =>
|
||||
|
||||
@@ -90,16 +90,21 @@ export default class ScopesScopeRolesRolePrincipalsRoute extends Route {
|
||||
async getManagedGroups(ids) {
|
||||
let managedGroups = [];
|
||||
if (ids?.length) {
|
||||
// TODO: Refactor to directly use store service
|
||||
// when types get added to indexedDB.
|
||||
|
||||
// Collect all oidc and ldap type auth methods.
|
||||
const authMethods = await this.resourceFilterStore.queryBy(
|
||||
'auth-method',
|
||||
{ type: [TYPE_AUTH_METHOD_OIDC, TYPE_AUTH_METHOD_LDAP] },
|
||||
{ scope_id: 'global', recursive: true },
|
||||
);
|
||||
|
||||
const authMethods = await this.store.query('auth-method', {
|
||||
scope_id: 'global',
|
||||
recursive: true,
|
||||
query: {
|
||||
filters: {
|
||||
type: [
|
||||
{ equals: TYPE_AUTH_METHOD_OIDC },
|
||||
{ equals: TYPE_AUTH_METHOD_LDAP },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
// TODO: Refactor to directly use store service
|
||||
// and add managed-group to indexedDB.
|
||||
const nestedManagedGroups = await Promise.all(
|
||||
authMethods.map(({ id: auth_method_id }) =>
|
||||
this.resourceFilterStore.queryBy(
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { all } from 'rsvp';
|
||||
import { loading } from 'ember-loading';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
@@ -33,8 +32,11 @@ export default class ScopesScopeUsersUserAddAccountsRoute extends Route {
|
||||
async model() {
|
||||
const user = this.modelFor('scopes.scope.users.user');
|
||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||
const authMethods = await this.store.query('auth-method', { scope_id });
|
||||
await all(
|
||||
const authMethods = await this.store.query('auth-method', {
|
||||
scope_id,
|
||||
query: { filters: { scope_id: [{ equals: scope_id }] } },
|
||||
});
|
||||
await Promise.all(
|
||||
authMethods.map(({ id: auth_method_id }) =>
|
||||
this.store.query('account', { auth_method_id }),
|
||||
),
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<page.actions>
|
||||
{{#if (can 'create model' this.scopeModel collection='auth-methods')}}
|
||||
<Hds::Dropdown as |dd|>
|
||||
<Hds::Dropdown data-test-new-dropdown as |dd|>
|
||||
<dd.ToggleButton @text={{t 'titles.new'}} @color='secondary' />
|
||||
<dd.Interactive
|
||||
@text={{t 'resources.auth-method.types.password'}}
|
||||
@@ -41,9 +41,8 @@
|
||||
{{/if}}
|
||||
</page.actions>
|
||||
|
||||
<page.body>
|
||||
|
||||
{{#if @model}}
|
||||
<page.body class='search-filtering'>
|
||||
{{#if @model.authMethodsExist}}
|
||||
{{#unless this.hasPrimaryAuthMethod}}
|
||||
<Hds::Alert
|
||||
class='auth-methods-list-alert'
|
||||
@@ -61,172 +60,154 @@
|
||||
</A.Description>
|
||||
</Hds::Alert>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{#if
|
||||
(or
|
||||
@model
|
||||
(has-resource-filter-selections 'scopes.scope.auth-methods' 'type')
|
||||
)
|
||||
}}
|
||||
<Rose::Toolbar>
|
||||
<Rose::Form as |form|>
|
||||
{{#let
|
||||
(resource-filter 'scopes.scope.auth-methods' 'type')
|
||||
as |filter|
|
||||
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@value={{this.search}}
|
||||
@type='search'
|
||||
placeholder={{t 'actions.search'}}
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' this.handleSearchInput}}
|
||||
/>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
name='type'
|
||||
@toggleText={{t 'form.type.label'}}
|
||||
@itemOptions={{this.authMethodTypeOptions}}
|
||||
@checkedItems={{this.types}}
|
||||
@applyFilter={{fn this.applyFilter 'types'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
name='primary'
|
||||
@toggleText={{t 'resources.auth-method.titles.primary'}}
|
||||
@itemOptions={{this.primaryOptions}}
|
||||
@checkedItems={{this.primary}}
|
||||
@applyFilter={{fn this.applyFilter 'primary'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
</Hds::SegmentedGroup>
|
||||
<FilterTags @filters={{this.filters}} />
|
||||
|
||||
{{#if @model.authMethods}}
|
||||
<Hds::Table
|
||||
class='auth-methods-list-table'
|
||||
@model={{@model.authMethods}}
|
||||
@columns={{array
|
||||
(hash label=(t 'form.name.label'))
|
||||
(hash label=(t 'form.type.label'))
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label='')
|
||||
(hash label=(t 'titles.actions'))
|
||||
}}
|
||||
<Rose::Dropdown
|
||||
@text={{t (concat 'form.' filter.name '.label')}}
|
||||
@count={{filter.selectedValue.length}}
|
||||
@ignoreClickInside={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
<form.checkboxGroup
|
||||
@name={{filter.name}}
|
||||
@items={{filter.allowedValues}}
|
||||
@selectedItems={{filter.selectedValue}}
|
||||
@onChange={{route-action 'filterBy' filter.name}}
|
||||
as |group|
|
||||
>
|
||||
<dropdown.item>
|
||||
<group.checkbox
|
||||
@label={{t
|
||||
(concat 'resources.auth-method.types.' group.item)
|
||||
}}
|
||||
value={{group.item}}
|
||||
/>
|
||||
</dropdown.item>
|
||||
</form.checkboxGroup>
|
||||
</Rose::Dropdown>
|
||||
{{/let}}
|
||||
|
||||
{{#if
|
||||
(has-resource-filter-selections 'scopes.scope.auth-methods' 'type')
|
||||
}}
|
||||
<Rose::Button
|
||||
@style='inline-link-action'
|
||||
@iconLeft='flight-icons/svg/x-16'
|
||||
{{on 'click' (route-action 'clearAllFilters')}}
|
||||
>{{t 'actions.clear-all-filters'}}</Rose::Button>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
</Rose::Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if @model}}
|
||||
<Hds::Table
|
||||
class='auth-methods-list-table'
|
||||
@model={{@model}}
|
||||
@columns={{array
|
||||
(hash label=(t 'form.name.label'))
|
||||
(hash label=(t 'form.type.label'))
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label='')
|
||||
(hash label=(t 'titles.actions'))
|
||||
}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
{{#if (can 'read auth-method' B.data)}}
|
||||
<LinkTo
|
||||
@route='scopes.scope.auth-methods.auth-method'
|
||||
@model={{B.data.id}}
|
||||
>
|
||||
{{B.data.displayName}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
{{B.data.displayName}}
|
||||
{{/if}}
|
||||
<p>{{B.data.description}}</p>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Badge
|
||||
@text={{t (concat 'resources.auth-method.types.' B.data.type)}}
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.id}}
|
||||
@color='secondary'
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.isPrimary}}
|
||||
<Hds::Badge @text={{t 'states.primary'}} @color='success' />
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if (can 'makePrimary auth-method' B.data)}}
|
||||
<Hds::Dropdown as |dd|>
|
||||
<dd.ToggleIcon
|
||||
@icon='more-horizontal'
|
||||
@text={{t 'actions.manage'}}
|
||||
@hasChevron={{false}}
|
||||
/>
|
||||
{{#unless B.data.isPrimary}}
|
||||
<dd.Interactive
|
||||
@text={{t 'resources.auth-method.actions.make-primary'}}
|
||||
{{on 'click' (route-action 'makePrimary' B.data)}}
|
||||
/>
|
||||
{{/unless}}
|
||||
{{#if B.data.isPrimary}}
|
||||
<dd.Interactive
|
||||
@text={{t
|
||||
'resources.auth-method.actions.remove-as-primary'
|
||||
}}
|
||||
{{on 'click' (route-action 'removeAsPrimary' B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Hds::Dropdown>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
|
||||
{{else if
|
||||
(has-resource-filter-selections 'scopes.scope.auth-methods' 'type')
|
||||
}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t 'titles.no-results'}} as |message|>
|
||||
<message.description>
|
||||
{{t 'descriptions.no-results'}}
|
||||
</message.description>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
{{else}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message
|
||||
@title={{t 'resources.auth-method.messages.welcome.title'}}
|
||||
as |message|
|
||||
@density='short'
|
||||
@valign='middle'
|
||||
>
|
||||
<message.description>
|
||||
{{#if (can 'list model' this.scopeModel collection='auth-methods')}}
|
||||
{{! can list (at least): show default welcome message}}
|
||||
{{t 'resources.auth-method.messages.welcome.description'}}
|
||||
{{else if
|
||||
(can 'create model' this.scopeModel collection='auth-methods')
|
||||
<:body as |B|>
|
||||
<B.Tr data-test-auth-methods-table-row={{B.data.id}}>
|
||||
<B.Td>
|
||||
{{#if (can 'read auth-method' B.data)}}
|
||||
<LinkTo
|
||||
@route='scopes.scope.auth-methods.auth-method'
|
||||
@model={{B.data.id}}
|
||||
>
|
||||
{{B.data.displayName}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
{{B.data.displayName}}
|
||||
{{/if}}
|
||||
<p>{{B.data.description}}</p>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Badge
|
||||
@text={{t
|
||||
(concat 'resources.auth-method.types.' B.data.type)
|
||||
}}
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{B.data.id}}
|
||||
@color='secondary'
|
||||
/>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.isPrimary}}
|
||||
<Hds::Badge @text={{t 'states.primary'}} @color='success' />
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if (can 'makePrimary auth-method' B.data)}}
|
||||
<Hds::Dropdown as |dd|>
|
||||
<dd.ToggleIcon
|
||||
@icon='more-horizontal'
|
||||
@text={{t 'actions.manage'}}
|
||||
@hasChevron={{false}}
|
||||
/>
|
||||
{{#unless B.data.isPrimary}}
|
||||
<dd.Interactive
|
||||
@text={{t 'resources.auth-method.actions.make-primary'}}
|
||||
data-test-make-primary-action
|
||||
{{on 'click' (route-action 'makePrimary' B.data)}}
|
||||
/>
|
||||
{{/unless}}
|
||||
{{#if B.data.isPrimary}}
|
||||
<dd.Interactive
|
||||
@text={{t
|
||||
'resources.auth-method.actions.remove-as-primary'
|
||||
}}
|
||||
{{on 'click' (route-action 'removeAsPrimary' B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Hds::Dropdown>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
<Rose::Pagination
|
||||
@totalItems={{@model.totalItems}}
|
||||
@currentPage={{this.page}}
|
||||
@currentPageSize={{this.pageSize}}
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::ApplicationState data-test-no-auth-method-results as |A|>
|
||||
<A.Header @title={{t 'titles.no-results-found'}} />
|
||||
<A.Body
|
||||
@text={{t
|
||||
(if
|
||||
this.search
|
||||
'descriptions.no-search-results'
|
||||
'descriptions.no-filter-results'
|
||||
)
|
||||
query=this.search
|
||||
resource=(t 'resources.auth-method.title_plural')
|
||||
}}
|
||||
{{! can create (only): show create-but-not-list welcome message}}
|
||||
{{t
|
||||
/>
|
||||
</Hds::ApplicationState>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<Hds::ApplicationState as |A|>
|
||||
<A.Header @title={{t 'resources.auth-method.messages.welcome.title'}} />
|
||||
<A.Body
|
||||
{{! can list (at least): show default welcome message}}
|
||||
{{! can create (only): show create-but-not-list welcome message}}
|
||||
{{! can neither list nor create: show neither-list-nor-create welcome message}}
|
||||
@text={{t
|
||||
(if
|
||||
(can 'list model' this.scopeModel collection='auth-methods')
|
||||
'resources.auth-method.description'
|
||||
(if
|
||||
(can 'create model' this.scopeModel collection='auth-methods')
|
||||
'descriptions.create-but-not-list'
|
||||
resource=(t 'resources.auth-method.title_plural')
|
||||
}}
|
||||
{{else}}
|
||||
{{! can neither list nor create }}
|
||||
{{t
|
||||
'descriptions.neither-list-nor-create'
|
||||
resource=(t 'resources.auth-method.title_plural')
|
||||
}}
|
||||
{{/if}}
|
||||
</message.description>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
)
|
||||
)
|
||||
resource=(t 'resources.auth-method.title_plural')
|
||||
}}
|
||||
/>
|
||||
</Hds::ApplicationState>
|
||||
{{/if}}
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
@@ -25,7 +25,8 @@ module('Acceptance | auth-methods | create', function (hooks) {
|
||||
'tbody .hds-table__tr:nth-child(1) .hds-table__td:last-child .hds-dropdown-toggle-icon';
|
||||
const DROPDOWN_SELECTOR_OPTION =
|
||||
'.hds-dropdown__content .hds-dropdown-list-item [type=button]';
|
||||
const NEW_DROPDOWN_SELECTOR = '.hds-dropdown-toggle-button';
|
||||
const NEW_DROPDOWN_SELECTOR =
|
||||
'[data-test-new-dropdown] .hds-dropdown-toggle-button';
|
||||
const SAVE_BTN_SELECTOR = '.rose-form-actions [type="submit"]';
|
||||
const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]';
|
||||
const NAME_INPUT_SELECTOR = '[name="name"]';
|
||||
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import { Response } from 'miragejs';
|
||||
import {
|
||||
@@ -20,6 +21,7 @@ import { TYPE_AUTH_METHOD_LDAP } from 'api/models/auth-method';
|
||||
module('Acceptance | auth-methods | delete', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let featuresService;
|
||||
let getAuthMethodCount;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, find } from '@ember/test-helpers';
|
||||
import { visit, click, waitFor, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
@@ -14,31 +14,46 @@ import {
|
||||
//currentSession,
|
||||
//invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
TYPE_AUTH_METHOD_PASSWORD,
|
||||
TYPE_AUTH_METHOD_OIDC,
|
||||
} from 'api/models/auth-method';
|
||||
|
||||
module('Acceptance | auth-methods | list', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
const SEARCH_INPUT_SELECTOR = '.search-filtering [type="search"]';
|
||||
const NO_RESULTS_MSG_SELECTOR = '[data-test-no-auth-method-results]';
|
||||
const FILTER_DROPDOWN_SELECTOR = (name) =>
|
||||
`.search-filtering [name="${name}"] button`;
|
||||
const FILTER_APPLY_BUTTON_SELECTOR =
|
||||
'.search-filtering [data-test-dropdown-apply-button]';
|
||||
const AUTH_ACTIONS_SELECTOR = (id) =>
|
||||
`tbody [data-test-auth-methods-table-row="${id}"] .hds-table__td:last-child .hds-dropdown button`;
|
||||
const MAKE_PRIMARY_ACTION_SELECTOR = '[data-test-make-primary-action]';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
org: null,
|
||||
},
|
||||
orgScope: null,
|
||||
passwordAuthMethod: null,
|
||||
oidcAuthMethod: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
scopes: {
|
||||
org: null,
|
||||
},
|
||||
authMethods: null,
|
||||
globalScope: null,
|
||||
orgScope: null,
|
||||
authMethods: null,
|
||||
passwordAuthMethod: null,
|
||||
oidcAuthMethod: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
instances.orgScope = this.server.create(
|
||||
instances.scopes.org = this.server.create(
|
||||
'scope',
|
||||
{
|
||||
type: 'org',
|
||||
@@ -46,38 +61,123 @@ module('Acceptance | auth-methods | list', function (hooks) {
|
||||
},
|
||||
'withChildren',
|
||||
);
|
||||
instances.passwordAuthMethod = this.server.create('auth-method', {
|
||||
scope: instances.scopes.org,
|
||||
type: TYPE_AUTH_METHOD_PASSWORD,
|
||||
});
|
||||
instances.oidcAuthMethod = this.server.create('auth-method', {
|
||||
scope: instances.scopes.org,
|
||||
type: TYPE_AUTH_METHOD_OIDC,
|
||||
});
|
||||
urls.globalScope = `/scopes/global/scopes`;
|
||||
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
|
||||
urls.authMethods = `/scopes/${instances.scopes.org.id}/auth-methods`;
|
||||
urls.passwordAuthMethod = `${urls.authMethods}/${instances.passwordAuthMethod.id}`;
|
||||
urls.oidcAuthMethod = `${urls.authMethods}/${instances.oidcAuthMethod.id}`;
|
||||
|
||||
urls.orgScope = `/scopes/${instances.orgScope.id}`;
|
||||
urls.authMethods = `/scopes/${instances.orgScope.id}/auth-methods`;
|
||||
authenticateSession({});
|
||||
});
|
||||
|
||||
test('Users can navigate to auth methods with proper authorization', async function (assert) {
|
||||
await visit(urls.orgScope);
|
||||
assert.ok(
|
||||
instances.orgScope.authorized_collection_actions['auth-methods'].includes(
|
||||
'list',
|
||||
),
|
||||
test('users can navigate to auth methods with proper authorization', async function (assert) {
|
||||
await visit(urls.globalScope);
|
||||
|
||||
await click(`[href="${urls.orgScope}"]`);
|
||||
|
||||
assert.true(
|
||||
instances.scopes.org.authorized_collection_actions[
|
||||
'auth-methods'
|
||||
].includes('list'),
|
||||
);
|
||||
assert.ok(find(`[href="${urls.authMethods}"]`));
|
||||
assert.dom(`[href="${urls.authMethods}"]`).exists();
|
||||
});
|
||||
|
||||
test('Users cannot navigate to index without either list or create actions', async function (assert) {
|
||||
instances.orgScope.authorized_collection_actions['auth-methods'] = [];
|
||||
await visit(urls.orgScope);
|
||||
assert.notOk(
|
||||
instances.orgScope.authorized_collection_actions['auth-methods'].includes(
|
||||
'list',
|
||||
),
|
||||
test('users cannot navigate to index without either list or create actions', async function (assert) {
|
||||
instances.scopes.org.authorized_collection_actions['auth-methods'] = [];
|
||||
await visit(urls.globalScope);
|
||||
|
||||
await click(`[href="${urls.orgScope}"]`);
|
||||
|
||||
assert.false(
|
||||
instances.scopes.org.authorized_collection_actions[
|
||||
'auth-methods'
|
||||
].includes('list'),
|
||||
);
|
||||
assert.notOk(find(`[href="${urls.authMethods}"]`));
|
||||
assert.dom(`[href="${urls.authMethods}"]`).doesNotExist();
|
||||
});
|
||||
|
||||
test('User can navigate to index with only create action', async function (assert) {
|
||||
instances.orgScope.authorized_collection_actions['auth-methods'] = [
|
||||
test('users can navigate to index with only create action', async function (assert) {
|
||||
instances.scopes.org.authorized_collection_actions['auth-methods'] = [
|
||||
'create',
|
||||
];
|
||||
await visit(urls.globalScope);
|
||||
|
||||
await click(`[href="${urls.orgScope}"]`);
|
||||
|
||||
assert.dom(`[href="${urls.authMethods}"]`).exists();
|
||||
});
|
||||
|
||||
test('user can search for a specifc auth-method by id', async function (assert) {
|
||||
await visit(urls.orgScope);
|
||||
assert.ok(find(`[href="${urls.authMethods}"]`));
|
||||
|
||||
await click(`[href="${urls.authMethods}"]`);
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).exists();
|
||||
|
||||
await fillIn(SEARCH_INPUT_SELECTOR, instances.passwordAuthMethod.id);
|
||||
await waitFor(`[href="${urls.oidcAuthMethod}"]`, { count: 0 });
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).doesNotExist();
|
||||
});
|
||||
|
||||
test('user can search for auth-methods and get no results', async function (assert) {
|
||||
await visit(urls.orgScope);
|
||||
|
||||
await click(`[href="${urls.authMethods}"]`);
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).exists();
|
||||
|
||||
await fillIn(SEARCH_INPUT_SELECTOR, 'fake target that does not exist');
|
||||
await waitFor(NO_RESULTS_MSG_SELECTOR, { count: 1 });
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).doesNotExist();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).doesNotExist();
|
||||
assert.dom(NO_RESULTS_MSG_SELECTOR).includesText('No results found');
|
||||
});
|
||||
|
||||
test('user can filter for auth-methods by type', async function (assert) {
|
||||
await visit(urls.orgScope);
|
||||
|
||||
await click(`[href="${urls.authMethods}"]`);
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).exists();
|
||||
|
||||
await click(FILTER_DROPDOWN_SELECTOR('type'));
|
||||
await click(`input[value="${TYPE_AUTH_METHOD_PASSWORD}"]`);
|
||||
await click(FILTER_APPLY_BUTTON_SELECTOR);
|
||||
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).doesNotExist();
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
});
|
||||
|
||||
test('user can filter for auth-methods by primary', async function (assert) {
|
||||
await visit(urls.orgScope);
|
||||
|
||||
await click(`[href="${urls.authMethods}"]`);
|
||||
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).exists();
|
||||
|
||||
await click(AUTH_ACTIONS_SELECTOR(instances.passwordAuthMethod.id));
|
||||
await click(MAKE_PRIMARY_ACTION_SELECTOR);
|
||||
await click(FILTER_DROPDOWN_SELECTOR('primary'));
|
||||
await click(`input[value="true"]`);
|
||||
await click(FILTER_APPLY_BUTTON_SELECTOR);
|
||||
|
||||
assert.dom(`[href="${urls.oidcAuthMethod}"]`).doesNotExist();
|
||||
assert.dom(`[href="${urls.passwordAuthMethod}"]`).exists();
|
||||
});
|
||||
});
|
||||
@@ -21,16 +21,12 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
const AUTH_LINK_SELECTOR = 'main tbody .hds-table__td:nth-child(1) a';
|
||||
const AUTH_TYPE_SELECTOR =
|
||||
'tbody .hds-table__tr:nth-child(1) .hds-table__td:nth-child(2)';
|
||||
const AUTH_ACTIONS_SELECTOR =
|
||||
'tbody .hds-table__tr:nth-child(1) .hds-table__td:last-child .hds-dropdown';
|
||||
const LDAP_AUTH_LINK_SELECTOR = 'tbody .hds-table__tr:nth-child(2) a';
|
||||
const LDAP_AUTH_TYPE_SELECTOR =
|
||||
'tbody .hds-table__tr:nth-child(2) .hds-table__td:nth-child(2)';
|
||||
const LDAP_AUTH_ACTIONS_SELECTOR =
|
||||
'tbody .hds-table__tr:nth-child(2) .hds-table__td:last-child .hds-dropdown';
|
||||
const AUTH_LINK_SELECTOR = (id) =>
|
||||
`tbody [data-test-auth-methods-table-row="${id}"] a`;
|
||||
const AUTH_TYPE_SELECTOR = (id) =>
|
||||
`tbody [data-test-auth-methods-table-row="${id}"] .hds-table__td:nth-child(2)`;
|
||||
const AUTH_ACTIONS_SELECTOR = (id) =>
|
||||
`tbody [data-test-auth-methods-table-row="${id}"] .hds-table__td:last-child .hds-dropdown`;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
@@ -99,10 +95,14 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.orgAuthMethods}"]`);
|
||||
|
||||
assert.dom(AUTH_TYPE_SELECTOR).hasText('Password');
|
||||
assert.dom(AUTH_ACTIONS_SELECTOR).exists();
|
||||
assert
|
||||
.dom(AUTH_TYPE_SELECTOR(instances.passwordAuthMethodOrg.id))
|
||||
.hasText('Password');
|
||||
assert
|
||||
.dom(AUTH_ACTIONS_SELECTOR(instances.passwordAuthMethodOrg.id))
|
||||
.exists();
|
||||
|
||||
await click(AUTH_LINK_SELECTOR);
|
||||
await click(AUTH_LINK_SELECTOR(instances.passwordAuthMethodOrg.id));
|
||||
await a11yAudit();
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordAuthMethodOrg);
|
||||
@@ -113,10 +113,14 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.globalAuthMethods}"]`);
|
||||
|
||||
assert.dom(AUTH_TYPE_SELECTOR).hasText('OIDC');
|
||||
assert.dom(AUTH_ACTIONS_SELECTOR).exists();
|
||||
assert
|
||||
.dom(AUTH_TYPE_SELECTOR(instances.oidcAuthMethodGlobal.id))
|
||||
.hasText('OIDC');
|
||||
assert
|
||||
.dom(AUTH_ACTIONS_SELECTOR(instances.oidcAuthMethodGlobal.id))
|
||||
.exists();
|
||||
|
||||
await click(AUTH_LINK_SELECTOR);
|
||||
await click(AUTH_LINK_SELECTOR(instances.oidcAuthMethodGlobal.id));
|
||||
await a11yAudit();
|
||||
|
||||
assert.strictEqual(currentURL(), urls.oidcAuthMethodGlobal);
|
||||
@@ -131,7 +135,9 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.orgAuthMethods}"]`);
|
||||
|
||||
assert.dom(AUTH_LINK_SELECTOR).doesNotExist();
|
||||
assert
|
||||
.dom(AUTH_LINK_SELECTOR(instances.passwordAuthMethodOrg.id))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('cannot navigate to an auth method form without proper authorization in global scope', async function (assert) {
|
||||
@@ -143,7 +149,9 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.globalAuthMethods}"]`);
|
||||
|
||||
assert.dom(AUTH_LINK_SELECTOR).doesNotExist();
|
||||
assert
|
||||
.dom(AUTH_LINK_SELECTOR(instances.oidcAuthMethodGlobal.id))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('cannot navigate to an ldap auth method form in global scope', async function (assert) {
|
||||
@@ -155,9 +163,11 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.globalAuthMethods}"]`);
|
||||
|
||||
assert.dom(LDAP_AUTH_LINK_SELECTOR).doesNotExist();
|
||||
assert.dom(LDAP_AUTH_TYPE_SELECTOR).hasText('LDAP');
|
||||
assert.dom(LDAP_AUTH_ACTIONS_SELECTOR).doesNotExist();
|
||||
assert.dom(AUTH_LINK_SELECTOR(instances.ldapAuthMethod.id)).doesNotExist();
|
||||
assert.dom(AUTH_TYPE_SELECTOR(instances.ldapAuthMethod.id)).hasText('LDAP');
|
||||
assert
|
||||
.dom(AUTH_ACTIONS_SELECTOR(instances.ldapAuthMethod.id))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('cannot navigate to an ldap auth method form in org scope', async function (assert) {
|
||||
@@ -169,9 +179,11 @@ module('Acceptance | auth-methods | read', function (hooks) {
|
||||
|
||||
await click(`[href="${urls.orgAuthMethods}"]`);
|
||||
|
||||
assert.dom(LDAP_AUTH_LINK_SELECTOR).doesNotExist();
|
||||
assert.dom(LDAP_AUTH_TYPE_SELECTOR).hasText('LDAP');
|
||||
assert.dom(LDAP_AUTH_ACTIONS_SELECTOR).doesNotExist();
|
||||
assert.dom(AUTH_LINK_SELECTOR(instances.ldapAuthMethod.id)).doesNotExist();
|
||||
assert.dom(AUTH_TYPE_SELECTOR(instances.ldapAuthMethod.id)).hasText('LDAP');
|
||||
assert
|
||||
.dom(AUTH_ACTIONS_SELECTOR(instances.ldapAuthMethod.id))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('users can navigate to auth method and incorrect url autocorrects', async function (assert) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, click, fillIn, select, findAll } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import { Response } from 'miragejs';
|
||||
import {
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
module('Acceptance | auth-methods | update', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let featuresService;
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ module('Acceptance | authentication', function (hooks) {
|
||||
|
||||
let indexURL;
|
||||
let globalScope;
|
||||
let orgScope;
|
||||
let orgScope1;
|
||||
let orgScope2;
|
||||
let orgScopeID;
|
||||
let scopesURL;
|
||||
let orgScopeURL;
|
||||
@@ -70,7 +71,15 @@ module('Acceptance | authentication', function (hooks) {
|
||||
invalidateSession();
|
||||
indexURL = '/';
|
||||
globalScope = this.server.create('scope', { id: 'global' });
|
||||
orgScope = this.server.create(
|
||||
orgScope1 = this.server.create(
|
||||
'scope',
|
||||
{
|
||||
type: 'org',
|
||||
scope: { id: globalScope.id, type: globalScope.type },
|
||||
},
|
||||
'withChildren',
|
||||
);
|
||||
orgScope2 = this.server.create(
|
||||
'scope',
|
||||
{
|
||||
type: 'org',
|
||||
@@ -87,20 +96,20 @@ module('Acceptance | authentication', function (hooks) {
|
||||
},
|
||||
'withChildren',
|
||||
);
|
||||
scope = { id: orgScope.id, type: orgScope.type };
|
||||
scope = { id: orgScope1.id, type: orgScope1.type };
|
||||
globalAuthMethod = this.server.create('auth-method', {
|
||||
scope: globalScope,
|
||||
type: TYPE_AUTH_METHOD_PASSWORD,
|
||||
});
|
||||
authMethod = this.server.create('auth-method', {
|
||||
scope: orgScope,
|
||||
scope: orgScope1,
|
||||
type: TYPE_AUTH_METHOD_PASSWORD,
|
||||
});
|
||||
authMethodOIDC = this.server.create('auth-method', {
|
||||
scope: orgScope,
|
||||
scope: orgScope2,
|
||||
type: TYPE_AUTH_METHOD_OIDC,
|
||||
});
|
||||
orgScopeID = orgScope.id;
|
||||
orgScopeID = orgScope1.id;
|
||||
globalAuthMethodID = globalAuthMethod.id;
|
||||
authMethodID = authMethod.id;
|
||||
authMethodOIDCID = authMethodOIDC.id;
|
||||
@@ -110,7 +119,7 @@ module('Acceptance | authentication', function (hooks) {
|
||||
authenticateURL = `/scopes/${orgScopeID}/authenticate`;
|
||||
authMethodGlobalAuthenticateURL = `/scopes/global/authenticate/${globalAuthMethodID}`;
|
||||
authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`;
|
||||
authMethodOIDCAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodOIDCID}`;
|
||||
authMethodOIDCAuthenticateURL = `/scopes/${orgScope2.id}/authenticate/${authMethodOIDCID}`;
|
||||
changePasswordURL = `/account/change-password`;
|
||||
orgsURL = `/scopes/global/scopes`;
|
||||
orgEditURL = `/scopes/${orgScopeID}/edit`;
|
||||
@@ -383,6 +392,6 @@ module('Acceptance | authentication', function (hooks) {
|
||||
|
||||
await click('.rose-dropdown-trigger');
|
||||
|
||||
assert.dom('.rose-dropdown-content a').exists({ count: 2 });
|
||||
assert.dom('.rose-dropdown-content a').exists({ count: 3 });
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import { Response } from 'miragejs';
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
module('Acceptance | managed-groups | create', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let getManagedGroupCount;
|
||||
let featuresService;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, click } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
TYPE_AUTH_METHOD_OIDC,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
module('Acceptance | managed-groups | list', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let featuresService;
|
||||
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'admin/tests/helpers';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { waitUntil } from '@ember/test-helpers';
|
||||
import {
|
||||
TYPE_AUTH_METHOD_PASSWORD,
|
||||
TYPE_AUTH_METHOD_OIDC,
|
||||
TYPE_AUTH_METHOD_LDAP,
|
||||
} from 'api/models/auth-method';
|
||||
|
||||
module('Unit | Controller | scopes/scope/auth-methods/index', function (hooks) {
|
||||
setupTest(hooks);
|
||||
@@ -15,4 +21,56 @@ module('Unit | Controller | scopes/scope/auth-methods/index', function (hooks) {
|
||||
);
|
||||
assert.ok(controller);
|
||||
});
|
||||
|
||||
test('primaryOptions returns expected object', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/auth-methods/index',
|
||||
);
|
||||
assert.deepEqual(controller.primaryOptions, [
|
||||
{ id: 'true', name: 'Yes' },
|
||||
{ id: 'false', name: 'No' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('authMethodTypeOptions returns expected object', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/auth-methods/index',
|
||||
);
|
||||
assert.deepEqual(controller.authMethodTypeOptions, [
|
||||
{ id: TYPE_AUTH_METHOD_PASSWORD, name: 'Password' },
|
||||
{ id: TYPE_AUTH_METHOD_OIDC, name: 'OIDC' },
|
||||
{ id: TYPE_AUTH_METHOD_LDAP, name: 'LDAP' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('filters returns expected entries', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/auth-methods/index',
|
||||
);
|
||||
assert.ok(controller.filters.allFilters);
|
||||
assert.ok(controller.filters.selectedFilters);
|
||||
});
|
||||
|
||||
test('handleSearchInput action sets expected values correctly', async function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/auth-methods/index',
|
||||
);
|
||||
const searchValue = 'test';
|
||||
controller.handleSearchInput({ target: { value: searchValue } });
|
||||
await waitUntil(() => controller.search === searchValue);
|
||||
|
||||
assert.strictEqual(controller.page, 1);
|
||||
assert.strictEqual(controller.search, searchValue);
|
||||
});
|
||||
|
||||
test('applyFilter action sets expected values correctly', async function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/auth-methods/index',
|
||||
);
|
||||
const selectedItems = ['true'];
|
||||
controller.applyFilter('primary', selectedItems);
|
||||
|
||||
assert.strictEqual(controller.page, 1);
|
||||
assert.deepEqual(controller.primary, selectedItems);
|
||||
});
|
||||
});
|
||||
@@ -14,8 +14,8 @@ module('Unit | Controller | scopes/scope/targets/index', function (hooks) {
|
||||
test('availableSessionOptions returns expected object', function (assert) {
|
||||
let controller = this.owner.lookup('controller:scopes/scope/targets/index');
|
||||
assert.deepEqual(controller.availableSessionOptions, [
|
||||
{ id: 'yes', name: 'Yes' },
|
||||
{ id: 'no', name: 'No' },
|
||||
{ id: 'yes', name: 'Has active sessions' },
|
||||
{ id: 'no', name: 'No active sessions' },
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user