diff --git a/addons/api/addon/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js b/addons/api/addon/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js new file mode 100644 index 000000000..59387536f --- /dev/null +++ b/addons/api/addon/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js @@ -0,0 +1,15 @@ +// app/initializers/fragment-serializer.js +import FragmentAuthMethodAttributesAccountClaimMapSerializer from '../serializers/fragment-auth-method-attributes-account-claim-map'; + +export function initialize(application) { + application.register( + 'serializer:fragment-auth-method-attributes-account-claim-map', + FragmentAuthMethodAttributesAccountClaimMapSerializer + ); +} + +// Fragment serializers must be manually registered +export default { + name: 'fragment-auth-method-attributes-account-claim-map', + initialize: initialize, +}; diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index d213e5c51..699f5137e 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -2,20 +2,29 @@ import GeneratedAuthMethodModel from '../generated/models/auth-method'; import { fragment } from 'ember-data-model-fragments/attributes'; import { equal } from '@ember/object/computed'; -export const signingAlgorithms = [ - 'RS256', - 'RS384', - 'RS512', - 'ES256', - 'ES384', - 'ES512', - 'PS256', - 'PS384', - 'PS512', - 'EdDSA', -]; - -export const states = ['inactive', 'active-private', 'active-public']; +/** + * Enum ptions per auth method type and field. + */ +export const options = { + oidc: { + signing_algorithm: [ + 'RS256', + 'RS384', + 'RS512', + 'ES256', + 'ES384', + 'ES512', + 'PS256', + 'PS384', + 'PS512', + 'EdDSA', + ], + state: ['inactive', 'active-private', 'active-public'], + account_claim_maps: { + to: ['sub', 'name', 'email'], + }, + }, +}; export default class AuthMethodModel extends GeneratedAuthMethodModel { // =attributes diff --git a/addons/api/addon/models/fragment-auth-method-attributes-account-claim-map.js b/addons/api/addon/models/fragment-auth-method-attributes-account-claim-map.js new file mode 100644 index 000000000..c3e2aec21 --- /dev/null +++ b/addons/api/addon/models/fragment-auth-method-attributes-account-claim-map.js @@ -0,0 +1,9 @@ +import Fragment from 'ember-data-model-fragments/fragment'; +import { attr } from '@ember-data/model'; + +export default class FragmentAuthMethodAttributesAccountClaimMapModel extends Fragment { + // =attributes (OIDC) + + @attr('string') from; + @attr('string') to; +} diff --git a/addons/api/addon/models/fragment-auth-method-attributes.js b/addons/api/addon/models/fragment-auth-method-attributes.js index a8ba64af5..b023401f2 100644 --- a/addons/api/addon/models/fragment-auth-method-attributes.js +++ b/addons/api/addon/models/fragment-auth-method-attributes.js @@ -1,5 +1,6 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { attr } from '@ember-data/model'; +import { fragmentArray } from 'ember-data-model-fragments/attributes'; export default class FragmentAuthMethodAttributesModel extends Fragment { // =attributes (OIDC) @@ -14,4 +15,9 @@ export default class FragmentAuthMethodAttributesModel extends Fragment { @attr('string') callback_url; @attr('boolean') disable_discovered_config_validation; @attr('boolean') dry_run; + + @fragmentArray('fragment-auth-method-attributes-account-claim-map', { + emptyArrayIfMissing: true, + }) + account_claim_maps; } diff --git a/addons/api/addon/serializers/auth-method.js b/addons/api/addon/serializers/auth-method.js index e40285ce9..6895cf512 100644 --- a/addons/api/addon/serializers/auth-method.js +++ b/addons/api/addon/serializers/auth-method.js @@ -1,4 +1,5 @@ import ApplicationSerializer from './application'; +import { copy } from 'ember-copy'; export default class AuthMethodSerializer extends ApplicationSerializer { // =methods @@ -80,10 +81,19 @@ export default class AuthMethodSerializer extends ApplicationSerializer { * * TODO: generalize this so that all zero values are reified. * @override - * @return {object} + * @see FragmentScope + * @param {Model} typeClass + * @param {Object} hash + * @return {Object} */ - normalize() { - const normalized = super.normalize(...arguments); + normalize(typeClass, hash, ...rest) { + let normalizedHash = copy(hash, true); + // switch (normalizedHash.type) { + // case 'oidc': + // normalizedHash = this.normalizeOIDC(normalizedHash); + // break; + // } + const normalized = super.normalize(typeClass, normalizedHash, ...rest); if (!normalized.data.attributes.is_primary) { normalized.data.attributes.is_primary = false; } diff --git a/addons/api/addon/serializers/fragment-auth-method-attributes-account-claim-map.js b/addons/api/addon/serializers/fragment-auth-method-attributes-account-claim-map.js new file mode 100644 index 000000000..5c5b4789e --- /dev/null +++ b/addons/api/addon/serializers/fragment-auth-method-attributes-account-claim-map.js @@ -0,0 +1,30 @@ +import JSONSerializer from '@ember-data/serializer/json'; + +export default class FragmentHostSetSerializer extends JSONSerializer { + // =methods + + /** + * Serializes an account claim to string. + * @param {Snapshot} snapshot + * @return {string} + */ + serialize(snapshot) { + const from = snapshot.attr('from'); + const to = snapshot.attr('to'); + return `${from}=${to}`; + } + + /** + * Normalizes account claim strings into JSON API objects. + * @param {Model} typeClass + * @param {string} value + * @return {object} + */ + normalize(typeClass, value) { + const normalizedHash = { + from: value.split('=')[0], + to: value.split('=')[1], + }; + return super.normalize(typeClass, normalizedHash); + } +} diff --git a/addons/api/app/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js b/addons/api/app/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js new file mode 100644 index 000000000..844980fbc --- /dev/null +++ b/addons/api/app/initializers/fragment-auth-method-attributes-account-claim-map-serializer.js @@ -0,0 +1,4 @@ +export { + default, + initialize, +} from 'api/initializers/fragment-auth-method-attributes-account-claim-map-serializer'; diff --git a/addons/api/app/models/fragment-auth-method-attributes-account-claim-map.js b/addons/api/app/models/fragment-auth-method-attributes-account-claim-map.js new file mode 100644 index 000000000..8d0f7b0c7 --- /dev/null +++ b/addons/api/app/models/fragment-auth-method-attributes-account-claim-map.js @@ -0,0 +1 @@ +export { default } from 'api/models/fragment-auth-method-attributes-account-claim-map'; diff --git a/addons/api/tests/unit/initializers/fragment-auth-method-attributes-account-claim-map-serializer-test.js b/addons/api/tests/unit/initializers/fragment-auth-method-attributes-account-claim-map-serializer-test.js new file mode 100644 index 000000000..caac46ee2 --- /dev/null +++ b/addons/api/tests/unit/initializers/fragment-auth-method-attributes-account-claim-map-serializer-test.js @@ -0,0 +1,35 @@ +import Application from '@ember/application'; + +import { initialize } from 'dummy/initializers/fragment-auth-method-attributes-account-claim-map-serializer'; +import { module, test } from 'qunit'; +import Resolver from 'ember-resolver'; +import { run } from '@ember/runloop'; + +module( + 'Unit | Initializer | fragment-auth-method-attributes-account-claim-map-serializer', + function (hooks) { + hooks.beforeEach(function () { + this.TestApplication = class TestApplication extends Application {}; + this.TestApplication.initializer({ + name: 'initializer under test', + initialize, + }); + + this.application = this.TestApplication.create({ + autoboot: false, + Resolver, + }); + }); + + hooks.afterEach(function () { + run(this.application, 'destroy'); + }); + + // TODO: Replace this with your real tests. + test('it works', async function (assert) { + await this.application.boot(); + + assert.ok(true); + }); + } +); diff --git a/addons/api/tests/unit/models/fragment-auth-method-attributes-account-claim-map-test.js b/addons/api/tests/unit/models/fragment-auth-method-attributes-account-claim-map-test.js new file mode 100644 index 000000000..ade04df8f --- /dev/null +++ b/addons/api/tests/unit/models/fragment-auth-method-attributes-account-claim-map-test.js @@ -0,0 +1,19 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Model | fragment auth method attributes account claim map', + function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord( + 'fragment-auth-method-attributes-account-claim-map', + {} + ); + assert.ok(model); + }); + } +); diff --git a/addons/api/tests/unit/serializers/auth-method-test.js b/addons/api/tests/unit/serializers/auth-method-test.js index 54d10dede..c9c7ec1bb 100644 --- a/addons/api/tests/unit/serializers/auth-method-test.js +++ b/addons/api/tests/unit/serializers/auth-method-test.js @@ -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 | Serializer | auth method', function (hooks) { setupTest(hooks); + setupMirage(hooks); // Replace this with your real tests. test('it exists', function (assert) { @@ -27,20 +29,36 @@ module('Unit | Serializer | auth method', function (hooks) { }); test('it serializes OIDC records without state', function (assert) { - assert.expect(3); + assert.expect(1); let store = this.owner.lookup('service:store'); let record = store.createRecord('auth-method', { type: 'oidc', + name: 'OIDC Auth Method', attributes: { state: 'foo', + account_claim_maps: [{ from: 'foo', to: 'bar' }], }, }); let serializedRecord = record.serialize(); - assert.ok(serializedRecord); - assert.ok(serializedRecord.attributes, 'OIDC should have attributes'); - assert.notOk(serializedRecord.attributes.state); + assert.deepEqual(serializedRecord, { + type: 'oidc', + name: 'OIDC Auth Method', + description: null, + attributes: { + account_claim_maps: ['foo=bar'], + api_url_prefix: null, + callback_url: null, + client_id: null, + client_secret: null, + client_secret_hmac: null, + disable_discovered_config_validation: false, + dry_run: false, + issuer: null, + max_age: null, + }, + }); }); test('it serializes OIDC records with only state and version when `adapterOptions.state` is passed', function (assert) { @@ -109,4 +127,22 @@ module('Unit | Serializer | auth method', function (hooks) { 'First normalized item is primary' ); }); + + test('it normalizes OIDC records', async function (assert) { + const store = this.owner.lookup('service:store'); + this.server.get('/v1/auth-methods/oidc123', () => ({ + attributes: { + account_claim_maps: ['from=to', 'foo=bar'], + }, + version: 1, + type: 'oidc', + id: 'oidc123', + })); + const record = await store.findRecord('auth-method', 'oidc123'); + const { account_claim_maps } = record.attributes; + assert.equal(account_claim_maps.firstObject.from, 'from'); + assert.equal(account_claim_maps.firstObject.to, 'to'); + assert.equal(account_claim_maps.lastObject.from, 'foo'); + assert.equal(account_claim_maps.lastObject.to, 'bar'); + }); });