diff --git a/addons/api/addon/models/managed-group.js b/addons/api/addon/models/managed-group.js
index 426807de2..5843bb0e5 100644
--- a/addons/api/addon/models/managed-group.js
+++ b/addons/api/addon/models/managed-group.js
@@ -5,6 +5,14 @@ import { A } from '@ember/array';
export default class ManagedGroupModel extends GeneratedManagedGroupModel {
// =attributes
+
+ // =error attributes
+ // These attributes exist solely to capture errors on nested fields.
+ // See the application adapter's error normalization method for
+ // more information.
+
+ @attr('string', { readOnly: true }) attributes_filter;
+
/**
* Members is read-only under normal circumstances. But members can
* be persisted via calls to `addMembers()` or `removeMembers()`.
diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js
index 89e2a82b5..9036f6653 100644
--- a/addons/api/mirage/config.js
+++ b/addons/api/mirage/config.js
@@ -521,6 +521,9 @@ export default function () {
return managedGroups.where({ authMethodId });
}
);
+ this.get('/managed-groups/:id');
+ this.del('/managed-groups/:id');
+ this.post('/managed-groups');
/* Uncomment the following line and the Response import above
* Then change the response code to simulate error responses.
diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js
index 051ea096f..755154b09 100644
--- a/addons/api/mirage/factories/auth-method.js
+++ b/addons/api/mirage/factories/auth-method.js
@@ -34,7 +34,9 @@ export default factory.extend({
user.update({ accountIds: [id] });
});
- server.createList('managed-group', 2, { scope, authMethod });
+ if (type === 'oidc') {
+ server.createList('managed-group', 2, { scope, authMethod });
+ }
},
}),
});
diff --git a/addons/api/mirage/factories/managed-group.js b/addons/api/mirage/factories/managed-group.js
index c65e62334..9460f847f 100644
--- a/addons/api/mirage/factories/managed-group.js
+++ b/addons/api/mirage/factories/managed-group.js
@@ -1,8 +1,12 @@
import factory from '../generated/factories/managed-group';
+import { random } from 'faker';
import permissions from '../helpers/permissions';
+const types = ['oidc'];
+
export default factory.extend({
id: (i) => `managed-groups-id-${i}`,
+ type: (i) => types[i % types.length],
authorized_actions: () =>
permissions.authorizedActionsFor('managed-group') || [
@@ -11,4 +15,13 @@ export default factory.extend({
'update',
'delete',
],
+
+ attributes() {
+ switch (this.type) {
+ case 'oidc':
+ return {
+ filter: random.words(),
+ };
+ }
+ },
});
diff --git a/addons/api/mirage/generated/factories/managed-group.js b/addons/api/mirage/generated/factories/managed-group.js
index 17bb7d6c6..a041c0f50 100644
--- a/addons/api/mirage/generated/factories/managed-group.js
+++ b/addons/api/mirage/generated/factories/managed-group.js
@@ -11,15 +11,4 @@ export default Factory.extend({
created_time: () => date.recent(),
updated_time: () => date.recent(),
version: () => datatype.number(),
- type: 'oidc',
- attributes: function () {
- switch (this.type) {
- case 'oidc':
- return {
- filter: random.words(),
- };
- default:
- return;
- }
- },
});
diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js
index 9291f8a3e..965234b5d 100644
--- a/addons/api/mirage/scenarios/default.js
+++ b/addons/api/mirage/scenarios/default.js
@@ -22,7 +22,7 @@ export default function (server) {
// Auth
const globalAuthMethods = server.createList(
'auth-method',
- 1,
+ 4,
{ scope: globalScope },
'withAccountsAndUsersAndManagedGroups'
);
diff --git a/addons/api/mirage/serializers/managed-groups.js b/addons/api/mirage/serializers/managed-group.js
similarity index 100%
rename from addons/api/mirage/serializers/managed-groups.js
rename to addons/api/mirage/serializers/managed-group.js
diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml
index cb7b69832..05105fbac 100644
--- a/addons/core/translations/form/en-us.yaml
+++ b/addons/core/translations/form/en-us.yaml
@@ -115,3 +115,6 @@ value:
callback_url:
label: Callback URL
help: The URL to which the third-party provider sends the authentication response. Paste this into the provider configuration.
+filter:
+ label: Filter
+ help: A boolean expression defining a filter run against the provided information.
diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml
index d7d07aee2..7b9d1e4fe 100644
--- a/addons/core/translations/resources/en-us.yaml
+++ b/addons/core/translations/resources/en-us.yaml
@@ -354,3 +354,7 @@ scope:
managed-group:
title: Managed Group
title_plural: Managed Groups
+ description: A managed group represents a collection of accounts.
+ actions:
+ delete: Delete Managed Group
+ create: Create Managed Group
diff --git a/ui/admin/app/components/form/managed-group/index.hbs b/ui/admin/app/components/form/managed-group/index.hbs
new file mode 100644
index 000000000..7918dd146
--- /dev/null
+++ b/ui/admin/app/components/form/managed-group/index.hbs
@@ -0,0 +1,79 @@
+
+
+
+
+ {{#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}}
+
+
+
+ {{#if @model.errors.filter}}
+
+ {{#each @model.errors.filter as |error|}}
+ {{error.message}}
+ {{/each}}
+
+ {{/if}}
+
+
+
+
\ No newline at end of file
diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js
index 17519a2a5..edc70b62d 100644
--- a/ui/admin/app/router.js
+++ b/ui/admin/app/router.js
@@ -55,7 +55,14 @@ Router.map(function () {
});
this.route('new');
});
- this.route('managed-groups', function () {});
+ this.route('managed-groups', function () {
+ this.route(
+ 'managed-group',
+ { path: ':managed_group_id' },
+ function () {}
+ );
+ this.route('new');
+ });
});
this.route('new');
});
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js
index bde134d3f..cef4017d2 100644
--- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js
+++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js
@@ -1,6 +1,19 @@
import Route from '@ember/routing/route';
+import { action } from '@ember/object';
+import { inject as service } from '@ember/service';
+import loading from 'ember-loading/decorator';
+import { confirm } from 'core/decorators/confirm';
+import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends Route {
+ // =services
+
+ @service intl;
+ @service notify;
+ @service can;
+
+ //=methods
+
/**
*
* @returns {Promise{[ManagedGroupsModel]}}
@@ -10,4 +23,53 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends
const { id: auth_method_id } = authMethod;
return this.store.query('managed-group', { auth_method_id });
}
+
+ // =actions
+ /**
+ * Rollback changes on a managed group.
+ * @param {ManagedGroupModel} managedGroup
+ */
+ @action
+ cancel(managedGroup) {
+ const { isNew } = managedGroup;
+ managedGroup.rollbackAttributes();
+ if (isNew) {
+ this.transitionTo('scopes.scope.auth-methods.auth-method.managed-groups');
+ }
+ }
+
+ /**
+ * Save a managed group in current scope.
+ * @param {ManagedGroupModel} managedGroup
+ */
+ @action
+ @loading
+ @notifyError(({ message }) => message)
+ @notifySuccess(({ isNew }) =>
+ isNew ? 'notifications.create-success' : 'notifications.save-success'
+ )
+ async save(managedGroup) {
+ await managedGroup.save();
+ await this.transitionTo(
+ 'scopes.scope.auth-methods.auth-method.managed-groups'
+ );
+ this.refresh();
+ }
+
+ /**
+ * Delete a managed group in current scope and redirect to index
+ * @param {ManagedGroupModel} managed group
+ */
+ @action
+ @loading
+ @confirm('questions.delete-confirm')
+ @notifyError(({ message }) => message, { catch: true })
+ @notifySuccess('notifications.delete-success')
+ async deleteManagedGroup(managedGroup) {
+ await managedGroup.destroyRecord();
+ await this.replaceWith(
+ 'scopes.scope.auth-methods.auth-method.managed-groups'
+ );
+ this.refresh();
+ }
}
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/index.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/index.js
index 24639a124..7cdc46a89 100644
--- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/index.js
+++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/index.js
@@ -1,3 +1,9 @@
import Route from '@ember/routing/route';
-export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsIndexRoute extends Route {}
+export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsIndexRoute extends Route {
+ setupController(controller) {
+ const authMethod = this.modelFor('scopes.scope.auth-methods.auth-method');
+ super.setupController(...arguments);
+ controller.setProperties({ authMethod });
+ }
+}
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js
new file mode 100644
index 000000000..816b537da
--- /dev/null
+++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js
@@ -0,0 +1,49 @@
+import Route from '@ember/routing/route';
+
+export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupRoute extends Route {
+ // =methods
+
+ /**
+ * Load managed group by ID.
+ * @param {object} params
+ * @param {string} params.managed_group_id
+ * @returns {ManagedGroupModel}
+ */
+ model({ managed_group_id }) {
+ return this.store.findRecord('managed-group', managed_group_id, {
+ reload: true,
+ });
+ }
+
+ /**
+ * Render the route-set specific page sections
+ * @override
+ */
+ renderTemplate() {
+ super.renderTemplate(...arguments);
+
+ this.render(
+ 'scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-header',
+ {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'header',
+ }
+ );
+
+ this.render(
+ 'scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions',
+ {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'actions',
+ }
+ );
+
+ this.render(
+ 'scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-navigation',
+ {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'navigation',
+ }
+ );
+ }
+}
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.js
new file mode 100644
index 000000000..0dbea955a
--- /dev/null
+++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.js
@@ -0,0 +1,3 @@
+import Route from '@ember/routing/route';
+
+export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupIndexRoute extends Route {}
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/new.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/new.js
new file mode 100644
index 000000000..ca2b1473c
--- /dev/null
+++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups/new.js
@@ -0,0 +1,36 @@
+import Route from '@ember/routing/route';
+
+export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsNewRoute extends Route {
+ // =methods
+ model() {
+ const { id: auth_method_id, type } = this.modelFor(
+ 'scopes.scope.auth-methods.auth-method'
+ );
+ return this.store.createRecord('managed-group', {
+ type,
+ auth_method_id,
+ });
+ }
+
+ renderTemplate() {
+ super.renderTemplate(...arguments);
+
+ this.render(
+ 'scopes/scope/auth-methods/auth-method/managed-groups/new/-header',
+ {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'header',
+ }
+ );
+
+ this.render('_empty', {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'actions',
+ });
+
+ this.render('_empty', {
+ into: 'scopes/scope/auth-methods/auth-method',
+ outlet: 'navigation',
+ });
+ }
+}
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs
index a33cfdb42..865bdfeec 100644
--- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs
@@ -67,6 +67,18 @@
{{/if}}
+ {{#if (feature-flag 'managed-groups')}}
+ {{! TODO: Add capabilities }}
+ {{#if @model.isOIDC}}
+
+ {{t 'resources.managed-group.actions.create'}}
+
+ {{/if}}
+ {{/if}}
+
{{#if (can 'delete model' @model)}}
- {{t 'resources.managed-group.title_plural'}}
-
+ {{#if @model.isOIDC}}
+
+ {{t 'resources.managed-group.title_plural'}}
+
+ {{/if}}
{{/if}}
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs
index 959c50afa..8165f109d 100644
--- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs
@@ -12,7 +12,18 @@
{{#each @model as |managed-group|}}
- {{managed-group.name}}
+
+ {{#if (can 'read model' managed-group)}}
+
+ {{managed-group.displayName}}
+
+ {{else}}
+ {{managed-group.displayName}}
+ {{/if}}
+ {{managed-group.description}}{{managed-group.type}}
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.hbs
new file mode 100644
index 000000000..a6696c3b6
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.hbs
@@ -0,0 +1,2 @@
+{{page-title 'ManagedGroup'}}
+{{outlet}}
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs
new file mode 100644
index 000000000..98a51d476
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs
@@ -0,0 +1,15 @@
+{{#if (can 'delete model' @model)}}
+
+
+ {{t 'resources.managed-group.actions.delete'}}
+
+
+{{/if}}
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-header.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-header.hbs
new file mode 100644
index 000000000..1e7ef1124
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-header.hbs
@@ -0,0 +1,12 @@
+
+ {{t 'resources.managed-group.title'}}
+
+
+
{{t 'resources.managed-group.description'}}
+
+ {{@model.id}}
+
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-navigation.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-navigation.hbs
new file mode 100644
index 000000000..3a2ce54b7
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-navigation.hbs
@@ -0,0 +1,7 @@
+
+
+ {{t 'titles.details'}}
+
+
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs
new file mode 100644
index 000000000..19ab29fcb
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new.hbs
new file mode 100644
index 000000000..bcaa893ac
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new.hbs
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new/-header.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new/-header.hbs
new file mode 100644
index 000000000..b6e89205e
--- /dev/null
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/new/-header.hbs
@@ -0,0 +1,5 @@
+
+ {{t 'titles.new'}}
+ {{! TODO: change the icon link }}
+
+
\ No newline at end of file
diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js
index 7e4c3ca78..613c6a3e8 100644
--- a/ui/admin/config/environment.js
+++ b/ui/admin/config/environment.js
@@ -48,6 +48,7 @@ module.exports = function (environment) {
account: '/accounts',
'account.new': '/accounts/new',
'auth-method': '/auth-methods',
+ 'managed-groups': '/managed-groups',
group: '/groups',
'group.add-members': '/groups/add-members',
'host-catalog': '/host-catalogs',
diff --git a/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js
new file mode 100644
index 000000000..f323d5e2e
--- /dev/null
+++ b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js
@@ -0,0 +1,16 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module(
+ 'Unit | Route | scopes/scope/auth-methods/auth-method/managed-groups/managed-group',
+ function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let route = this.owner.lookup(
+ 'route:scopes/scope/auth-methods/auth-method/managed-groups/managed-group'
+ );
+ assert.ok(route);
+ });
+ }
+);
diff --git a/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index-test.js b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index-test.js
new file mode 100644
index 000000000..47aadd91a
--- /dev/null
+++ b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index-test.js
@@ -0,0 +1,16 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module(
+ 'Unit | Route | scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index',
+ function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let route = this.owner.lookup(
+ 'route:scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index'
+ );
+ assert.ok(route);
+ });
+ }
+);
diff --git a/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js
new file mode 100644
index 000000000..3ea26ad63
--- /dev/null
+++ b/ui/admin/tests/unit/routes/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js
@@ -0,0 +1,16 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module(
+ 'Unit | Route | scopes/scope/auth-methods/auth-method/managed-groups/new',
+ function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let route = this.owner.lookup(
+ 'route:scopes/scope/auth-methods/auth-method/managed-groups/new'
+ );
+ assert.ok(route);
+ });
+ }
+);