feat: Add oidc status change dropdown to auth method actions

This commit is contained in:
Susmitha Girumala committed 2021-06-02 13:10:34 -04:00
1 parent bc10b00874
commit 44fa206619
9 files changed
+226 -4

No files matched your search

+3 -1
View File
@@ -3,7 +3,7 @@ import { fragment } from 'ember-data-model-fragments/attributes';
import { equal } from '@ember/object/computed';
/**
* Enum ptions per auth method type and field.
* Enum options per auth method type and field.
*/
export const options = {
oidc: {
@@ -27,6 +27,8 @@ export const options = {
};
export default class AuthMethodModel extends GeneratedAuthMethodModel {
STATES_ENUM = options.oidc.state;
// =attributes
/**
+27 -2
View File
@@ -1,6 +1,6 @@
import Ember from 'ember';
import config from '../config/environment';
import { authHandler, deauthHandler } from './route-handlers/auth';
import { deauthHandler } from './route-handlers/auth';
import { pickRandomStatusString } from './factories/session';
import { Response } from 'miragejs';
import initializeMockIPC from './scenarios/ipc';
@@ -71,6 +71,31 @@ export default function () {
}
return new Response(202);
});
this.post('/auth-methods/:idMethod', function (schema, request) {
const idMethod = request.params.idMethod;
const id = idMethod.split(':')[0];
const method = idMethod.split(':')[1];
if (method === 'authenticate') {
schema.authHandler.call(this, schema, request);
return;
}
const attrs = this.normalizedRequestAttrs();
const authMethods = schema.authMethods;
const authMethod = authMethods.find(id);
let updatedAttrs = {};
if (method === 'change-state') {
updatedAttrs = {
attributes: {
state: attrs.attributes.state,
},
};
}
return authMethod.update(updatedAttrs);
});
// Auth Method Accounts
this.get(
@@ -107,7 +132,7 @@ export default function () {
);
// Authenticate/deauthenticate routes
this.post('/auth-methods/:id_method', authHandler);
// this.post('/auth-methods/:id_method', authHandler);
this.post('/scopes/:id_method', deauthHandler);
// IAM : Users
+1 -1
View File
@@ -109,7 +109,7 @@ const commandHandlers = {
export function authHandler({ scopes, authMethods }, request) {
const payload = JSON.parse(request.requestBody);
const [, id] = request.params.id_method.match(/(?<id>.[^:]*)/);
const [, id] = request.params.idMethod.match(/(?<id>.[^:]*)/);
const { command } = payload;
const authMethod = authMethods.find(id);
const scope = scopes.find(authMethod.scopeId);
+6
View File
@@ -1,3 +1,5 @@
import { authHandler } from '../route-handlers/auth';
export default function (server) {
// Scope resources
@@ -50,4 +52,8 @@ export default function (server) {
server.createList('target', 2, { scope }, 'withRandomHostSets');
server.createList('session', 4, { scope }, 'withAssociations');
});
// Authenticate route handler
// TODO: Better way to pass-in auth handler
server.schema.authHandler = authHandler;
}
@@ -45,6 +45,7 @@ auth-method:
delete: Delete Auth Method
make-primary: Make Primary
remove-as-primary: Remove as Primary
change-state: State
notifications:
make-primary-success: Auth method successfully elected to primary.
remove-as-primary-success: Auth method successfully removed as primary.
@@ -8,3 +8,9 @@ connection:
success:
description: "Local proxy address ({protocol}): {address}:{port}"
primary: Primary
auth-method:
oidc:
states:
inactive: Inactive
active-private: Active Private
active-public: Active Public
@@ -1,4 +1,6 @@
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route {
// =methods
@@ -36,4 +38,18 @@ export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route {
outlet: 'actions',
});
}
// =actions
/**
* Update state of OIDC auth method
* @param {string} state - "inactive", "active-private", "active-public"
*/
@action
@notifyError(({ message }) => message)
@notifySuccess('notifications.save-success')
async changeState(state) {
const model = this.modelFor('scopes.scope.auth-methods.auth-method');
await model.changeState(state);
}
}
@@ -1,3 +1,35 @@
{{#if @model.isOIDC}}
<Rose::Dropdown
@text='{{
concat (t 'resources.auth-method.actions.change-state')
' : '
(t (concat 'states.auth-method.oidc.states.' @model.attributes.state))
}}'
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.section @title={{t 'resources.auth-method.actions.change-state'}}>
<Rose::Form as |form|>
<form.radioGroup
@name='theme'
@selectedValue={{@model.attributes.state}}
@changed={{route-action 'changeState'}}
as |radioGroup|
>
{{#each @model.STATES_ENUM as |state|}}
<dropdown.item>
<radioGroup.radio
@label={{t (concat 'states.auth-method.oidc.states.' state)}}
@value={{state}}
/>
</dropdown.item>
{{/each}}
</form.radioGroup>
</Rose::Form>
</dropdown.section>
</Rose::Dropdown>
{{/if}}
<Rose::Dropdown
@text='{{t 'actions.manage'}}'
@dropdownRight={{true}}
@@ -0,0 +1,134 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
click,
// fillIn,
find,
// findAll,
} 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 | auth methods | oidc', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
const instances = {
scopes: {
global: null,
org: null,
},
authMethod: null,
};
const urls = {
orgScope: null,
authMethods: null,
authMethod: null,
};
hooks.beforeEach(function () {
authenticateSession({});
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.org,
type: 'oidc',
});
urls.orgScope = `/scopes/${instances.scopes.org.id}`;
urls.authMethods = `${urls.orgScope}/auth-methods`;
urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`;
});
test('visiting oidc auth method', async function (assert) {
assert.expect(1);
await visit(urls.authMethod);
await a11yAudit();
assert.equal(currentURL(), urls.authMethod);
});
test('can view oidc state', async function(assert) {
assert.expect(1);
await visit(urls.authMethod);
await click('.rose-layout-page-actions .rose-dropdown-trigger');
assert.equal(
find('.rose-dropdown[open] input[aria-checked=true]').value,
instances.authMethod.attributes.state
);
});
test('can update oidc state', async function(assert) {
assert.expect(1);
const updateValue = 'inactive';
await visit(urls.authMethod);
await click('.rose-layout-page-actions .rose-dropdown-trigger');
await click(`.rose-dropdown[open] input[value="${updateValue}"]`);
const authMethod = this.server.db.authMethods.find(instances.authMethod.id);
assert.equal(authMethod.attributes.state, updateValue);
});
test('can update oidc state to active-private', async function(assert) {
assert.expect(1);
const updateValue = 'active-private';
await visit(urls.authMethod);
await click('.rose-layout-page-actions .rose-dropdown-trigger');
await click(`.rose-dropdown[open] input[value="${updateValue}"]`);
const authMethod = this.server.db.authMethods.find(instances.authMethod.id);
assert.equal(authMethod.attributes.state, updateValue);
});
test('can update oidc state to active-public', async function(assert) {
assert.expect(1);
// Update default 'active-public' state to inactive
instances.authMethod.attributes.state = 'inactive';
const updateValue = 'active-public';
await visit(urls.authMethod);
await click('.rose-layout-page-actions .rose-dropdown-trigger');
await click(`.rose-dropdown[open] input[value="${updateValue}"]`);
const authMethod = this.server.db.authMethods.find(instances.authMethod.id);
assert.equal(authMethod.attributes.state, updateValue);
});
test('errors are displayed when state update fails', async function (assert) {
assert.expect(2);
const newState = 'inactive';
await visit(urls.authMethod);
await click('.rose-layout-page-actions .rose-dropdown-trigger');
// Load page before mocking update
this.server.post('/auth-methods/:idMethod', () => {
return new Response(
400,
{},
{
status: 400,
code: 'error',
message: 'Sorry!',
}
);
});
await click(`.rose-dropdown[open] input[value="${newState}"]`);
const authMethod = this.server.db.authMethods.find(instances.authMethod.id);
assert.notEqual(
newState,
authMethod.attributes.state,
'Auth method state is not be updated.'
);
assert.ok(
find('[role="alert"]').textContent.trim(),
'Sorry!',
'Displays error message.'
);
});
});