From d7b6eba2afff944b655dc47418065d232ca83a49 Mon Sep 17 00:00:00 2001 From: Devon Anderson Date: Thu, 13 Jul 2023 09:19:38 -0400 Subject: [PATCH] [ICU-10024] feat: Revise auth-method model (#1762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Revised auth-method model Revised auth-method model to include LDAP attributes ✅ Closes: ICU-10024 * chore: 🤖 create transform for `account_attribute_maps` Create transform to handle `account_attribute_maps` array * refactor: 💡 Refactor transform and serializer Refactor `account_claims_map` transforms overall name and logic to be used for `account_attributes_map`. Also refactored serializer to serialize auth-method data, by type. ✅ Closes: ICU-100024 * refactor: 💡 refactor transform Refactor `account_claim_maps` transform. * Update addons/api/addon/serializers/auth-method.js changing naming of serialize method Co-authored-by: Dharini Jeeva * refactor: 💡 refactor serializer Refactor naming convention for serializer methods and added comments for attributes. * refactor: 💡 quick refactor fixed name for method call in serializer * refactor: 💡 refactor attributes and serializer Refactor attributes for `auth-methods` to use the `for` option to select which attribute belongs to which auth-method. * Update auth-method.js comment change * refactor: 💡 rebase rebase * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva * refactor: 💡 refactor serialize attribute method refactor serialize attribute method not not include type for password, and instead let `serializePassword` handle serialization * refactor: 💡 refactor serializer Refcator serializer to not include password type in attribute serializer and added `for` to one missing attribute. * Update addons/api/addon/serializers/auth-method.js Co-authored-by: Dharini Jeeva --------- Co-authored-by: Dharini Jeeva --- .../api/addon/generated/models/auth-method.js | 208 +++++++++++++++++- addons/api/addon/models/auth-method.js | 10 +- addons/api/addon/serializers/auth-method.js | 31 ++- .../transforms/account-claim-map-array.js | 21 -- .../transforms/account-value-map-array.js | 21 ++ ...ap-array.js => account-value-map-array.js} | 2 +- ...est.js => account-value-map-array-test.js} | 6 +- 7 files changed, 253 insertions(+), 46 deletions(-) delete mode 100644 addons/api/addon/transforms/account-claim-map-array.js create mode 100644 addons/api/addon/transforms/account-value-map-array.js rename addons/api/app/transforms/{account-claim-map-array.js => account-value-map-array.js} (55%) rename addons/api/tests/unit/transforms/{account-claim-map-array-test.js => account-value-map-array-test.js} (80%) diff --git a/addons/api/addon/generated/models/auth-method.js b/addons/api/addon/generated/models/auth-method.js index eeef31ec0..b13a3fd71 100644 --- a/addons/api/addon/generated/models/auth-method.js +++ b/addons/api/addon/generated/models/auth-method.js @@ -55,55 +55,241 @@ export default class GeneratedAuthMethodModel extends BaseModel { }) version; - @attr('string', { readOnly: true, isNestedAttribute: true }) state; + // =attributes(oidc, ldap) - @attr('string', { isNestedAttribute: true }) issuer; + @attr('string', { + for: ['oidc', 'ldap'], + readOnly: true, + isNestedAttribute: true, + }) + state; - @attr('string', { isNestedAttribute: true }) client_id; + // =attributes(oidc) - @attr('string', { isNestedAttribute: true, isSecret: true }) client_secret; + @attr('string', { + for: 'oidc', + isNestedAttribute: true, + }) + issuer; - @attr('string', { readOnly: true, isNestedAttribute: true }) + @attr('string', { + for: 'oidc', + isNestedAttribute: true, + }) + client_id; + + @attr('string', { + for: 'oidc', + isNestedAttribute: true, + isSecret: true, + }) + client_secret; + + @attr('string', { + for: 'oidc', + readOnly: true, + isNestedAttribute: true, + }) client_secret_hmac; - @attr('number', { isNestedAttribute: true }) max_age; + @attr('number', { + for: 'oidc', + isNestedAttribute: true, + }) + max_age; - @attr('string', { isNestedAttribute: true }) api_url_prefix; + @attr('string', { + for: 'oidc', + isNestedAttribute: true, + }) + api_url_prefix; - @attr('string', { readOnly: true, isNestedAttribute: true }) callback_url; + @attr('string', { + for: 'oidc', + readOnly: true, + isNestedAttribute: true, + }) + callback_url; - @attr('boolean', { isNestedAttribute: true }) + @attr('boolean', { + for: 'oidc', + isNestedAttribute: true, + }) disable_discovered_config_validation; - @attr('boolean', { isNestedAttribute: true }) dry_run; + @attr('boolean', { + for: 'oidc', + isNestedAttribute: true, + }) + dry_run; @attr('string-array', { + for: 'oidc', emptyArrayIfMissing: true, isNestedAttribute: true, }) claims_scopes; @attr('string-array', { + for: 'oidc', emptyArrayIfMissing: true, isNestedAttribute: true, }) signing_algorithms; @attr('string-array', { + for: 'oidc', emptyArrayIfMissing: true, isNestedAttribute: true, }) allowed_audiences; @attr('string-array', { + for: 'oidc', emptyArrayIfMissing: true, isNestedAttribute: true, }) idp_ca_certs; - @attr('account-claim-map-array', { + @attr('account-value-map-array', { + for: 'oidc', emptyArrayIfMissing: true, isNestedAttribute: true, }) account_claim_maps; + + // =attributes(ldap) + + @attr('boolean', { + for: 'ldap', + isNestedAttribute: true, + }) + start_tls; + + @attr('boolean', { + for: 'ldap', + isNestedAttribute: true, + }) + insecure_tls; + + @attr('boolean', { + for: 'ldap', + isNestedAttribute: true, + }) + discover_dn; + + @attr('boolean', { + for: 'ldap', + isNestedAttribute: true, + }) + anon_group_search; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + upn_domain; + + @attr('string-array', { + for: 'ldap', + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + urls; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + user_dn; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + user_attr; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + user_filter; + + @attr('boolean', { + for: 'ldap', + isNestedAttribute: true, + }) + enable_groups; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + group_dn; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + group_attr; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + group_filter; + + @attr('string-array', { + for: 'ldap', + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + certificates; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + client_certificate; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + isSecret: true, + }) + client_certificate_key; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + readOnly: true, + }) + client_certificate_key_hmac; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + }) + bind_dn; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + isSecret: true, + }) + bind_password; + + @attr('string', { + for: 'ldap', + isNestedAttribute: true, + readOnly: true, + }) + bind_password_hmac; + + @attr('account-value-map-array', { + for: 'ldap', + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + account_attribute_maps; } diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index 8599160f1..083d6fb1f 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -38,6 +38,12 @@ export const options = { to: ['sub', 'name', 'email'], }, }, + ldap: { + account_attribute_maps: { + to: ['fullName', 'email'], + }, + state: ['inactive', 'active-private', 'active-public'], + }, }; export default class AuthMethodModel extends GeneratedAuthMethodModel { @@ -102,9 +108,9 @@ export default class AuthMethodModel extends GeneratedAuthMethodModel { // =methods /** - * Change the active and visibility state of an OIDC auth method + * Change the active and visibility state of an OIDC and LDAP auth method * given its ID. - * For OIDC auth methods only. + * For OIDC and LDAP auth methods only. * @param {[string]} state * @param {object} options * @param {object} options.adapterOptions diff --git a/addons/api/addon/serializers/auth-method.js b/addons/api/addon/serializers/auth-method.js index 01488e4e6..8ceb461ab 100644 --- a/addons/api/addon/serializers/auth-method.js +++ b/addons/api/addon/serializers/auth-method.js @@ -4,7 +4,7 @@ */ import ApplicationSerializer from './application'; -import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; +import { TYPE_AUTH_METHOD_PASSWORD } from 'api/models/auth-method'; export default class AuthMethodSerializer extends ApplicationSerializer { // =methods @@ -17,18 +17,33 @@ export default class AuthMethodSerializer extends ApplicationSerializer { */ serialize(snapshot) { switch (snapshot.record.type) { - case TYPE_AUTH_METHOD_OIDC: - return this.serializeOIDC(...arguments); + case TYPE_AUTH_METHOD_PASSWORD: + return this.serializePassword(...arguments); default: return this.serializeDefault(...arguments); } } + serializeAttribute(snapshot, json, key, attribute) { + const { type } = snapshot.record; + super.serializeAttribute(...arguments); + const { options } = attribute; + + // If an attribute has a `for` option, it must match the + // record's `type`, else the attribute is excluded + // from serialization. + if (options?.for && !options.for.includes(type)) { + if (options.isNestedAttribute) { + delete json?.attributes?.[key]; + } + } + } + /** - * Default serialization omits `attributes`. + * Password serialization omits `attributes`. * @return {object} */ - serializeDefault() { + serializePassword() { let serialized = super.serialize(...arguments); delete serialized.attributes; return serialized; @@ -40,11 +55,11 @@ export default class AuthMethodSerializer extends ApplicationSerializer { * @param {Snapshot} snapshot * @return {object} */ - serializeOIDC(snapshot) { + serializeDefault(snapshot) { let serialized = super.serialize(...arguments); const state = snapshot?.adapterOptions?.state; if (state) { - serialized = this.serializeOIDCWithState(snapshot, state); + serialized = this.serializeWithState(snapshot, state); } else { delete serialized.attributes.state; } @@ -57,7 +72,7 @@ export default class AuthMethodSerializer extends ApplicationSerializer { * @param {string} state * @return {object} */ - serializeOIDCWithState(snapshot, state) { + serializeWithState(snapshot, state) { return { version: snapshot.attr('version'), attributes: { state }, diff --git a/addons/api/addon/transforms/account-claim-map-array.js b/addons/api/addon/transforms/account-claim-map-array.js deleted file mode 100644 index 17d519892..000000000 --- a/addons/api/addon/transforms/account-claim-map-array.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import Transform from '@ember-data/serializer/transform'; - -export default class AccountClaimMapArrayTransform extends Transform { - deserialize(serialized) { - const accountClaims = serialized || []; - return accountClaims.map((accountClaimString) => ({ - from: accountClaimString.split('=')[0], - to: accountClaimString.split('=')[1], - })); - } - - serialize(deserialized) { - const accountClaims = deserialized || []; - return accountClaims.map(({ from, to }) => `${from}=${to}`); - } -} diff --git a/addons/api/addon/transforms/account-value-map-array.js b/addons/api/addon/transforms/account-value-map-array.js new file mode 100644 index 000000000..f4b9aa7d7 --- /dev/null +++ b/addons/api/addon/transforms/account-value-map-array.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Transform from '@ember-data/serializer/transform'; + +export default class AccountValueMapArrayTransform extends Transform { + deserialize(serialized) { + const accountValues = serialized || []; + return accountValues.map((accountValueString) => ({ + from: accountValueString.split('=')[0], + to: accountValueString.split('=')[1], + })); + } + + serialize(deserialized) { + const accountValues = deserialized || []; + return accountValues.map(({ from, to }) => `${from}=${to}`); + } +} diff --git a/addons/api/app/transforms/account-claim-map-array.js b/addons/api/app/transforms/account-value-map-array.js similarity index 55% rename from addons/api/app/transforms/account-claim-map-array.js rename to addons/api/app/transforms/account-value-map-array.js index 696213b2f..14083164e 100644 --- a/addons/api/app/transforms/account-claim-map-array.js +++ b/addons/api/app/transforms/account-value-map-array.js @@ -3,4 +3,4 @@ * SPDX-License-Identifier: MPL-2.0 */ -export { default } from 'api/transforms/account-claim-map-array'; +export { default } from 'api/transforms/account-value-map-array'; diff --git a/addons/api/tests/unit/transforms/account-claim-map-array-test.js b/addons/api/tests/unit/transforms/account-value-map-array-test.js similarity index 80% rename from addons/api/tests/unit/transforms/account-claim-map-array-test.js rename to addons/api/tests/unit/transforms/account-value-map-array-test.js index acba3091d..ba7e75fbc 100644 --- a/addons/api/tests/unit/transforms/account-claim-map-array-test.js +++ b/addons/api/tests/unit/transforms/account-value-map-array-test.js @@ -6,12 +6,12 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Transform | account claim map array', function (hooks) { +module('Unit | Transform | account value map array', function (hooks) { setupTest(hooks); test('it deserializes an array of account claims', function (assert) { assert.expect(1); - const transform = this.owner.lookup('transform:account-claim-map-array'); + const transform = this.owner.lookup('transform:account-value-map-array'); const deserialized = transform.deserialize(['oid=sub', 'full_name=name']); assert.deepEqual(deserialized, [ { from: 'oid', to: 'sub' }, @@ -21,7 +21,7 @@ module('Unit | Transform | account claim map array', function (hooks) { test('it serializes an array of account claims', function (assert) { assert.expect(1); - const transform = this.owner.lookup('transform:account-claim-map-array'); + const transform = this.owner.lookup('transform:account-value-map-array'); const serialized = transform.serialize([ { from: 'oid', to: 'sub' }, { from: 'full_name', to: 'name' },