Merge pull request #557 from hashicorp/feature-oidc-state-change

feat: 🎸 add OIDC change state custom method
This commit is contained in:
Randall Morey authored and GitHub committed 2021-05-20 16:00:32 -04:00
commit 8886f14996
4 files changed
+167 -2

No files matched your search

+25
View File
@@ -44,4 +44,29 @@ export default class AuthMethodModel extends GeneratedAuthMethodModel {
get isOIDC() {
return this.type === 'oidc';
}
// =methods
/**
* Change the active and visibility state of an OIDC auth method
* given its ID.
* For OIDC auth methods only.
* @param {[string]} state
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
changeState(state, options = { adapterOptions: {} }) {
const defaultAdapterOptions = {
method: 'change-state',
state,
};
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions,
},
});
}
}
@@ -1,6 +1,63 @@
import ApplicationSerializer from './application';
export default class AuthMethodSerializer extends ApplicationSerializer {
// =methods
/**
* Delegates to a type-specific serialization, or default.
* @override
* @param {Snapshot} snapshot
* @return {object}
*/
serialize(snapshot) {
switch (snapshot.record.type) {
case 'oidc':
return this.serializeOIDC(...arguments);
default:
return this.serializeDefault(...arguments);
}
}
/**
* Default serialization omits `attributes`.
* @return {object}
*/
serializeDefault() {
let serialized = super.serialize(...arguments);
delete serialized.attributes;
return serialized;
}
/**
* If `adapterOptions.state` is set, the serialization should
* include **only state** and version. Normally, this is not serialized.
* @param {Snapshot} snapshot
* @return {object}
*/
serializeOIDC(snapshot) {
let serialized = super.serialize(...arguments);
const state = snapshot?.adapterOptions?.state;
if (state) {
serialized = this.serializeOIDCWithState(snapshot, state);
} else {
delete serialized.attributes.state;
}
return serialized;
}
/**
* Returns a payload containing only state and version.
* @param {Snapshot} snapshot
* @param {string} state
* @return {object}
*/
serializeOIDCWithState(snapshot, state) {
return {
version: snapshot.attr('version'),
attributes: { state },
};
}
/**
* Sorts the array in order of "is_primary" such that the primary method
* appears first. The "natural" order of auth methods places primary first.
@@ -1,8 +1,10 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
module('Unit | Model | auth method', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
@@ -10,4 +12,39 @@ module('Unit | Model | auth method', function (hooks) {
let model = store.createRecord('auth-method', {});
assert.ok(model);
});
test('it has a `changeState` method that targets a specific POST API endpoint and serialization', async function (assert) {
assert.expect(1);
this.server.post(
'/v1/auth-methods/123abc:change-state',
(schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
attributes: {
state: 'foobar',
},
version: 1,
});
return { id: '123abc' };
}
);
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'auth-method',
attributes: {
type: 'oidc',
state: 'foobar',
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope',
},
},
},
});
const model = store.peekRecord('auth-method', '123abc');
await model.changeState('foobar');
});
});
@@ -12,13 +12,59 @@ module('Unit | Serializer | auth method', function (hooks) {
assert.ok(serializer);
});
test('it serializes records', function (assert) {
test('it serializes password records', function (assert) {
assert.expect(2);
let store = this.owner.lookup('service:store');
let record = store.createRecord('auth-method', {});
let record = store.createRecord('auth-method', { type: 'password' });
let serializedRecord = record.serialize();
assert.ok(serializedRecord);
assert.notOk(
serializedRecord.attributes,
'Password should not have attributes'
);
});
test('it serializes OIDC records without state', function (assert) {
assert.expect(3);
let store = this.owner.lookup('service:store');
let record = store.createRecord('auth-method', {
type: 'oidc',
attributes: {
state: 'foo',
},
});
let serializedRecord = record.serialize();
assert.ok(serializedRecord);
assert.ok(serializedRecord.attributes, 'OIDC should have attributes');
assert.notOk(serializedRecord.attributes.state);
});
test('it serializes OIDC records with only state and version when `adapterOptions.state` is passed', function (assert) {
assert.expect(1);
const store = this.owner.lookup('service:store');
const serializer = store.serializerFor('auth-method');
const record = store.createRecord('auth-method', {
type: 'oidc',
attributes: {
state: 'foo',
},
version: 1,
});
const snapshot = record._createSnapshot();
snapshot.adapterOptions = {
state: 'bar',
};
const serializedRecord = serializer.serialize(snapshot);
assert.deepEqual(serializedRecord, {
attributes: {
state: 'bar',
},
version: 1,
});
});
test('it sorts primary first in normalizeArrayResponse', function (assert) {