From fd72b414e03fc9a319715b9c046fbb45b148cd8e Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Wed, 10 May 2023 14:13:30 -0700 Subject: [PATCH 01/14] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20yaml=20v?= =?UTF-8?q?ersion=20to=20fix=20security=20alert=20(#1684)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index dca617400..67e1e793e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26164,9 +26164,9 @@ yaml@^1.10.0, yaml@^1.7.2: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" - integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@^20.0.0, yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^21.0.0, yargs-parser@~20.2.7: version "20.2.9" From b344c5fc18e51208dfaab6d166844770e8ad66db Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Wed, 10 May 2023 15:07:33 -0700 Subject: [PATCH 02/14] =?UTF-8?q?chore:=20=F0=9F=A4=96=20upgrade=20engine.?= =?UTF-8?q?io=20version=20to=20fix=20security=20alert=20(#1685)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yarn.lock | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index 67e1e793e..e400fa5b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13925,10 +13925,10 @@ engine.io-parser@~5.0.3: resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== -engine.io@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" - integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== +engine.io@~6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.4.2.tgz#ffeaf68f69b1364b0286badddf15ff633476473f" + integrity sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" @@ -13939,7 +13939,7 @@ engine.io@~6.2.1: cors "~2.8.5" debug "~4.3.1" engine.io-parser "~5.0.3" - ws "~8.2.3" + ws "~8.11.0" enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: version "4.5.0" @@ -23419,10 +23419,12 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-adapter@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" - integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== +socket.io-adapter@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" + integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== + dependencies: + ws "~8.11.0" socket.io-parser@~4.2.1: version "4.2.1" @@ -23433,15 +23435,15 @@ socket.io-parser@~4.2.1: debug "~4.3.1" socket.io@^4.1.2: - version "4.5.4" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" - integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== + version "4.6.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.6.1.tgz#62ec117e5fce0692fa50498da9347cfb52c3bc70" + integrity sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA== dependencies: accepts "~1.3.4" base64id "~2.0.0" debug "~4.3.2" - engine.io "~6.2.1" - socket.io-adapter "~2.4.0" + engine.io "~6.4.1" + socket.io-adapter "~2.5.2" socket.io-parser "~4.2.1" socks-proxy-agent@^6.0.0: @@ -26088,7 +26090,7 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^6.1.0, ws@^7.4.6, ws@~8.2.3: +ws@^6.1.0, ws@^7.4.6, ws@~8.11.0: version "7.5.3" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== From 4c80f8d8ff3f2ddc7468283fd1ed77e058f7ab08 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 11 May 2023 13:55:27 -0700 Subject: [PATCH 03/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20initial=20LDAP=20a?= =?UTF-8?q?uthentication=20support=20(#1645)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 initial LDAP authentication support --- addons/core/addon/authenticators/ldap.js | 13 ++++ .../form/authenticate/details/index.hbs | 41 ++++++++++ .../{password => details}/index.js | 2 +- .../components/form/authenticate/index.hbs | 1 - .../form/authenticate/ldap/index.hbs | 6 ++ .../form/authenticate/oidc/index.hbs | 1 - .../form/authenticate/password/index.hbs | 33 +------- addons/core/app/authenticators/ldap.js | 6 ++ .../components/form/authenticate/details.js | 6 ++ .../app/components/form/authenticate/ldap.js | 6 ++ .../components/form/authenticate-test.js | 78 +++++++++++++++++++ addons/core/translations/resources/en-us.yaml | 1 + .../scopes/scope/authenticate/method.js | 1 + .../scopes/scope/authenticate/method.js | 1 + 14 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 addons/core/addon/authenticators/ldap.js create mode 100644 addons/core/addon/components/form/authenticate/details/index.hbs rename addons/core/addon/components/form/authenticate/{password => details}/index.js (90%) create mode 100644 addons/core/addon/components/form/authenticate/ldap/index.hbs create mode 100644 addons/core/app/authenticators/ldap.js create mode 100644 addons/core/app/components/form/authenticate/details.js create mode 100644 addons/core/app/components/form/authenticate/ldap.js create mode 100644 addons/core/tests/integration/components/form/authenticate-test.js diff --git a/addons/core/addon/authenticators/ldap.js b/addons/core/addon/authenticators/ldap.js new file mode 100644 index 000000000..9fc6bc734 --- /dev/null +++ b/addons/core/addon/authenticators/ldap.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import PasswordAuthenticator from './password'; + +/** + * A simple login_name/password authenticator that authenticates with + * a scope/auth-method endpoint. Authentication occurs on an auth method + * endpoint, while deauthentication occurs on a scope endpoint. + */ +export default class LDAPAuthenticator extends PasswordAuthenticator {} diff --git a/addons/core/addon/components/form/authenticate/details/index.hbs b/addons/core/addon/components/form/authenticate/details/index.hbs new file mode 100644 index 000000000..177e9a2ee --- /dev/null +++ b/addons/core/addon/components/form/authenticate/details/index.hbs @@ -0,0 +1,41 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + + + + {{t 'form.login_name.label'}} + + + + {{t 'form.password.label'}} + + + + + \ No newline at end of file diff --git a/addons/core/addon/components/form/authenticate/password/index.js b/addons/core/addon/components/form/authenticate/details/index.js similarity index 90% rename from addons/core/addon/components/form/authenticate/password/index.js rename to addons/core/addon/components/form/authenticate/details/index.js index 55c931da6..7b1306c64 100644 --- a/addons/core/addon/components/form/authenticate/password/index.js +++ b/addons/core/addon/components/form/authenticate/details/index.js @@ -8,7 +8,7 @@ import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; import { run } from '@ember/runloop'; -export default class FormAuthenticatePasswordComponent extends Component { +export default class FormAuthenticateDetailsComponent extends Component { // =properties /** diff --git a/addons/core/addon/components/form/authenticate/index.hbs b/addons/core/addon/components/form/authenticate/index.hbs index 49ee937a1..5a454b6c8 100644 --- a/addons/core/addon/components/form/authenticate/index.hbs +++ b/addons/core/addon/components/form/authenticate/index.hbs @@ -9,7 +9,6 @@ {{component (concat 'form/authenticate/' @model.type) submit=@submit - cancel=false disabled=@disabled }} {{/if}} \ No newline at end of file diff --git a/addons/core/addon/components/form/authenticate/ldap/index.hbs b/addons/core/addon/components/form/authenticate/ldap/index.hbs new file mode 100644 index 000000000..9cb8405e7 --- /dev/null +++ b/addons/core/addon/components/form/authenticate/ldap/index.hbs @@ -0,0 +1,6 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + + \ No newline at end of file diff --git a/addons/core/addon/components/form/authenticate/oidc/index.hbs b/addons/core/addon/components/form/authenticate/oidc/index.hbs index f83a4b2ba..61ca08cfe 100644 --- a/addons/core/addon/components/form/authenticate/oidc/index.hbs +++ b/addons/core/addon/components/form/authenticate/oidc/index.hbs @@ -6,7 +6,6 @@ diff --git a/addons/core/addon/components/form/authenticate/password/index.hbs b/addons/core/addon/components/form/authenticate/password/index.hbs index 6ca551628..9cb8405e7 100644 --- a/addons/core/addon/components/form/authenticate/password/index.hbs +++ b/addons/core/addon/components/form/authenticate/password/index.hbs @@ -3,35 +3,4 @@ SPDX-License-Identifier: MPL-2.0 }} - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/addons/core/app/authenticators/ldap.js b/addons/core/app/authenticators/ldap.js new file mode 100644 index 000000000..7bd0705ab --- /dev/null +++ b/addons/core/app/authenticators/ldap.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'core/authenticators/ldap'; diff --git a/addons/core/app/components/form/authenticate/details.js b/addons/core/app/components/form/authenticate/details.js new file mode 100644 index 000000000..c5bd5a8d4 --- /dev/null +++ b/addons/core/app/components/form/authenticate/details.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'core/components/form/authenticate/details'; diff --git a/addons/core/app/components/form/authenticate/ldap.js b/addons/core/app/components/form/authenticate/ldap.js new file mode 100644 index 000000000..26787de39 --- /dev/null +++ b/addons/core/app/components/form/authenticate/ldap.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'core/components/form/authenticate/ldap'; diff --git a/addons/core/tests/integration/components/form/authenticate-test.js b/addons/core/tests/integration/components/form/authenticate-test.js new file mode 100644 index 000000000..8a85394c0 --- /dev/null +++ b/addons/core/tests/integration/components/form/authenticate-test.js @@ -0,0 +1,78 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { setupIntl } from 'ember-intl/test-support'; + +module('Integration | Component | form | authenticate', function (hooks) { + setupRenderingTest(hooks); + setupIntl(hooks); + + test('it shows login name and password when LDAP is selected from the list', async function (assert) { + assert.expect(3); + + const store = this.owner.lookup('service:store'); + + this.model = store.createRecord('auth-method', { type: 'ldap' }); + this.submit = () => {}; + this.disabled = () => {}; + + await render(hbs``); + assert.ok(find('.full-width')); + assert.ok(find('.hds-form-text-input')); + assert.strictEqual( + find('button[type="submit"]').textContent.trim(), + 'Sign In' + ); + }); + + test('it shows login name and password when password is selected from the list', async function (assert) { + assert.expect(3); + + const store = this.owner.lookup('service:store'); + + this.model = store.createRecord('auth-method', { type: 'password' }); + this.submit = () => {}; + this.disabled = () => {}; + + await render(hbs``); + assert.ok(find('.full-width')); + assert.ok(find('.hds-form-text-input')); + assert.strictEqual( + find('button[type="submit"]').textContent.trim(), + 'Sign In' + ); + }); + + test('it should only show sign in button when OIDC is selected from the list', async function (assert) { + assert.expect(3); + + const store = this.owner.lookup('service:store'); + + this.model = store.createRecord('auth-method', { type: 'oidc' }); + this.submit = () => {}; + this.disabled = () => {}; + + await render(hbs``); + assert.ok(find('.full-width')); + assert.notOk(find('.hds-form-text-input')); + assert.strictEqual( + find('button[type="submit"]').textContent.trim(), + 'Sign In' + ); + }); +}); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index f9cba0b87..c57556183 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -47,6 +47,7 @@ auth-method: types: password: Password oidc: OIDC + ldap: LDAP actions: create: Create Auth Method delete: Delete Auth Method diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 84732cc40..f6532a008 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -63,6 +63,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const requestCookies = false; switch (authMethod.type) { case 'password': + case 'ldap': await this.session.authenticate( authenticatorName, creds, diff --git a/ui/desktop/app/routes/scopes/scope/authenticate/method.js b/ui/desktop/app/routes/scopes/scope/authenticate/method.js index f01c49aa2..8358f8ef5 100644 --- a/ui/desktop/app/routes/scopes/scope/authenticate/method.js +++ b/ui/desktop/app/routes/scopes/scope/authenticate/method.js @@ -70,6 +70,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const requestCookies = false; switch (authMethod.type) { case 'password': + case 'ldap': await this.session.authenticate( authenticatorName, creds, From 3b3d6ab50fb690e307479b925302ad08f13d2732 Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Thu, 11 May 2023 15:15:31 -0700 Subject: [PATCH 04/14] Icu 9202 disable all actions for ldap auth method (#1683) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 disable all actions for ldap auth-method ✅ Closes: ICU-9202 --- addons/api/addon/abilities/auth-method.js | 20 +++ addons/api/addon/models/auth-method.js | 28 +++- addons/api/addon/serializers/auth-method.js | 4 +- addons/api/mirage/factories/auth-method.js | 9 +- .../mirage/generated/factories/auth-method.js | 9 +- .../api/tests/unit/models/auth-method-test.js | 83 +++++++++- .../unit/serializers/auth-method-test.js | 17 +- ui/admin/app/abilities/auth-method.js | 21 +++ .../app/routes/scopes/scope/auth-methods.js | 3 +- .../scopes/scope/authenticate/method.js | 11 +- .../scopes/scope/auth-methods/index.hbs | 53 +++--- .../acceptance/auth-methods/oidc-test.js | 4 +- .../acceptance/auth-methods/read-test.js | 154 +++++++++++++++--- .../tests/unit/abilities/auth-method-test.js | 75 +++++++++ 14 files changed, 424 insertions(+), 67 deletions(-) create mode 100644 addons/api/addon/abilities/auth-method.js create mode 100644 ui/admin/app/abilities/auth-method.js create mode 100644 ui/admin/tests/unit/abilities/auth-method-test.js diff --git a/addons/api/addon/abilities/auth-method.js b/addons/api/addon/abilities/auth-method.js new file mode 100644 index 000000000..744f93ac7 --- /dev/null +++ b/addons/api/addon/abilities/auth-method.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import ModelAbility from './model'; +/** + * Provides abilities for auth method. + */ +export default class AuthMethodAbility extends ModelAbility { + // =permissions + + /** + * Only "known" auth method types may be read. + * @type {boolean} + */ + get canRead() { + return !this.model.isUnknown && this.hasAuthorizedAction('read'); + } +} diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index dc519cf47..8599160f1 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -6,6 +6,16 @@ import GeneratedAuthMethodModel from '../generated/models/auth-method'; import { equal } from '@ember/object/computed'; +export const TYPE_AUTH_METHOD_PASSWORD = 'password'; +export const TYPE_AUTH_METHOD_OIDC = 'oidc'; +export const TYPE_AUTH_METHOD_LDAP = 'ldap'; + +export const TYPES_AUTH_METHOD = Object.freeze([ + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +]); + /** * Enum options per auth method type and field. */ @@ -44,14 +54,28 @@ export default class AuthMethodModel extends GeneratedAuthMethodModel { * @type {boolean} */ get isPassword() { - return this.type === 'password'; + return this.type === TYPE_AUTH_METHOD_PASSWORD; } /** * @type {boolean} */ get isOIDC() { - return this.type === 'oidc'; + return this.type === TYPE_AUTH_METHOD_OIDC; + } + + /** + * @type {boolean} + */ + get isLDAP() { + return this.type === TYPE_AUTH_METHOD_LDAP; + } + + /** + * @type {boolean} + */ + get isUnknown() { + return !TYPES_AUTH_METHOD.includes(this.type); } /** diff --git a/addons/api/addon/serializers/auth-method.js b/addons/api/addon/serializers/auth-method.js index d21f54cff..01488e4e6 100644 --- a/addons/api/addon/serializers/auth-method.js +++ b/addons/api/addon/serializers/auth-method.js @@ -4,6 +4,8 @@ */ import ApplicationSerializer from './application'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; + export default class AuthMethodSerializer extends ApplicationSerializer { // =methods @@ -15,7 +17,7 @@ export default class AuthMethodSerializer extends ApplicationSerializer { */ serialize(snapshot) { switch (snapshot.record.type) { - case 'oidc': + case TYPE_AUTH_METHOD_OIDC: return this.serializeOIDC(...arguments); default: return this.serializeDefault(...arguments); diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js index 96589133f..dee5481a3 100644 --- a/addons/api/mirage/factories/auth-method.js +++ b/addons/api/mirage/factories/auth-method.js @@ -8,7 +8,10 @@ import { trait } from 'miragejs'; import permissions from '../helpers/permissions'; import generateId from '../helpers/id'; -const types = ['password', 'oidc']; +import { + TYPE_AUTH_METHOD_OIDC, + TYPES_AUTH_METHOD, +} from 'api/models/auth-method'; export default factory.extend({ authorized_actions: () => @@ -29,7 +32,7 @@ export default factory.extend({ id: () => generateId('am_'), // Cycle through available types - type: (i) => types[i % types.length], + type: (i) => TYPES_AUTH_METHOD[i % TYPES_AUTH_METHOD.length], /** * Adds accounts (with associated users) to auth method and managed groups. @@ -43,7 +46,7 @@ export default factory.extend({ user.update({ accountIds: [id] }); }); - if (type === 'oidc') { + if (type === TYPE_AUTH_METHOD_OIDC) { server .createList('managed-group', 2, { scope, authMethod }) .map((managedGroup) => { diff --git a/addons/api/mirage/generated/factories/auth-method.js b/addons/api/mirage/generated/factories/auth-method.js index bc6b5c79b..d0cdd71c3 100644 --- a/addons/api/mirage/generated/factories/auth-method.js +++ b/addons/api/mirage/generated/factories/auth-method.js @@ -6,11 +6,16 @@ import { Factory } from 'miragejs'; import { faker } from '@faker-js/faker'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, +} from 'api/models/auth-method'; + /** * GeneratedAuthMethodModelFactory */ export default Factory.extend({ - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: () => faker.random.words(), description: () => faker.random.words(), created_time: () => faker.date.recent(), @@ -20,7 +25,7 @@ export default Factory.extend({ attributes: function () { let attrs; switch (this.type) { - case 'oidc': + case TYPE_AUTH_METHOD_OIDC: attrs = { api_url_prefix: 'http://127.0.0.1:9200', callback_url: diff --git a/addons/api/tests/unit/models/auth-method-test.js b/addons/api/tests/unit/models/auth-method-test.js index 1e084b9ae..a908d2280 100644 --- a/addons/api/tests/unit/models/auth-method-test.js +++ b/addons/api/tests/unit/models/auth-method-test.js @@ -6,13 +6,23 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; module('Unit | Model | auth method', function (hooks) { setupTest(hooks); setupMirage(hooks); + let store; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:store'); + }); + test('it exists', function (assert) { - let store = this.owner.lookup('service:store'); let model = store.createRecord('auth-method', {}); assert.ok(model); }); @@ -32,13 +42,12 @@ module('Unit | Model | auth method', function (hooks) { return { id: '123abc' }; } ); - const store = this.owner.lookup('service:store'); store.push({ data: { id: '123abc', type: 'auth-method', attributes: { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, state: 'foobar', version: 1, scope: { @@ -51,4 +60,72 @@ module('Unit | Model | auth method', function (hooks) { const model = store.peekRecord('auth-method', '123abc'); await model.changeState('foobar'); }); + + test('it has isPassword property and returns the expected values', async function (assert) { + assert.expect(3); + const modelA = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelB = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelC = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(modelA.isPassword); + assert.false(modelB.isPassword); + assert.false(modelC.isPassword); + }); + + test('it has isOIDC property and returns the expected values', async function (assert) { + assert.expect(3); + const modelA = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelB = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(modelA.isOIDC); + assert.false(modelB.isOIDC); + assert.false(modelC.isOIDC); + }); + + test('it has isLDAP property and returns the expected values', async function (assert) { + assert.expect(3); + const modelA = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + const modelB = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(modelA.isLDAP); + assert.false(modelB.isLDAP); + assert.false(modelC.isLDAP); + }); + + test('it has isUnknown property and returns the expected values', async function (assert) { + assert.expect(4); + const modelA = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + const modelB = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelD = store.createRecord('auth-method', { + type: 'no-such-type', + }); + assert.false(modelA.isUnknown); + assert.false(modelB.isUnknown); + assert.false(modelC.isUnknown); + assert.true(modelD.isUnknown); + }); }); diff --git a/addons/api/tests/unit/serializers/auth-method-test.js b/addons/api/tests/unit/serializers/auth-method-test.js index 4ecf4d08c..f9989a5f3 100644 --- a/addons/api/tests/unit/serializers/auth-method-test.js +++ b/addons/api/tests/unit/serializers/auth-method-test.js @@ -7,6 +7,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, +} from 'api/models/auth-method'; + module('Unit | Serializer | auth method', function (hooks) { setupTest(hooks); setupMirage(hooks); @@ -21,7 +26,9 @@ module('Unit | Serializer | auth method', function (hooks) { test('it serializes password records', function (assert) { assert.expect(2); let store = this.owner.lookup('service:store'); - let record = store.createRecord('auth-method', { type: 'password' }); + let record = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); let serializedRecord = record.serialize(); @@ -36,7 +43,7 @@ module('Unit | Serializer | auth method', function (hooks) { assert.expect(1); let store = this.owner.lookup('service:store'); let record = store.createRecord('auth-method', { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'OIDC Auth Method', state: 'foo', account_claim_maps: [{ from: 'foo', to: 'bar' }], @@ -62,7 +69,7 @@ module('Unit | Serializer | auth method', function (hooks) { let serializedRecord = record.serialize(); assert.deepEqual(serializedRecord, { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'OIDC Auth Method', description: null, attributes: { @@ -87,7 +94,7 @@ module('Unit | Serializer | auth method', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('auth-method'); const record = store.createRecord('auth-method', { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, attributes: { state: 'foo', }, @@ -175,7 +182,7 @@ module('Unit | Serializer | auth method', function (hooks) { max_age: maxAge, }, version: 1, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, id: 'oidc123', })); diff --git a/ui/admin/app/abilities/auth-method.js b/ui/admin/app/abilities/auth-method.js new file mode 100644 index 000000000..ebbf9b0cd --- /dev/null +++ b/ui/admin/app/abilities/auth-method.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import AuthMethodAbility from 'api/abilities/auth-method'; + +export default class OverrideAuthMethodAbility extends AuthMethodAbility { + /** + * These override abilities are temporary and just part of Phase 1 + * of LDAP auth method implementation. + */ + + get canRead() { + return !this.model.isLDAP && super.canRead; + } + + get canMakePrimary() { + return !this.model.isLDAP; + } +} diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js index e4c2b9312..b5522e3db 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods.js @@ -10,6 +10,7 @@ import { loading } from 'ember-loading'; import { confirm } from 'core/decorators/confirm'; import { notifySuccess, notifyError } from 'core/decorators/notify'; import { resourceFilter } from 'core/decorators/resource-filter'; +import { TYPES_AUTH_METHOD } from 'api/models/auth-method'; export default class ScopesScopeAuthMethodsRoute extends Route { // =services @@ -23,7 +24,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route { // =attributes - @resourceFilter({ allowed: ['password', 'oidc'] }) type; + @resourceFilter({ allowed: TYPES_AUTH_METHOD }) type; // =methods diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index f6532a008..f7cc6b680 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -9,6 +9,11 @@ import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import { loading } from 'ember-loading'; import { notifyError } from 'core/decorators/notify'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; export default class ScopesScopeAuthenticateMethodRoute extends Route { // =services @@ -62,8 +67,8 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const authenticatorName = `authenticator:${authMethod.type}`; const requestCookies = false; switch (authMethod.type) { - case 'password': - case 'ldap': + case TYPE_AUTH_METHOD_PASSWORD: + case TYPE_AUTH_METHOD_LDAP: await this.session.authenticate( authenticatorName, creds, @@ -72,7 +77,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { ); this.router.transitionTo('index'); break; - case 'oidc': + case TYPE_AUTH_METHOD_OIDC: await this.startOIDCAuthentication(authenticatorName, { scope, authMethod, diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs index 2ecdc0458..31dac7bb6 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -120,7 +120,7 @@ {{#each @model as |authMethod|}} - {{#if (can 'read model' authMethod)}} + {{#if (can 'read auth-method' authMethod)}} - - {{#unless authMethod.isPrimary}} - - {{t 'resources.auth-method.actions.make-primary'}} - - {{/unless}} - {{#if authMethod.isPrimary}} - - {{t 'resources.auth-method.actions.remove-as-primary'}} - - {{/if}} - + {{#if (can 'makePrimary auth-method' authMethod)}} + + {{#unless authMethod.isPrimary}} + + {{t 'resources.auth-method.actions.make-primary'}} + + {{/unless}} + {{#if authMethod.isPrimary}} + + {{t 'resources.auth-method.actions.remove-as-primary'}} + + {{/if}} + + {{/if}} {{/each}} diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js index 7262e3f2a..00c62238f 100644 --- a/ui/admin/tests/acceptance/auth-methods/oidc-test.js +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -23,6 +23,8 @@ import { //invalidateSession, } from 'ember-simple-auth/test-support'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; + module('Acceptance | auth methods | oidc', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -49,7 +51,7 @@ module('Acceptance | auth methods | oidc', function (hooks) { }); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); urls.orgScope = `/scopes/${instances.scopes.org.id}`; diff --git a/ui/admin/tests/acceptance/auth-methods/read-test.js b/ui/admin/tests/acceptance/auth-methods/read-test.js index 29ea63b75..72ff3c2b9 100644 --- a/ui/admin/tests/acceptance/auth-methods/read-test.js +++ b/ui/admin/tests/acceptance/auth-methods/read-test.js @@ -4,7 +4,7 @@ */ import { module, test } from 'qunit'; -import { visit, currentURL, click, find } from '@ember/test-helpers'; +import { visit, currentURL, click } 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'; @@ -14,23 +14,44 @@ import { // //currentSession, // //invalidateSession, } from 'ember-simple-auth/test-support'; +import { + TYPE_AUTH_METHOD_LDAP, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_PASSWORD, +} from 'api/models/auth-method'; module('Acceptance | auth methods | read', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + const AUTH_LINK_SELECTOR = + 'main tbody .rose-table-header-cell:nth-child(1) a'; + const AUTH_TYPE_SELECTOR = + 'tbody .rose-table-row:nth-child(1) .rose-table-cell'; + const AUTH_ACTIONS_SELECTOR = + 'tbody .rose-table-row:nth-child(1) .rose-table-cell:last-child .rose-dropdown'; + const LDAP_AUTH_LINK_SELECTOR = 'tbody .rose-table-row:nth-child(2) a'; + const LDAP_AUTH_TYPE_SELECTOR = + 'tbody .rose-table-row:nth-child(2) .rose-table-cell'; + const LDAP_AUTH_ACTIONS_SELECTOR = + 'tbody .rose-table-row:nth-child(2) .rose-table-cell:last-child .rose-dropdown'; const instances = { scopes: { global: null, org: null, }, - authMethod: null, + passwordAuthMethodOrg: null, + oidcAuthMethodGlobal: null, + ldapAuthMethod: null, }; + const urls = { + globalScope: null, orgScope: null, - authMethods: null, - newAuthMethod: null, - authMethod: null, + orgAuthMethods: null, + globalAuthMethods: null, + passwordAuthMethodOrg: null, + oidcAuthMethodGlobal: null, }; hooks.beforeEach(function () { @@ -41,37 +62,126 @@ module('Acceptance | auth methods | read', function (hooks) { type: 'org', scope: { id: 'global', type: 'global' }, }); - instances.authMethod = this.server.create('auth-method', { + instances.passwordAuthMethodOrg = this.server.create('auth-method', { scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_PASSWORD, + }); + instances.oidcAuthMethodGlobal = this.server.create('auth-method', { + scope: instances.scopes.global, + type: TYPE_AUTH_METHOD_OIDC, }); - // Generate route URLs for resources + urls.globalScope = `/scopes/global`; urls.orgScope = `/scopes/${instances.scopes.org.id}`; - urls.authMethods = `${urls.orgScope}/auth-methods`; - urls.newAuthMethod = `${urls.authMethods}/new?type=password`; - urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + urls.orgAuthMethods = `${urls.orgScope}/auth-methods`; + urls.globalAuthMethods = `${urls.globalScope}/auth-methods`; + urls.passwordAuthMethodOrg = `${urls.orgAuthMethods}/${instances.passwordAuthMethodOrg.id}`; + urls.oidcAuthMethodGlobal = `${urls.globalAuthMethods}/${instances.oidcAuthMethodGlobal.id}`; }); - test('visiting auth methods', async function (assert) { + test('visiting auth methods in org scope', async function (assert) { assert.expect(1); - await visit(urls.authMethods); + await visit(urls.orgScope); + + await click(`[href="${urls.orgAuthMethods}"]`); await a11yAudit(); - assert.strictEqual(currentURL(), urls.authMethods); + + assert.strictEqual(currentURL(), urls.orgAuthMethods); }); - test('can navigate to an auth method form', async function (assert) { + test('visiting auth methods in global scope', async function (assert) { assert.expect(1); - await visit(urls.authMethods); - await click('main tbody .rose-table-header-cell:nth-child(1) a'); + await visit(urls.globalScope); + + await click(`[href="${urls.globalAuthMethods}"]`); await a11yAudit(); - assert.strictEqual(currentURL(), urls.authMethod); + + assert.strictEqual(currentURL(), urls.globalAuthMethods); }); - test('cannot navigate to an auth method form without proper authorization', async function (assert) { + test('can navigate to an auth method form in org scope', async function (assert) { + assert.expect(3); + await visit(urls.orgScope); + + await click(`[href="${urls.orgAuthMethods}"]`); + + assert.dom(AUTH_TYPE_SELECTOR).hasText('Password'); + assert.dom(AUTH_ACTIONS_SELECTOR).exists(); + + await click(AUTH_LINK_SELECTOR); + await a11yAudit(); + + assert.strictEqual(currentURL(), urls.passwordAuthMethodOrg); + }); + + test('can navigate to an auth method form in global scope', async function (assert) { + assert.expect(3); + await visit(urls.globalScope); + + await click(`[href="${urls.globalAuthMethods}"]`); + + assert.dom(AUTH_TYPE_SELECTOR).hasText('OIDC'); + assert.dom(AUTH_ACTIONS_SELECTOR).exists(); + + await click(AUTH_LINK_SELECTOR); + await a11yAudit(); + + assert.strictEqual(currentURL(), urls.oidcAuthMethodGlobal); + }); + + test('cannot navigate to an auth method form without proper authorization in org scope', async function (assert) { assert.expect(1); - instances.authMethod.authorized_actions = - instances.authMethod.authorized_actions.filter((item) => item !== 'read'); - await visit(urls.authMethod); - assert.notOk(find('main tbody .rose-table-header-cell:nth-child(1) a')); + instances.passwordAuthMethodOrg.authorized_actions = + instances.passwordAuthMethodOrg.authorized_actions.filter( + (item) => item !== 'read' + ); + await visit(urls.orgScope); + + await click(`[href="${urls.orgAuthMethods}"]`); + + assert.dom(AUTH_LINK_SELECTOR).doesNotExist(); + }); + + test('cannot navigate to an auth method form without proper authorization in global scope', async function (assert) { + assert.expect(1); + instances.oidcAuthMethodGlobal.authorized_actions = + instances.oidcAuthMethodGlobal.authorized_actions.filter( + (item) => item !== 'read' + ); + await visit(urls.globalScope); + + await click(`[href="${urls.globalAuthMethods}"]`); + + assert.dom(AUTH_LINK_SELECTOR).doesNotExist(); + }); + + test('cannot navigate to an ldap auth method form in global scope', async function (assert) { + assert.expect(3); + instances.ldapAuthMethod = this.server.create('auth-method', { + scope: instances.scopes.global, + type: TYPE_AUTH_METHOD_LDAP, + }); + await visit(urls.globalScope); + + await click(`[href="${urls.globalAuthMethods}"]`); + + assert.dom(LDAP_AUTH_LINK_SELECTOR).doesNotExist(); + assert.dom(LDAP_AUTH_TYPE_SELECTOR).hasText('LDAP'); + assert.dom(LDAP_AUTH_ACTIONS_SELECTOR).doesNotExist(); + }); + + test('cannot navigate to an ldap auth method form in org scope', async function (assert) { + assert.expect(3); + instances.ldapAuthMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + }); + await visit(urls.orgScope); + + await click(`[href="${urls.orgAuthMethods}"]`); + + assert.dom(LDAP_AUTH_LINK_SELECTOR).doesNotExist(); + assert.dom(LDAP_AUTH_TYPE_SELECTOR).hasText('LDAP'); + assert.dom(LDAP_AUTH_ACTIONS_SELECTOR).doesNotExist(); }); }); diff --git a/ui/admin/tests/unit/abilities/auth-method-test.js b/ui/admin/tests/unit/abilities/auth-method-test.js new file mode 100644 index 000000000..22cfa8699 --- /dev/null +++ b/ui/admin/tests/unit/abilities/auth-method-test.js @@ -0,0 +1,75 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_LDAP, + TYPE_AUTH_METHOD_OIDC, +} from 'api/models/auth-method'; + +module('Unit | Abilities | auth-method', function (hooks) { + setupTest(hooks); + + let canService; + let store; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + }); + + test('cannot read LDAP auth-method when authorized', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['read'], + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('cannot read LDAP auth-method when unauthorized', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('can read non-LDAP auth-method when authorized', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['read'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('read auth-method', authMethod)); + }); + + test('cannot read non-LDAP auth-method when unauthorized', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('cannot make LDAP auth-method primary', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.false(canService.can('makePrimary auth-method', authMethod)); + }); + + test('can make non-LDAP auth-method primary', function (assert) { + assert.expect(1); + const authMethod = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('makePrimary auth-method', authMethod)); + }); +}); From 7d909551ac45a0649bd11adc10b8f8dbe270cce9 Mon Sep 17 00:00:00 2001 From: Dharini Jeeva Date: Tue, 16 May 2023 09:37:53 -0700 Subject: [PATCH 05/14] chore: Use HDS components in auth methods list view (#1686) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 🤖 use hds table and dropdown --- addons/rose/addon/styles/hds/overrides.scss | 4 + ui/admin/app/styles/app.scss | 8 + .../scopes/scope/auth-methods/index.hbs | 184 +++++++++--------- .../acceptance/auth-methods/create-test.js | 24 ++- .../acceptance/auth-methods/read-test.js | 13 +- 5 files changed, 124 insertions(+), 109 deletions(-) diff --git a/addons/rose/addon/styles/hds/overrides.scss b/addons/rose/addon/styles/hds/overrides.scss index 354862697..7f862403b 100644 --- a/addons/rose/addon/styles/hds/overrides.scss +++ b/addons/rose/addon/styles/hds/overrides.scss @@ -24,6 +24,10 @@ [class^='hds-form-group'] { margin-bottom: 0; } + + .hds-table__td p { + color: var(--token-form-indicator-optional-color); + } } .hds-dropdown { diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 425d3ad0d..e90bda620 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -679,3 +679,11 @@ } /* stylelint-enable selector-class-pattern */ } + +.auth-methods-list-alert { + margin-bottom: 1rem; +} + +.auth-methods-list-table { + margin-top: 1rem; +} diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs index 31dac7bb6..c8e63f53d 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -41,6 +41,25 @@ + {{#if @model}} + {{#unless this.hasPrimaryAuthMethod}} + + + {{t 'resources.auth-method.messages.no-primary-auth-method.title'}} + + + {{t + 'resources.auth-method.messages.no-primary-auth-method.description' + }} + + + {{/unless}} + {{/if}} {{#if (or @model @@ -92,101 +111,80 @@ {{/if}} {{#if @model}} - {{#unless this.hasPrimaryAuthMethod}} - - {{t - 'resources.auth-method.messages.no-primary-auth-method.description' - }} - - {{/unless}} - - - - - {{t 'form.name.label'}} - {{t 'form.type.label'}} - {{t 'form.id.label'}} - - {{t 'titles.actions'}} - - - - - {{#each @model as |authMethod|}} - - - {{#if (can 'read auth-method' authMethod)}} - - {{authMethod.displayName}} - - {{else}} - {{authMethod.displayName}} - {{/if}} -

{{authMethod.description}}

-
- - - - - + <:body as |B|> + + + {{#if (can 'read auth-method' B.data)}} + - {{authMethod.id}} - - - - {{#if authMethod.isPrimary}} - - {{/if}} - - - {{#if (can 'makePrimary auth-method' authMethod)}} - + {{else}} + {{B.data.displayName}} + {{/if}} +

{{B.data.description}}

+ + + + + + + {{B.data.id}} + + + + {{#if B.data.isPrimary}} + + {{/if}} + + + {{#if (can 'makePrimary auth-method' B.data)}} + + - {{#unless authMethod.isPrimary}} - - {{t 'resources.auth-method.actions.make-primary'}} - - {{/unless}} - {{#if authMethod.isPrimary}} - - {{t 'resources.auth-method.actions.remove-as-primary'}} - - {{/if}} -
- {{/if}} -
-
- {{/each}} -
-
+ @hasChevron={{false}} + /> + {{#unless B.data.isPrimary}} + + {{/unless}} + {{#if B.data.isPrimary}} + + {{/if}} + + {{/if}} + + + + {{else if (has-resource-filter-selections 'scopes.scope.auth-methods' 'type' diff --git a/ui/admin/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index 5f4f38dfb..8f292d4ce 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -22,10 +22,13 @@ import { // //invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | auth-methods | create ', function (hooks) { +module('Acceptance | auth-methods | create', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - + const DROPDOWN_SELECTOR_ICON = + 'tbody .hds-table__tr:nth-child(1) .hds-table__td:last-child .hds-dropdown-toggle-icon'; + const DROPDOWN_SELECTOR_OPTION = + '.hds-dropdown__content .hds-dropdown-list-item [type=button]'; let getAuthMethodsCount; const instances = { scopes: { @@ -255,24 +258,27 @@ module('Acceptance | auth-methods | create ', function (hooks) { }); test('user can make and remove primary auth methods from index', async function (assert) { - assert.expect(3); + assert.expect(4); assert.notOk( instances.orgScope.primaryAuthMethodId, 'Primary auth method is not yet set.' ); await visit(urls.authMethods); - await click( - '.rose-table-body .rose-table-row:first-child .rose-dropdown-content [type="button"]:first-child' - ); + + await click(DROPDOWN_SELECTOR_ICON); + assert.dom(DROPDOWN_SELECTOR_OPTION).exists(); + await click(DROPDOWN_SELECTOR_OPTION); + let scope = this.server.schema.scopes.find(instances.orgScope.id); + assert.strictEqual( scope.primaryAuthMethodId, instances.authMethod.id, 'Primary auth method is set.' ); - await click( - '.rose-table-body .rose-table-row:first-child .rose-dropdown-content [type="button"]:first-child' - ); + await click(DROPDOWN_SELECTOR_ICON); + await click(DROPDOWN_SELECTOR_OPTION); + scope = this.server.schema.scopes.find(instances.orgScope.id); assert.notOk(scope.primaryAuthMethodId, 'Primary auth method is unset.'); }); diff --git a/ui/admin/tests/acceptance/auth-methods/read-test.js b/ui/admin/tests/acceptance/auth-methods/read-test.js index 72ff3c2b9..b46e82272 100644 --- a/ui/admin/tests/acceptance/auth-methods/read-test.js +++ b/ui/admin/tests/acceptance/auth-methods/read-test.js @@ -23,17 +23,16 @@ import { module('Acceptance | auth methods | read', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - const AUTH_LINK_SELECTOR = - 'main tbody .rose-table-header-cell:nth-child(1) a'; + const AUTH_LINK_SELECTOR = 'main tbody .hds-table__td:nth-child(1) a'; const AUTH_TYPE_SELECTOR = - 'tbody .rose-table-row:nth-child(1) .rose-table-cell'; + 'tbody .hds-table__tr:nth-child(1) .hds-table__td:nth-child(2)'; const AUTH_ACTIONS_SELECTOR = - 'tbody .rose-table-row:nth-child(1) .rose-table-cell:last-child .rose-dropdown'; - const LDAP_AUTH_LINK_SELECTOR = 'tbody .rose-table-row:nth-child(2) a'; + 'tbody .hds-table__tr:nth-child(1) .hds-table__td:last-child .hds-dropdown'; + const LDAP_AUTH_LINK_SELECTOR = 'tbody .hds-table__tr:nth-child(2) a'; const LDAP_AUTH_TYPE_SELECTOR = - 'tbody .rose-table-row:nth-child(2) .rose-table-cell'; + 'tbody .hds-table__tr:nth-child(2) .hds-table__td:nth-child(2)'; const LDAP_AUTH_ACTIONS_SELECTOR = - 'tbody .rose-table-row:nth-child(2) .rose-table-cell:last-child .rose-dropdown'; + 'tbody .hds-table__tr:nth-child(2) .hds-table__td:last-child .hds-dropdown'; const instances = { scopes: { From e8a10b313135baf91a18066cf45f3b36bfee54df Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Tue, 16 May 2023 16:10:38 -0700 Subject: [PATCH 06/14] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20ldap-auth-me?= =?UTF-8?q?thods=20feature=20flag=20(#1690)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/config/features.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/admin/config/features.js b/ui/admin/config/features.js index 9a4382ec9..435c83939 100644 --- a/ui/admin/config/features.js +++ b/ui/admin/config/features.js @@ -27,6 +27,7 @@ const baseEdition = { 'target-worker-filters-v2-hcp': false, 'target-network-address': false, 'vault-worker-filter': false, + 'ldap-auth-methods': false, }; // Editions maps edition keys to their associated featuresets. const featureEditions = {}; @@ -37,6 +38,7 @@ featureEditions.oss = { 'static-credentials': true, 'target-worker-filters-v2': true, 'target-network-address': true, + 'ldap-auth-methods': false, }; featureEditions.enterprise = { ...featureEditions.oss, From 6dd0423923dc66282f44c46257102840c0348504 Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Wed, 17 May 2023 14:31:12 -0700 Subject: [PATCH 07/14] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20account=20and?= =?UTF-8?q?=20auth-method=20abilities=20(#1691)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 account and auth-method abilities * refactor: 💡 add update & delete abilities & unit tests --- addons/api/addon/abilities/account.js | 24 ++++ addons/api/addon/abilities/auth-method.js | 14 ++ addons/api/addon/models/account.js | 31 ++++- addons/api/app/abilities/auth-method.js | 6 + addons/api/mirage/factories/account.js | 10 +- .../api/tests/unit/abilities/account-test-.js | 22 ---- .../api/tests/unit/abilities/account-test.js | 124 ++++++++++++++++++ .../tests/unit/abilities/auth-method-test.js | 114 ++++++++++++++++ addons/api/tests/unit/models/account-test.js | 83 +++++++++++- .../api/tests/unit/models/auth-method-test.js | 8 +- ui/admin/app/abilities/account.js | 25 ++++ ui/admin/app/abilities/auth-method.js | 10 +- ui/admin/tests/unit/abilities/account-test.js | 80 +++++++++++ .../tests/unit/abilities/auth-method-test.js | 51 +++++-- 14 files changed, 551 insertions(+), 51 deletions(-) create mode 100644 addons/api/app/abilities/auth-method.js delete mode 100644 addons/api/tests/unit/abilities/account-test-.js create mode 100644 addons/api/tests/unit/abilities/account-test.js create mode 100644 addons/api/tests/unit/abilities/auth-method-test.js create mode 100644 ui/admin/app/abilities/account.js create mode 100644 ui/admin/tests/unit/abilities/account-test.js diff --git a/addons/api/addon/abilities/account.js b/addons/api/addon/abilities/account.js index 8d741f157..03ab318a5 100644 --- a/addons/api/addon/abilities/account.js +++ b/addons/api/addon/abilities/account.js @@ -11,6 +11,30 @@ import ModelAbility from './model'; export default class AccountAbility extends ModelAbility { // =permissions + /** + * Only "known" account types may be read. + * @type {boolean} + */ + get canRead() { + return !this.model.isUnknown && this.hasAuthorizedAction('read'); + } + + /** + * Only "known" account types may be updated. + * @type {boolean} + */ + get canUpdate() { + return !this.model.isUnknown && this.hasAuthorizedAction('update'); + } + + /** + * Only "known" account types may be deleted. + * @type {boolean} + */ + get canDelete() { + return !this.model.isUnknown && this.hasAuthorizedAction('delete'); + } + /** * @type {boolean} */ diff --git a/addons/api/addon/abilities/auth-method.js b/addons/api/addon/abilities/auth-method.js index 744f93ac7..421fca156 100644 --- a/addons/api/addon/abilities/auth-method.js +++ b/addons/api/addon/abilities/auth-method.js @@ -17,4 +17,18 @@ export default class AuthMethodAbility extends ModelAbility { get canRead() { return !this.model.isUnknown && this.hasAuthorizedAction('read'); } + + /** + * Only "known" auth method types may be updated. + */ + get canUpdate() { + return !this.model.isUnknown && this.hasAuthorizedAction('update'); + } + + /** + * Only "known" auth method types may be deleted. + */ + get canDelete() { + return !this.model.isUnknown && this.hasAuthorizedAction('delete'); + } } diff --git a/addons/api/addon/models/account.js b/addons/api/addon/models/account.js index 87aacf331..4607433e7 100644 --- a/addons/api/addon/models/account.js +++ b/addons/api/addon/models/account.js @@ -4,8 +4,12 @@ */ import GeneratedAccountModel from '../generated/models/account'; -import { computed } from '@ember/object'; -import { equal } from '@ember/object/computed'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, + TYPES_AUTH_METHOD, +} from './auth-method'; export default class AccountModel extends GeneratedAccountModel { // =attributes @@ -13,18 +17,35 @@ export default class AccountModel extends GeneratedAccountModel { /** * @type {boolean} */ - @equal('type', 'password') isPassword; + get isPassword() { + return this.type === TYPE_AUTH_METHOD_PASSWORD; + } /** * @type {boolean} */ - @equal('type', 'oidc') isOIDC; + get isOIDC() { + return this.type === TYPE_AUTH_METHOD_OIDC; + } + + /** + * @type {boolean} + */ + get isLDAP() { + return this.type === TYPE_AUTH_METHOD_LDAP; + } + + /** + * @type {boolean} + */ + get isUnknown() { + return !TYPES_AUTH_METHOD.includes(this.type); + } /** * Convenience for getting username in account. * @type {string} */ - @computed('{email,full_name,login_name}') get accountName() { const { email, full_name, login_name } = this; return email || full_name || login_name; diff --git a/addons/api/app/abilities/auth-method.js b/addons/api/app/abilities/auth-method.js new file mode 100644 index 000000000..f81e174f4 --- /dev/null +++ b/addons/api/app/abilities/auth-method.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'api/abilities/auth-method'; diff --git a/addons/api/mirage/factories/account.js b/addons/api/mirage/factories/account.js index ee46ac64e..176e0786d 100644 --- a/addons/api/mirage/factories/account.js +++ b/addons/api/mirage/factories/account.js @@ -7,6 +7,11 @@ import factory from '../generated/factories/account'; import { faker } from '@faker-js/faker'; import permissions from '../helpers/permissions'; import generateId from '../helpers/id'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; export default factory.extend({ authorized_actions: () => @@ -22,9 +27,10 @@ export default factory.extend({ attributes() { switch (this.type) { - case 'password': + case TYPE_AUTH_METHOD_PASSWORD: + case TYPE_AUTH_METHOD_LDAP: return { login_name: faker.random.words() }; - case 'oidc': + case TYPE_AUTH_METHOD_OIDC: return { issuer: faker.internet.ip(), subject: 'sub', diff --git a/addons/api/tests/unit/abilities/account-test-.js b/addons/api/tests/unit/abilities/account-test-.js deleted file mode 100644 index a631d7518..000000000 --- a/addons/api/tests/unit/abilities/account-test-.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Abilities | Account', function (hooks) { - setupTest(hooks); - - test('it reflects when a given account may set password based on authorized_actions', function (assert) { - assert.expect(2); - const service = this.owner.lookup('service:can'); - const model = { - authorized_actions: ['set-password'], - }; - assert.ok(service.can('setPassword target', model)); - model.authorized_actions = []; - assert.notOk(service.can('setPassword target', model)); - }); -}); diff --git a/addons/api/tests/unit/abilities/account-test.js b/addons/api/tests/unit/abilities/account-test.js new file mode 100644 index 000000000..8c7824ff8 --- /dev/null +++ b/addons/api/tests/unit/abilities/account-test.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; + +module('Unit | Abilities | Account', function (hooks) { + setupTest(hooks); + + let canService; + let store; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + }); + + test('it reflects when a given account may set password based on authorized_actions', function (assert) { + assert.expect(2); + const account = store.createRecord('auth-method', { + authorized_actions: ['set-password'], + }); + assert.true(canService.can('setPassword account', account)); + account.authorized_actions = []; + assert.false(canService.can('setPassword account', account)); + }); + + test('can read known account types when authorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: ['read'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('read account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('read account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('read account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('read account', account)); + }); + + test('cannot read accounts when unauthorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.false(canService.can('read account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false(canService.can('read account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.false(canService.can('read account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('read account', account)); + }); + + test('can delete known account types when authorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: ['delete'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('delete account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('delete account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('delete account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('delete account', account)); + }); + + test('cannot delete accounts when unauthorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: ['delete'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('delete account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('delete account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('delete account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('delete account', account)); + }); + + test('can update known account types when authorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: ['update'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('update account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('update account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('update account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('update account', account)); + }); + + test('cannot update accounts when unauthorized', function (assert) { + assert.expect(4); + const account = store.createRecord('account', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.false(canService.can('update account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false(canService.can('update account', account)); + account.type = TYPE_AUTH_METHOD_LDAP; + assert.false(canService.can('update account', account)); + account.type = 'no-such-type'; + assert.false(canService.can('update account', account)); + }); +}); diff --git a/addons/api/tests/unit/abilities/auth-method-test.js b/addons/api/tests/unit/abilities/auth-method-test.js new file mode 100644 index 000000000..ade783e69 --- /dev/null +++ b/addons/api/tests/unit/abilities/auth-method-test.js @@ -0,0 +1,114 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; + +module('Unit | Abilities | Auth Method', function (hooks) { + setupTest(hooks); + + let canService; + let store; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + }); + + test('can read known auth method types when authorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['read'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('read auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('cannot read auth methods when unauthorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.false(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.false(canService.can('read auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('can delete known auth method types when authorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['delete'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('delete auth-method', authMethod)); + }); + + test('cannot delete auth methods when unauthorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['delete'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('delete auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('delete auth-method', authMethod)); + }); + + test('can update known auth method types when authorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['update'], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('update auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('update auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.true(canService.can('update auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('update auth-method', authMethod)); + }); + + test('cannot update auth methods when unauthorized', function (assert) { + assert.expect(4); + const authMethod = store.createRecord('auth-method', { + authorized_actions: [], + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.false(canService.can('update auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false(canService.can('update auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_LDAP; + assert.false(canService.can('update auth-method', authMethod)); + authMethod.type = 'no-such-type'; + assert.false(canService.can('update auth-method', authMethod)); + }); +}); diff --git a/addons/api/tests/unit/models/account-test.js b/addons/api/tests/unit/models/account-test.js index 9dfc427a4..71f5db4bc 100644 --- a/addons/api/tests/unit/models/account-test.js +++ b/addons/api/tests/unit/models/account-test.js @@ -5,19 +5,30 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; module('Unit | Model | account', function (hooks) { setupTest(hooks); + let store; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:store'); + }); + test('it exists', function (assert) { - let store = this.owner.lookup('service:store'); + store = this.owner.lookup('service:store'); let model = store.createRecord('account', {}); assert.ok(model); }); test('it has an accountName attribute for password account', function (assert) { assert.expect(2); - const store = this.owner.lookup('service:store'); + store = this.owner.lookup('service:store'); let model = store.createRecord('account', { type: 'password', }); @@ -25,4 +36,72 @@ module('Unit | Model | account', function (hooks) { model.login_name = 'foobar'; assert.strictEqual(model.accountName, 'foobar'); }); + + test('it has isPassword property and returns the expected values', function (assert) { + assert.expect(3); + const modelA = store.createRecord('account', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelB = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelC = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(modelA.isPassword); + assert.false(modelB.isPassword); + assert.false(modelC.isPassword); + }); + + test('it has isOIDC property and returns the expected values', function (assert) { + assert.expect(3); + const modelA = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelB = store.createRecord('account', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(modelA.isOIDC); + assert.false(modelB.isOIDC); + assert.false(modelC.isOIDC); + }); + + test('it has isLDAP property and returns the expected values', function (assert) { + assert.expect(3); + const modelA = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + const modelB = store.createRecord('account', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(modelA.isLDAP); + assert.false(modelB.isLDAP); + assert.false(modelC.isLDAP); + }); + + test('it has isUnknown property and returns the expected values', function (assert) { + assert.expect(4); + const modelA = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + const modelB = store.createRecord('account', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); + const modelC = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + const modelD = store.createRecord('account', { + type: 'no-such-type', + }); + assert.false(modelA.isUnknown); + assert.false(modelB.isUnknown); + assert.false(modelC.isUnknown); + assert.true(modelD.isUnknown); + }); }); diff --git a/addons/api/tests/unit/models/auth-method-test.js b/addons/api/tests/unit/models/auth-method-test.js index a908d2280..3ff01039d 100644 --- a/addons/api/tests/unit/models/auth-method-test.js +++ b/addons/api/tests/unit/models/auth-method-test.js @@ -61,7 +61,7 @@ module('Unit | Model | auth method', function (hooks) { await model.changeState('foobar'); }); - test('it has isPassword property and returns the expected values', async function (assert) { + test('it has isPassword property and returns the expected values', function (assert) { assert.expect(3); const modelA = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_PASSWORD, @@ -77,7 +77,7 @@ module('Unit | Model | auth method', function (hooks) { assert.false(modelC.isPassword); }); - test('it has isOIDC property and returns the expected values', async function (assert) { + test('it has isOIDC property and returns the expected values', function (assert) { assert.expect(3); const modelA = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_OIDC, @@ -93,7 +93,7 @@ module('Unit | Model | auth method', function (hooks) { assert.false(modelC.isOIDC); }); - test('it has isLDAP property and returns the expected values', async function (assert) { + test('it has isLDAP property and returns the expected values', function (assert) { assert.expect(3); const modelA = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_LDAP, @@ -109,7 +109,7 @@ module('Unit | Model | auth method', function (hooks) { assert.false(modelC.isLDAP); }); - test('it has isUnknown property and returns the expected values', async function (assert) { + test('it has isUnknown property and returns the expected values', function (assert) { assert.expect(4); const modelA = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_LDAP, diff --git a/ui/admin/app/abilities/account.js b/ui/admin/app/abilities/account.js new file mode 100644 index 000000000..5f6fcec87 --- /dev/null +++ b/ui/admin/app/abilities/account.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import AccountAbility from 'api/abilities/account'; +import { inject as service } from '@ember/service'; + +export default class OverrideAccountAbility extends AccountAbility { + // =service + @service features; + + /** + * These override abilities are temporary and just part of Phase 1 + * of LDAP auth method implementation. + */ + + get canAddAccount() { + return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP; + } + + get canRemoveAccount() { + return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP; + } +} diff --git a/ui/admin/app/abilities/auth-method.js b/ui/admin/app/abilities/auth-method.js index ebbf9b0cd..61ce714ea 100644 --- a/ui/admin/app/abilities/auth-method.js +++ b/ui/admin/app/abilities/auth-method.js @@ -4,18 +4,24 @@ */ import AuthMethodAbility from 'api/abilities/auth-method'; +import { inject as service } from '@ember/service'; export default class OverrideAuthMethodAbility extends AuthMethodAbility { + //service + @service features; + /** * These override abilities are temporary and just part of Phase 1 * of LDAP auth method implementation. */ get canRead() { - return !this.model.isLDAP && super.canRead; + return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP + ? super.canRead + : false; } get canMakePrimary() { - return !this.model.isLDAP; + return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP; } } diff --git a/ui/admin/tests/unit/abilities/account-test.js b/ui/admin/tests/unit/abilities/account-test.js new file mode 100644 index 000000000..914bacaaa --- /dev/null +++ b/ui/admin/tests/unit/abilities/account-test.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_LDAP, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_PASSWORD, +} from 'api/models/auth-method'; + +module('Unit | Abilities | account', function (hooks) { + setupTest(hooks); + + let canService; + let store; + let features; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + features = this.owner.lookup('service:features'); + }); + + test('can add ldap account to user when feature flag enabled', function (assert) { + assert.expect(1); + features.enable('ldap-auth-methods'); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(canService.can('addAccount account', account)); + }); + + test('can add non-ldap account to user', function (assert) { + assert.expect(2); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('addAccount account', account)); + account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('addAccount account', account)); + }); + + test('cannot add ldap account to user when feature flag disabled', function (assert) { + assert.expect(1); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.false(canService.can('addAccount account', account)); + }); + + test('can remove ldap account from user when feature flag enabled', function (assert) { + assert.expect(1); + features.enable('ldap-auth-methods'); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(canService.can('removeAccount account', account)); + }); + + test('cannot remove ldap account from user when feature flag disabled', function (assert) { + assert.expect(1); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.false(canService.can('removeAccount account', account)); + }); + + test('can remove non-ldap account from user', function (assert) { + assert.expect(2); + const account = store.createRecord('account', { + type: TYPE_AUTH_METHOD_OIDC, + }); + assert.true(canService.can('removeAccount account', account)); + (account.type = TYPE_AUTH_METHOD_PASSWORD), + assert.true(canService.can('removeAccount account', account)); + }); +}); diff --git a/ui/admin/tests/unit/abilities/auth-method-test.js b/ui/admin/tests/unit/abilities/auth-method-test.js index 22cfa8699..66888a231 100644 --- a/ui/admin/tests/unit/abilities/auth-method-test.js +++ b/ui/admin/tests/unit/abilities/auth-method-test.js @@ -8,6 +8,7 @@ import { setupTest } from 'ember-qunit'; import { TYPE_AUTH_METHOD_LDAP, TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_PASSWORD, } from 'api/models/auth-method'; module('Unit | Abilities | auth-method', function (hooks) { @@ -15,49 +16,60 @@ module('Unit | Abilities | auth-method', function (hooks) { let canService; let store; + let features; hooks.beforeEach(function () { canService = this.owner.lookup('service:can'); store = this.owner.lookup('service:store'); + features = this.owner.lookup('service:features'); }); - test('cannot read LDAP auth-method when authorized', function (assert) { - assert.expect(1); + test('can read LDAP auth-method when authorized and feature flag enabled', function (assert) { + assert.expect(2); + features.enable('ldap-auth-methods'); + const authMethod = store.createRecord('auth-method', { + authorized_actions: ['read'], + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(canService.can('read auth-method', authMethod)); + authMethod.authorized_actions = []; + assert.false(canService.can('read auth-method', authMethod)); + }); + + test('cannot read LDAP auth-method when authorized and feature flag disabled', function (assert) { + assert.expect(2); const authMethod = store.createRecord('auth-method', { authorized_actions: ['read'], type: TYPE_AUTH_METHOD_LDAP, }); assert.false(canService.can('read auth-method', authMethod)); - }); - - test('cannot read LDAP auth-method when unauthorized', function (assert) { - assert.expect(1); - const authMethod = store.createRecord('auth-method', { - authorized_actions: [], - type: TYPE_AUTH_METHOD_LDAP, - }); + authMethod.authorized_actions = []; assert.false(canService.can('read auth-method', authMethod)); }); test('can read non-LDAP auth-method when authorized', function (assert) { - assert.expect(1); + assert.expect(2); const authMethod = store.createRecord('auth-method', { authorized_actions: ['read'], type: TYPE_AUTH_METHOD_OIDC, }); assert.true(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('read auth-method', authMethod)); }); test('cannot read non-LDAP auth-method when unauthorized', function (assert) { - assert.expect(1); + assert.expect(2); const authMethod = store.createRecord('auth-method', { authorized_actions: [], type: TYPE_AUTH_METHOD_OIDC, }); assert.false(canService.can('read auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false(canService.can('read auth-method', authMethod)); }); - test('cannot make LDAP auth-method primary', function (assert) { + test('cannot make LDAP auth-method primary when feature flag disabled', function (assert) { assert.expect(1); const authMethod = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_LDAP, @@ -65,11 +77,22 @@ module('Unit | Abilities | auth-method', function (hooks) { assert.false(canService.can('makePrimary auth-method', authMethod)); }); - test('can make non-LDAP auth-method primary', function (assert) { + test('can make LDAP auth-method primary when feature flag enabled', function (assert) { assert.expect(1); + features.enable('ldap-auth-methods'); + const authMethod = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); + assert.true(canService.can('makePrimary auth-method', authMethod)); + }); + + test('can make non-LDAP auth-method primary', function (assert) { + assert.expect(2); const authMethod = store.createRecord('auth-method', { type: TYPE_AUTH_METHOD_OIDC, }); assert.true(canService.can('makePrimary auth-method', authMethod)); + authMethod.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true(canService.can('makePrimary auth-method', authMethod)); }); }); From e6664b882faecacc4e9ffa64d18dedcb14534d61 Mon Sep 17 00:00:00 2001 From: Devon Anderson Date: Fri, 19 May 2023 15:59:49 -0400 Subject: [PATCH 08/14] =?UTF-8?q?fix:=20=F0=9F=90=9B=20fix=20descriptions?= =?UTF-8?q?=20for=20targets=20list=20for=20desktop=20(#1692)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Target descriptions where not displaying due to some previous refactoring. Corrected `description` and `isActive` references. ✅ Closes: ICU-9375 --- .../app/templates/scopes/scope/projects/targets/index.hbs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs index a39a6d1b3..51414095b 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs @@ -121,10 +121,10 @@ {{else}} {{model.displayName}} {{/if}} - {{#if model.target.description}} -

{{model.target.description}}

+ {{#if model.description}} +

{{model.description}}

{{/if}} - {{#if model.target.isActive}} + {{#if model.isActive}} Date: Fri, 19 May 2023 16:10:55 -0700 Subject: [PATCH 09/14] e2e Test documentation for FE onboarding (#1681) * Add e2e documentation to faciltiate FE devs onboarding Co-authored-by: Michael Li Co-authored-by: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> --------- Co-authored-by: Michael Li Co-authored-by: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> --- ui/admin/package.json | 2 +- ui/admin/tests/e2e/README.md | 135 +++++++++++++++++++++++++++++++---- 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/ui/admin/package.json b/ui/admin/package.json index 137d535d9..1e7f567d6 100644 --- a/ui/admin/package.json +++ b/ui/admin/package.json @@ -10,7 +10,7 @@ "test": "tests" }, "scripts": { - "doc:toc": "doctoc README.md", + "doc:toc": "doctoc README.md tests/e2e/README.md", "build:development": "ember build", "build": "ember build --environment=production", "build:oss": "EDITION=oss ember build --environment=production", diff --git a/ui/admin/tests/e2e/README.md b/ui/admin/tests/e2e/README.md index 9560a2109..e6de7407c 100644 --- a/ui/admin/tests/e2e/README.md +++ b/ui/admin/tests/e2e/README.md @@ -1,19 +1,59 @@ + + + +# Admin UI e2e tests + +- [Admin UI e2e tests](#admin-ui-e2e-tests) + - [Prerequisites:](#prerequisites) + - [Accesses](#accesses) + - [Software](#software) + - [Getting Started](#getting-started) + - [Set up Boundary CLI](#set-up-boundary-cli) + - [Setup Boundary UI](#setup-boundary-ui) + - [Setup AWS:](#setup-aws) + - [Setup HCP Terraform (Terraform cloud):](#setup-hcp-terraform-terraform-cloud) + - [Setup Enos:](#setup-enos) + - [Run tests:](#run-tests) + - [Launch Enos Scenario](#launch-enos-scenario) + - [Run tests](#run-tests) + - [Destroy Enos Scenario](#destroy-enos-scenario) + - [Developing Tests](#developing-tests) + + # Admin UI e2e tests This test suite tests the Boundary Admin UI in an end-to-end setting. It was designed to be run in a variety of environments as long as the appropriate environment variables are set. You can choose to spin up your own infrastructure or use [Enos](https://github.com/hashicorp/boundary/tree/main/enos) -to generate it for you. +to generate it for you. More information about [Enos available here.](https://github.com/hashicorp/Enos-Docs) The test suite uses the [Playwright](https://playwright.dev/) framework. +## Prerequisites: + +You will need [Homebrew](https://brew.sh/) install. For secure persisting your SSH keys and tokens we recommend using 1Password. + +### Accesses + +If you are missing any acccess, requested [through the IT service catalog](https://hashicorp.freshservice.com/support/catalog/items). + +- Doormat account: `boundary_team_acctest_dev`. +- HCP account: you will need to use Terraform cloud to spin up enos. + +### Software + +- Vault: [documentation how to install](https://developer.hashicorp.com/vault/tutorials/getting-started/getting-started-install). +- Terraform: [documentation how to install](https://developer.hashicorp.com/terraform/downloads). +- Github CLI: [documentation how to install](https://cli.github.com/manual/installation). +- Install Enos: [documentation how to install using homebrew](https://github.com/hashicorp/Enos-Docs/blob/main/installation.md), also binaries available [here](https://github.com/hashicorp/enos/releases). +- Install Doormat CLI: [documentation how to install](https://docs.prod.secops.hashicorp.services/doormat/cli/). +- Install AWS CLI (Optional): [documentation how to install](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). + ## Getting Started -Setup... -```bash -cd boundary-ui/ui/admin -yarn install -npx playwright install # this installs the browsers used by Playwright -``` + +We will cover everything you need to set up before being able to run Enos + the tests. + +### Set up Boundary CLI Ensure the `boundary` CLI is available on the path. You can validate this by making sure this command returns something. @@ -21,16 +61,73 @@ command returns something. which boundary ``` -Run tests... +Otherwise, follow instructions to [install Boundary](https://developer.hashicorp.com/boundary/downloads?product_intent=boundary) or install it locally running `$ make install` within [Boundary repository](https://github.com/hashicorp/boundary/tree/main). + +### Setup Boundary UI + +Ensure you install project dependencies and playwright needs. + ```bash -export BOUNDARY_ADDR= -export E2E_PASSWORD_ADMIN_LOGIN_NAME= -export E2E_PASSWORD_ADMIN_PASSWORD= -export E2E_PASSWORD_AUTH_METHOD_ID= -... # Tests may have additional variables that it needs cd boundary-ui/ui/admin -yarn run e2e +yarn install +npx playwright install # this installs the browsers used by Playwright ``` +### Setup AWS: + +**Region awareness:** take note of the AWS region you are setting up because we will need it later to configure enos. [More information about AWS regions](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html). + +**SSH Key pair:** + +You need to provide an SSH Key pair for the EC2 instance. We recommend creating specific e2e SSH keys and not re-use existing ones. To differentiate them, we recommend adding `enos` to the name, i.e: `name_enos`. + +[How to create EC2 SSH key pair](https://docs.aws.amazon.com/ground-station/latest/ug/create-ec2-ssh-key-pair.html). + +### Setup HCP Terraform (Terraform cloud): + +Log in to [HCP Production env](https://portal.cloud.hashicorp.com/). Through HCP Terraform, open Terraform cloud, or [access here](https://app.terraform.io/). Then create an API token [following this documentation](https://developer.hashicorp.com/terraform/cloud-docs/users-teams-organizations/users#tokens). + +**Token awareness:** Copy your token from the box and save it in a secure location. Terraform Cloud only displays the token once, right after you create it. And we will need the token later for enos configuration. + +### Setup Enos: + +Enos needs some configuration variables to run the scenario successfully. [See the configuration file](https://github.com/hashicorp/boundary/blob/main/enos/enos.vars.hcl). The file has comments per each variable, but some awareness: + +- `aws_region`: The AWS region you are using. Very important as mentioned within the EC2 Setup. +- `aws_ssh_keypair_name`: The name of the AWS keypair. +- `aws_ssh_private_key_path`: The path to the private key associated with your keypair. +- `enos_user`: The user name to use for Boundary. +- `tfc_api_token`: you need to provide the previously created token in Terraform cloud, there is no shared token within Boundary team. +- `local_boundary_dir`: The directory that contains Boundary binary. +- `local_boundary_ui_dir`: The directory that contains the copy of boundary-ui you want to use for UI tests. +- `e2e_debug_no_run`: make sure this is set to true. + +More documentation about [scenario variables](https://github.com/hashicorp/boundary/tree/main/enos#scenarios-variables). + +## Run tests: + +Before running the e2e test locally, we need to launch Enos Scenario, make sure you followed all the steps within the [Getting started section](#getting-started). + +It is not necessary, but from this point we recommend having 2 terminals open.  +- Terminal 1: Will be use to run enos (Boundary). +- Terminal 2: Will be use to run e2e UI tests (Boundary UI). + +### Launch Enos Scenario + +Using Terminal 1: +- `$ cd boundary/enos`. +- `$ doormat login`. Login with Doormat. +- `$ eval "$(doormat aws export --account boundary_team_acctest_dev)"`. Exporting AWS env variables from doormat to your terminal. +- `$ enos scenario launch e2e_ui builder:local`. Launch enos scenario, this will take from 5 to 10 minutes. When its done, you will see a Enos Operations finished! within your terminal. +- `$ bash scripts/test_e2e_env.sh`. Prints all the env variables within Enos scenario. Copy the output and paste it within your Terminal 2 (Boundary UI). These env variables are need within Boundary UI to run the test against the enos scenario. + +*Be aware once the scenario is launch you will create and run resources within AWS, once you are done using the scenario, [you should destroy it](#destroy-enos-scenario).* + +### Run tests + +Using Terminal 2: +- Set the env varibales `test_e2e_env.sh` script output within this terminal. +- `$ cd boundary-ui/ui/admin`. +- `$ yarn run e2e`. Here are some additional commands to assist with debugging. ```bash @@ -39,6 +136,16 @@ PWDEBUG=console yarn playwright test --headed --config ./tests/e2e/playwright.co PWDEBUG=console yarn playwright test --headed --config ./tests/e2e/playwright.config.js login.spec.js --debug ``` +[Playwright documentation about running tests](https://playwright.dev/docs/running-tests). + +### Destroy Enos Scenario + +Using Terminal 1: +- `$ enos scenario destroy`. +- After all the steps pass, you should see a `Enos operations finished!`. + + + ## Developing Tests It is recommended to use locators that resemble how users interact with the application. `getByRole` From fe81a626fa7c30647f6966cbadc56ac63954d5bc Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Mon, 22 May 2023 08:38:01 -0700 Subject: [PATCH 10/14] Chore icu 9071 audit resolutions (#1693) * Delete cryptiles from resolutions * Delete prismjs from global resolutions and add a specific resolution * Delete npm-user-validate from global resolutions --- package.json | 6 ++---- yarn.lock | 21 +-------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index ee73cb337..097a543f7 100755 --- a/package.json +++ b/package.json @@ -50,9 +50,6 @@ }, "resolutions": { "request": "^2.88.0", - "cryptiles": "~4.1.2", - "prismjs": "^1.27.0", - "npm-user-validate": "^1.0.1", "caniuse-lite": "^1.0.30001157", "immer": "^9.0.6", "node-notifier": "^8.0.1", @@ -101,7 +98,8 @@ "loader-utils": "^2.0.4", "**/@storybook/**/minimatch": "^3.0.5", "**/ember-electron/**/minimatch": "^3.0.5", - "**/babel-core/json5": "^2.2.2" + "**/babel-core/json5": "^2.2.2", + "**/react-syntax-highlighter/refractor/prismjs": "^1.27.0" }, "config": { "commitizen": { diff --git a/yarn.lock b/yarn.lock index e400fa5b3..752a1568b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9336,13 +9336,6 @@ boolean@^3.1.4: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== -boom@7.x.x: - version "7.3.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9" - integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A== - dependencies: - hoek "6.x.x" - bower-config@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/bower-config/-/bower-config-1.4.3.tgz#3454fecdc5f08e7aa9cc6d556e492be0669689ae" @@ -11392,13 +11385,6 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cryptiles@~4.1.2: - version "4.1.3" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-4.1.3.tgz#2461d3390ea0b82c643a6ba79f0ed491b0934c25" - integrity sha512-gT9nyTMSUC1JnziQpPbxKGBbUg8VL7Zn2NB4E1cJYvuXdElHrwxrV9bmltZGDzet45zSDGyYceueke1TjynGzw== - dependencies: - boom "7.x.x" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -16412,7 +16398,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoek@6.x.x, hoek@~4.2.0: +hoek@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== @@ -20268,11 +20254,6 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -npm-user-validate@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz#31428fc5475fe8416023f178c0ab47935ad8c561" - integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== - npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" From 379f317d8b03e573cc2e35ccde06b040d8dc1862 Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Mon, 22 May 2023 11:16:48 -0700 Subject: [PATCH 11/14] Refactor account and user abilities (#1694) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 add abilities to user and remove from account --- addons/api/addon/abilities/user.js | 16 ++ addons/api/tests/unit/abilities/user-test.js | 154 ++++++++++++++++-- .../app/abilities/{account.js => user.js} | 12 +- ui/admin/tests/unit/abilities/account-test.js | 80 --------- ui/admin/tests/unit/abilities/user-test.js | 109 +++++++++++++ 5 files changed, 269 insertions(+), 102 deletions(-) rename ui/admin/app/abilities/{account.js => user.js} (67%) delete mode 100644 ui/admin/tests/unit/abilities/account-test.js create mode 100644 ui/admin/tests/unit/abilities/user-test.js diff --git a/addons/api/addon/abilities/user.js b/addons/api/addon/abilities/user.js index bda0003cf..2e5d7a740 100644 --- a/addons/api/addon/abilities/user.js +++ b/addons/api/addon/abilities/user.js @@ -24,4 +24,20 @@ export default class UserAbility extends ModelAbility { get canRemoveAccounts() { return this.hasAuthorizedAction('remove-accounts'); } + + /** + * @type {boolean} + */ + get canAddAccount() { + const { account, canAddAccounts } = this; + return canAddAccounts && !account.isUnknown; + } + + /** + * @type {boolean} + */ + get canRemoveAccount() { + const { account, canRemoveAccounts } = this; + return canRemoveAccounts && !account.isUnknown; + } } diff --git a/addons/api/tests/unit/abilities/user-test.js b/addons/api/tests/unit/abilities/user-test.js index 06c551fdf..e15f94994 100644 --- a/addons/api/tests/unit/abilities/user-test.js +++ b/addons/api/tests/unit/abilities/user-test.js @@ -5,29 +5,147 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_LDAP, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_PASSWORD, +} from 'api/models/auth-method'; module('Unit | Abilities | User', function (hooks) { setupTest(hooks); - test('it reflects when a given user resource may add accounts based on authorized_actions', function (assert) { - assert.expect(2); - const service = this.owner.lookup('service:can'); - const model = { - authorized_actions: ['add-accounts'], - }; - assert.ok(service.can('addAccounts user', model)); - model.authorized_actions = []; - assert.notOk(service.can('addAccounts user', model)); + let canService; + let store; + + const instances = { + account: null, + user: null, + }; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + instances.user = store.createRecord('user'); + instances.account = store.createRecord('account'); }); - test('it reflects when a given user resource may remove accounts based on authorized_actions', function (assert) { - assert.expect(2); - const service = this.owner.lookup('service:can'); - const model = { - authorized_actions: ['remove-accounts'], - }; - assert.ok(service.can('removeAccounts user', model)); - model.authorized_actions = []; - assert.notOk(service.can('removeAccounts user', model)); + test('can add accounts to user when authorized and given a known account type', function (assert) { + assert.expect(5); + instances.user.authorized_actions = ['add-accounts']; + assert.true(canService.can('addAccounts user', instances.user)); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = 'no-such-type'; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('cannot add accounts to user when unauthorized', function (assert) { + assert.expect(5); + instances.user.authorized_actions = []; + assert.false(canService.can('addAccounts user', instances.user)); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = 'no-such-type'; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('can remove accounts from user when authorized and given a known account type', function (assert) { + assert.expect(5); + instances.user.authorized_actions = ['remove-accounts']; + assert.true(canService.can('removeAccounts user', instances.user)); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = 'no-such-type'; + assert.false( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('cannot remove accounts from user when unauthorized', function (assert) { + assert.expect(5); + instances.user.authorized_actions = []; + assert.false(canService.can('removeAccounts user', instances.user)); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.false( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.false( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = 'no-such-type'; + assert.false( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); }); }); diff --git a/ui/admin/app/abilities/account.js b/ui/admin/app/abilities/user.js similarity index 67% rename from ui/admin/app/abilities/account.js rename to ui/admin/app/abilities/user.js index 5f6fcec87..80e46d66d 100644 --- a/ui/admin/app/abilities/account.js +++ b/ui/admin/app/abilities/user.js @@ -3,10 +3,10 @@ * SPDX-License-Identifier: MPL-2.0 */ -import AccountAbility from 'api/abilities/account'; +import UserAbility from 'api/abilities/user'; import { inject as service } from '@ember/service'; -export default class OverrideAccountAbility extends AccountAbility { +export default class OverrideUserAbility extends UserAbility { // =service @service features; @@ -16,10 +16,14 @@ export default class OverrideAccountAbility extends AccountAbility { */ get canAddAccount() { - return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP; + return this.features.isEnabled('ldap-auth-methods') || !this.account.isLDAP + ? super.canAddAccount + : false; } get canRemoveAccount() { - return this.features.isEnabled('ldap-auth-methods') || !this.model.isLDAP; + return this.features.isEnabled('ldap-auth-methods') || !this.account.isLDAP + ? super.canRemoveAccount + : false; } } diff --git a/ui/admin/tests/unit/abilities/account-test.js b/ui/admin/tests/unit/abilities/account-test.js deleted file mode 100644 index 914bacaaa..000000000 --- a/ui/admin/tests/unit/abilities/account-test.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: MPL-2.0 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { - TYPE_AUTH_METHOD_LDAP, - TYPE_AUTH_METHOD_OIDC, - TYPE_AUTH_METHOD_PASSWORD, -} from 'api/models/auth-method'; - -module('Unit | Abilities | account', function (hooks) { - setupTest(hooks); - - let canService; - let store; - let features; - - hooks.beforeEach(function () { - canService = this.owner.lookup('service:can'); - store = this.owner.lookup('service:store'); - features = this.owner.lookup('service:features'); - }); - - test('can add ldap account to user when feature flag enabled', function (assert) { - assert.expect(1); - features.enable('ldap-auth-methods'); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_LDAP, - }); - assert.true(canService.can('addAccount account', account)); - }); - - test('can add non-ldap account to user', function (assert) { - assert.expect(2); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_OIDC, - }); - assert.true(canService.can('addAccount account', account)); - account.type = TYPE_AUTH_METHOD_PASSWORD; - assert.true(canService.can('addAccount account', account)); - }); - - test('cannot add ldap account to user when feature flag disabled', function (assert) { - assert.expect(1); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_LDAP, - }); - assert.false(canService.can('addAccount account', account)); - }); - - test('can remove ldap account from user when feature flag enabled', function (assert) { - assert.expect(1); - features.enable('ldap-auth-methods'); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_LDAP, - }); - assert.true(canService.can('removeAccount account', account)); - }); - - test('cannot remove ldap account from user when feature flag disabled', function (assert) { - assert.expect(1); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_LDAP, - }); - assert.false(canService.can('removeAccount account', account)); - }); - - test('can remove non-ldap account from user', function (assert) { - assert.expect(2); - const account = store.createRecord('account', { - type: TYPE_AUTH_METHOD_OIDC, - }); - assert.true(canService.can('removeAccount account', account)); - (account.type = TYPE_AUTH_METHOD_PASSWORD), - assert.true(canService.can('removeAccount account', account)); - }); -}); diff --git a/ui/admin/tests/unit/abilities/user-test.js b/ui/admin/tests/unit/abilities/user-test.js new file mode 100644 index 000000000..04c09244c --- /dev/null +++ b/ui/admin/tests/unit/abilities/user-test.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_LDAP, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_PASSWORD, +} from 'api/models/auth-method'; + +module('Unit | Abilities | user', function (hooks) { + setupTest(hooks); + + let canService; + let store; + let features; + + const instances = { + account: null, + user: null, + }; + + hooks.beforeEach(function () { + canService = this.owner.lookup('service:can'); + store = this.owner.lookup('service:store'); + features = this.owner.lookup('service:features'); + instances.user = store.createRecord('user', { + authorized_actions: ['add-accounts', 'remove-accounts'], + }); + instances.account = store.createRecord('account'); + }); + + test('can add ldap account to user when feature flag enabled', function (assert) { + assert.expect(1); + features.enable('ldap-auth-methods'); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('can add non-ldap account to user', function (assert) { + assert.expect(2); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('cannot add ldap account to user when feature flag disabled', function (assert) { + assert.expect(1); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.false( + canService.can('addAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('can remove ldap account from user when feature flag enabled', function (assert) { + assert.expect(1); + features.enable('ldap-auth-methods'); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('cannot remove ldap account from user when feature flag disabled', function (assert) { + assert.expect(1); + instances.account.type = TYPE_AUTH_METHOD_LDAP; + assert.false( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + }); + + test('can remove non-ldap account from user', function (assert) { + assert.expect(2); + instances.account.type = TYPE_AUTH_METHOD_OIDC; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + instances.account.type = TYPE_AUTH_METHOD_PASSWORD; + assert.true( + canService.can('removeAccount user', instances.user, { + account: instances.account, + }) + ); + }); +}); From 8d77bc60b3bc5cd2234e90d76b2ab33eb1be1ae6 Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Mon, 22 May 2023 15:46:13 -0700 Subject: [PATCH 12/14] Icu 9226 disable all actions for ldap accounts within users (#1688) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 disable all actions for ldap accounts within users ✅ Closes: ICU-9226 --- .../accounts/account/actions/index.hbs | 2 +- .../auth-method/actions/index.hbs | 2 +- .../form/user/add-accounts/index.hbs | 12 +- .../form/user/add-accounts/index.js | 11 +- .../scopes/scope/users/user/add-accounts.js | 4 +- .../auth-method/accounts/index.hbs | 4 +- .../scopes/scope/users/user/accounts.hbs | 2 +- .../tests/acceptance/users/accounts-test.js | 212 ++++++++++++++---- 8 files changed, 190 insertions(+), 59 deletions(-) diff --git a/ui/admin/app/components/auth-methods/auth-method/accounts/account/actions/index.hbs b/ui/admin/app/components/auth-methods/auth-method/accounts/account/actions/index.hbs index 00e985e8b..e98e75aa7 100644 --- a/ui/admin/app/components/auth-methods/auth-method/accounts/account/actions/index.hbs +++ b/ui/admin/app/components/auth-methods/auth-method/accounts/account/actions/index.hbs @@ -3,7 +3,7 @@ SPDX-License-Identifier: MPL-2.0 }} -{{#if (can 'delete model' @model)}} +{{#if (can 'delete account' @model)}} - + + {{account.id}} + {{account.description}} + {{account.accountName}} diff --git a/ui/admin/app/components/form/user/add-accounts/index.js b/ui/admin/app/components/form/user/add-accounts/index.js index 66797a6a8..a111f71d2 100644 --- a/ui/admin/app/components/form/user/add-accounts/index.js +++ b/ui/admin/app/components/form/user/add-accounts/index.js @@ -4,12 +4,13 @@ */ import Component from '@glimmer/component'; -import { computed, action } from '@ember/object'; +import { action } from '@ember/object'; import { A } from '@ember/array'; +import { inject as service } from '@ember/service'; export default class FormUserAddAccountsComponent extends Component { // =properties - + @service can; /** * Array of selected account IDs. * @type {EmberArray} @@ -21,7 +22,6 @@ export default class FormUserAddAccountsComponent extends Component { * @param {[AccountModel]} filteredAccounts * @type {boolean} */ - @computed('filteredAccounts.length') get hasAvailableAccounts() { return this.filteredAccounts.length > 0; } @@ -30,12 +30,13 @@ export default class FormUserAddAccountsComponent extends Component { * Accounts not already added to the user. * @type {[AccountModel]} */ - @computed('args.{accounts.[],model.account_ids.[]}') get filteredAccounts() { // Get IDs for accounts already added to the current user const alreadyAddedAccountIDs = this.args.model.account_ids; const notAddedAccounts = this.args.accounts.filter( - ({ id }) => !alreadyAddedAccountIDs.includes(id) + (account) => + !alreadyAddedAccountIDs.includes(account.id) && + this.can.can('addAccount user', this.args.model, { account }) ); return notAddedAccounts; } diff --git a/ui/admin/app/routes/scopes/scope/users/user/add-accounts.js b/ui/admin/app/routes/scopes/scope/users/user/add-accounts.js index c36ddae17..d16152ae6 100644 --- a/ui/admin/app/routes/scopes/scope/users/user/add-accounts.js +++ b/ui/admin/app/routes/scopes/scope/users/user/add-accounts.js @@ -27,8 +27,8 @@ export default class ScopesScopeUsersUserAddAccountsRoute extends Route { } /** - * Returns the current target, all host catalogs, and all host sets. - * @return {{target: TargetModel, hostCatalogs: [HostCatalogModel], hostSets: [HostSetModel]}} + * Returns the current user, all auth methods, and all accounts. + * @return {{user: UserModel, authMethods: [AuthMethodModel], accounts: [AccountModel]}} */ async model() { const user = this.modelFor('scopes.scope.users.user'); diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs index 14841ac19..022c16030 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs @@ -41,7 +41,7 @@ {{#each @model.accounts as |account|}} - {{#if (can 'read model' account)}} + {{#if (can 'read account' account)}} - {{#if (can 'read model' account)}} + {{#if (can 'read account' account)}} - {{#if (can 'removeAccounts user' @model.user)}} + {{#if (can 'removeAccount user' @model.user account=account)}} item !== 'remove-accounts' ); instances.user.update({ authorized_actions }); - await visit(urls.accounts); - assert.notOk(find('tbody tr .rose-dropdown-button-danger')); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + + assert.dom(REMOVE_ACTION_SELECTOR).doesNotExist(); + }); + + test('cannot remove an ldap account when feature flag disabled', async function (assert) { + assert.expect(2); + const authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + }); + const { id } = this.server.create('account', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + authMethod, + }); + instances.user.update({ accountIds: [id] }); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + + assert.dom(ACCOUNTS_ACTION_SELECTOR).doesNotExist(); + assert.dom(ACCOUNTS_TYPE_SELECTOR).hasText('LDAP'); + }); + + test('can remove an ldap account when feature flag enabled', async function (assert) { + assert.expect(2); + features.enable('ldap-auth-methods'); + const authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + }); + const { id } = this.server.create('account', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + authMethod, + }); + instances.user.update({ accountIds: [id] }); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + + assert.dom(ACCOUNTS_ACTION_SELECTOR).exists(); + assert.dom(ACCOUNTS_TYPE_SELECTOR).hasText('LDAP'); }); test('shows error message on account remove', async function (assert) { @@ -98,24 +164,31 @@ module('Acceptance | users | accounts', function (hooks) { } ); }); - await visit(urls.accounts); - assert.strictEqual(findAll('tbody tr').length, accountsCount); - await click('tbody tr .rose-dropdown-button-danger'); - assert.ok(find('[role="alert"]')); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: accountsCount }); + await click(REMOVE_ACTION_SELECTOR); + assert.dom(ERROR_MSG_SELECTOR).isVisible(); }); test('visiting account add accounts', async function (assert) { assert.expect(1); - await visit(urls.addAccounts); - await visit(urls.addAccounts); + await visit(urls.accounts); + + await click(ADD_ACCOUNTS_ACTION_SELECTOR); await a11yAudit(); + assert.strictEqual(currentURL(), urls.addAccounts); }); test('can navigate to add accounts with proper authorization', async function (assert) { assert.expect(1); - await visit(urls.accounts); - assert.ok(find(`[href="${urls.addAccounts}"]`)); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + + assert.dom(`[href="${urls.addAccounts}"]`).exists(); }); test('cannot navigate to add accounts without proper authorization', async function (assert) { @@ -124,38 +197,93 @@ module('Acceptance | users | accounts', function (hooks) { (item) => item !== 'add-accounts' ); instances.user.update({ authorized_actions }); - await visit(urls.accounts); - assert.notOk(find(`[href="${urls.addAccounts}"]`)); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + assert.dom(ADD_ACCOUNTS_ACTION_SELECTOR).doesNotExist(); + }); + + test('cannot add ldap accounts to user when feature flag is disabled', async function (assert) { + assert.expect(1); + const authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + }); + this.server.create('account', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + authMethod, + }); + const accountsAvailableCount = + this.server.schema.accounts.all().length - + instances.user.accountIds.length; + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + await click(ADD_ACCOUNTS_ACTION_SELECTOR); + + assert + .dom(TABLE_ROWS_SELECTOR) + .exists({ count: accountsAvailableCount - 1 }); + }); + + test('can add ldap accounts to user when feature flag is enabled', async function (assert) { + assert.expect(1); + features.enable('ldap-auth-methods'); + const authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + }); + this.server.create('account', { + scope: instances.scopes.org, + type: TYPE_AUTH_METHOD_LDAP, + authMethod, + }); + const accountsAvailableCount = + this.server.schema.accounts.all().length - + instances.user.accountIds.length; + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + await click(ADD_ACCOUNTS_ACTION_SELECTOR); + + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: accountsAvailableCount }); }); test('select and save accounts to add', async function (assert) { - assert.expect(3); + assert.expect(4); instances.user.update({ accountIds: [] }); - await visit(urls.accounts); - assert.strictEqual(findAll('tbody tr').length, 0); - await click('.rose-layout-page-actions a'); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: 0 }); + await click(ADD_ACCOUNTS_ACTION_SELECTOR); assert.strictEqual(currentURL(), urls.addAccounts); // Click three times to select, unselect, then reselect (for coverage) - await click('tbody label'); - await click('tbody label'); - await click('tbody label'); - await click('form [type="submit"]'); + await click(CHECKBOX_SELECTOR); + await click(CHECKBOX_SELECTOR); + await click(CHECKBOX_SELECTOR); + await click(SUBMIT_BTN_SELECTOR); await visit(urls.accounts); - assert.strictEqual(findAll('tbody tr').length, 1); + + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: 1 }); + assert.strictEqual(instances.user.accountIds.length, 1); }); test('select and cancel accounts to add', async function (assert) { assert.expect(4); - await visit(urls.accounts); - assert.strictEqual(findAll('tbody tr').length, accountsCount); - await click('tbody tr .rose-dropdown-button-danger'); - assert.strictEqual(findAll('tbody tr').length, accountsCount - 1); - await click('.rose-layout-page-actions a'); + await visit(urls.user); + + await click(`[href="${urls.accounts}"]`); + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: accountsCount }); + await click(REMOVE_ACTION_SELECTOR); + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: accountsCount - 1 }); + await click(ADD_ACCOUNTS_ACTION_SELECTOR); assert.strictEqual(currentURL(), urls.addAccounts); - await click('tbody label'); - await click('form [type="button"]'); + await click(CHECKBOX_SELECTOR); + await click(CANCEL_BTN_SELECTOR); await visit(urls.accounts); - assert.strictEqual(findAll('tbody tr').length, accountsCount - 1); + assert.dom(TABLE_ROWS_SELECTOR).exists({ count: accountsCount - 1 }); }); test('shows error message on account add', async function (assert) { @@ -174,8 +302,8 @@ module('Acceptance | users | accounts', function (hooks) { }); instances.user.update({ accountIds: [] }); await visit(urls.addAccounts); - await click('tbody label'); - await click('form [type="submit"]'); - assert.ok(find('[role="alert"]')); + await click(CHECKBOX_SELECTOR); + await click(SUBMIT_BTN_SELECTOR); + assert.dom(ERROR_MSG_SELECTOR).isVisible(); }); }); From fde4ec4e05908939503fea866c5599132d67918d Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Tue, 23 May 2023 12:37:35 -0700 Subject: [PATCH 13/14] Refactor type to use const values (#1695) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 use auth-method type consts --- addons/api/addon/models/role.js | 3 ++- .../api/mirage/generated/factories/account.js | 3 ++- addons/api/tests/unit/models/account-test.js | 2 +- .../tests/unit/serializers/account-test.js | 24 +++++++++++-------- .../components/form/authenticate-test.js | 17 ++++++++++--- .../scopes/scope/roles/role/add-principals.js | 5 ++-- .../acceptance/auth-methods/update-test.js | 3 ++- .../tests/acceptance/authentication-test.js | 10 +++++--- .../acceptance/managed-groups/create-test.js | 3 ++- .../acceptance/managed-groups/delete-test.js | 3 ++- .../acceptance/managed-groups/list-test.js | 3 ++- .../acceptance/managed-groups/members-test.js | 3 ++- .../acceptance/managed-groups/read-test.js | 3 ++- 13 files changed, 55 insertions(+), 27 deletions(-) diff --git a/addons/api/addon/models/role.js b/addons/api/addon/models/role.js index dd55a78eb..83caa27d9 100644 --- a/addons/api/addon/models/role.js +++ b/addons/api/addon/models/role.js @@ -7,6 +7,7 @@ import GeneratedRoleModel from '../generated/models/role'; import { attr } from '@ember-data/model'; import { inject as service } from '@ember/service'; import { all, resolve } from 'rsvp'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; export default class RoleModel extends GeneratedRoleModel { // =services @@ -133,7 +134,7 @@ export default class RoleModel extends GeneratedRoleModel { // Collect all auth methods. const authMethods = this.resourceFilterStore.queryBy( 'auth-method', - { type: 'oidc' }, + { type: TYPE_AUTH_METHOD_OIDC }, { scope_id: 'global', recursive: true } ); diff --git a/addons/api/mirage/generated/factories/account.js b/addons/api/mirage/generated/factories/account.js index 96eb86e80..55bd0df37 100644 --- a/addons/api/mirage/generated/factories/account.js +++ b/addons/api/mirage/generated/factories/account.js @@ -5,12 +5,13 @@ import { Factory } from 'miragejs'; import { faker } from '@faker-js/faker'; +import { TYPE_AUTH_METHOD_PASSWORD } from 'api/models/auth-method'; /** * GeneratedAccountFactory */ export default Factory.extend({ - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, created_time: () => faker.date.recent(), updated_time: () => faker.date.recent(), version: () => faker.datatype.number(), diff --git a/addons/api/tests/unit/models/account-test.js b/addons/api/tests/unit/models/account-test.js index 71f5db4bc..946b7362a 100644 --- a/addons/api/tests/unit/models/account-test.js +++ b/addons/api/tests/unit/models/account-test.js @@ -30,7 +30,7 @@ module('Unit | Model | account', function (hooks) { assert.expect(2); store = this.owner.lookup('service:store'); let model = store.createRecord('account', { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, }); assert.notOk(model.accountName); model.login_name = 'foobar'; diff --git a/addons/api/tests/unit/serializers/account-test.js b/addons/api/tests/unit/serializers/account-test.js index 2a9003649..326298a26 100644 --- a/addons/api/tests/unit/serializers/account-test.js +++ b/addons/api/tests/unit/serializers/account-test.js @@ -5,6 +5,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, +} from 'api/models/auth-method'; module('Unit | Serializer | account', function (hooks) { setupTest(hooks); @@ -14,7 +18,7 @@ module('Unit | Serializer | account', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); const record = store.createRecord('account', { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'Name', auth_method_id: '1', description: 'Description', @@ -27,7 +31,7 @@ module('Unit | Serializer | account', function (hooks) { const snapshot = record._createSnapshot(); const serializedRecord = serializer.serialize(snapshot); assert.deepEqual(serializedRecord, { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'Name', auth_method_id: '1', description: 'Description', @@ -48,7 +52,7 @@ module('Unit | Serializer | account', function (hooks) { id: '1', type: 'account', attributes: { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'Name', auth_method_id: '1', description: 'Description', @@ -65,7 +69,7 @@ module('Unit | Serializer | account', function (hooks) { const serializedRecord = serializer.serialize(snapshot); assert.notOk(serializedRecord.attributes); assert.deepEqual(serializedRecord, { - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, name: 'Name', auth_method_id: '1', description: 'Description', @@ -78,7 +82,7 @@ module('Unit | Serializer | account', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); const record = store.createRecord('account', { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', auth_method_id: '1', description: 'Description', @@ -88,7 +92,7 @@ module('Unit | Serializer | account', function (hooks) { const snapshot = record._createSnapshot(); const serializedRecord = serializer.serialize(snapshot); assert.deepEqual(serializedRecord, { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', auth_method_id: '1', description: 'Description', @@ -104,7 +108,7 @@ module('Unit | Serializer | account', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); const record = store.createRecord('account', { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', auth_method_id: '1', description: 'Description', @@ -117,7 +121,7 @@ module('Unit | Serializer | account', function (hooks) { }; const serializedRecord = serializer.serialize(snapshot); assert.deepEqual(serializedRecord, { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', auth_method_id: '1', description: 'Description', @@ -138,7 +142,7 @@ module('Unit | Serializer | account', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); const record = store.createRecord('account', { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', description: 'Description', login_name: 'Login Name', @@ -162,7 +166,7 @@ module('Unit | Serializer | account', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); const record = store.createRecord('account', { - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, name: 'Name', description: 'Description', login_name: 'Login Name', diff --git a/addons/core/tests/integration/components/form/authenticate-test.js b/addons/core/tests/integration/components/form/authenticate-test.js index 8a85394c0..9ae56b9c6 100644 --- a/addons/core/tests/integration/components/form/authenticate-test.js +++ b/addons/core/tests/integration/components/form/authenticate-test.js @@ -8,6 +8,11 @@ import { setupRenderingTest } from 'ember-qunit'; import { render, find } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupIntl } from 'ember-intl/test-support'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, + TYPE_AUTH_METHOD_LDAP, +} from 'api/models/auth-method'; module('Integration | Component | form | authenticate', function (hooks) { setupRenderingTest(hooks); @@ -18,7 +23,9 @@ module('Integration | Component | form | authenticate', function (hooks) { const store = this.owner.lookup('service:store'); - this.model = store.createRecord('auth-method', { type: 'ldap' }); + this.model = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_LDAP, + }); this.submit = () => {}; this.disabled = () => {}; @@ -39,7 +46,9 @@ module('Integration | Component | form | authenticate', function (hooks) { const store = this.owner.lookup('service:store'); - this.model = store.createRecord('auth-method', { type: 'password' }); + this.model = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_PASSWORD, + }); this.submit = () => {}; this.disabled = () => {}; @@ -60,7 +69,9 @@ module('Integration | Component | form | authenticate', function (hooks) { const store = this.owner.lookup('service:store'); - this.model = store.createRecord('auth-method', { type: 'oidc' }); + this.model = store.createRecord('auth-method', { + type: TYPE_AUTH_METHOD_OIDC, + }); this.submit = () => {}; this.disabled = () => {}; diff --git a/ui/admin/app/routes/scopes/scope/roles/role/add-principals.js b/ui/admin/app/routes/scopes/scope/roles/role/add-principals.js index 8d2cefeb8..e3b96bae0 100644 --- a/ui/admin/app/routes/scopes/scope/roles/role/add-principals.js +++ b/ui/admin/app/routes/scopes/scope/roles/role/add-principals.js @@ -10,6 +10,7 @@ import { hash, all } from 'rsvp'; import { loading } from 'ember-loading'; import { notifySuccess, notifyError } from 'core/decorators/notify'; import { resourceFilter } from 'core/decorators/resource-filter'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route { // =services @@ -75,12 +76,12 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route { const oidcAuthMethods = scopeIDs?.length ? await this.resourceFilterStore.queryBy( 'auth-method', - { scope_id: scopeIDs, type: 'oidc' }, + { scope_id: scopeIDs, type: TYPE_AUTH_METHOD_OIDC }, { scope_id: 'global', recursive: true } ) : await this.resourceFilterStore.queryBy( 'auth-method', - { type: 'oidc' }, + { type: TYPE_AUTH_METHOD_OIDC }, { scope_id: 'global', recursive: true } ); //extract oidc authMethod IDs diff --git a/ui/admin/tests/acceptance/auth-methods/update-test.js b/ui/admin/tests/acceptance/auth-methods/update-test.js index fb8cd3985..6bb95c225 100644 --- a/ui/admin/tests/acceptance/auth-methods/update-test.js +++ b/ui/admin/tests/acceptance/auth-methods/update-test.js @@ -22,6 +22,7 @@ import { // //currentSession, // //invalidateSession, } from 'ember-simple-auth/test-support'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | auth methods | update', function (hooks) { setupApplicationTest(hooks); @@ -73,7 +74,7 @@ module('Acceptance | auth methods | update', function (hooks) { assert.expect(12); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); await visit(`${urls.authMethods}/${instances.authMethod.id}`); await click('form [type="button"]:not(:disabled)', 'Activate edit mode'); diff --git a/ui/admin/tests/acceptance/authentication-test.js b/ui/admin/tests/acceptance/authentication-test.js index 694cd8229..4a2eb37a7 100644 --- a/ui/admin/tests/acceptance/authentication-test.js +++ b/ui/admin/tests/acceptance/authentication-test.js @@ -25,6 +25,10 @@ import { invalidateSession, } from 'ember-simple-auth/test-support'; import Service from '@ember/service'; +import { + TYPE_AUTH_METHOD_PASSWORD, + TYPE_AUTH_METHOD_OIDC, +} from 'api/models/auth-method'; module('Acceptance | authentication', function (hooks) { setupApplicationTest(hooks); @@ -76,15 +80,15 @@ module('Acceptance | authentication', function (hooks) { scope = { id: orgScope.id, type: orgScope.type }; globalAuthMethod = this.server.create('auth-method', { scope: globalScope, - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, }); authMethod = this.server.create('auth-method', { scope: orgScope, - type: 'password', + type: TYPE_AUTH_METHOD_PASSWORD, }); authMethodOIDC = this.server.create('auth-method', { scope: orgScope, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); orgScopeID = orgScope.id; globalAuthMethodID = globalAuthMethod.id; diff --git a/ui/admin/tests/acceptance/managed-groups/create-test.js b/ui/admin/tests/acceptance/managed-groups/create-test.js index 85fe28615..2d4802bfc 100644 --- a/ui/admin/tests/acceptance/managed-groups/create-test.js +++ b/ui/admin/tests/acceptance/managed-groups/create-test.js @@ -10,6 +10,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | managed-groups | create', function (hooks) { setupApplicationTest(hooks); @@ -40,7 +41,7 @@ module('Acceptance | managed-groups | create', function (hooks) { }); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); instances.managedGroup = this.server.create('managed-group', { scope: instances.scopes.org, diff --git a/ui/admin/tests/acceptance/managed-groups/delete-test.js b/ui/admin/tests/acceptance/managed-groups/delete-test.js index 00a8d06d8..db30cefff 100644 --- a/ui/admin/tests/acceptance/managed-groups/delete-test.js +++ b/ui/admin/tests/acceptance/managed-groups/delete-test.js @@ -10,6 +10,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | managed-groups | delete', function (hooks) { setupApplicationTest(hooks); @@ -40,7 +41,7 @@ module('Acceptance | managed-groups | delete', function (hooks) { }); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); instances.managedGroup = this.server.create('managed-group', { scope: instances.scopes.org, diff --git a/ui/admin/tests/acceptance/managed-groups/list-test.js b/ui/admin/tests/acceptance/managed-groups/list-test.js index 0c007bced..6b33317ad 100644 --- a/ui/admin/tests/acceptance/managed-groups/list-test.js +++ b/ui/admin/tests/acceptance/managed-groups/list-test.js @@ -8,6 +8,7 @@ import { visit, click, find } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | managed-groups | list', function (hooks) { setupApplicationTest(hooks); @@ -38,7 +39,7 @@ module('Acceptance | managed-groups | list', function (hooks) { }); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); instances.managedGroup = this.server.create('managed-group', { scope: instances.scopes.org, diff --git a/ui/admin/tests/acceptance/managed-groups/members-test.js b/ui/admin/tests/acceptance/managed-groups/members-test.js index fc682cfec..7f36c5792 100644 --- a/ui/admin/tests/acceptance/managed-groups/members-test.js +++ b/ui/admin/tests/acceptance/managed-groups/members-test.js @@ -14,6 +14,7 @@ import { //currentSession, //invalidateSession, } from 'ember-simple-auth/test-support'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | managed-groups | members', function (hooks) { setupApplicationTest(hooks); @@ -47,7 +48,7 @@ module('Acceptance | managed-groups | members', function (hooks) { 'auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }, 'withAccountsAndUsersAndManagedGroups' ); diff --git a/ui/admin/tests/acceptance/managed-groups/read-test.js b/ui/admin/tests/acceptance/managed-groups/read-test.js index 2cb3c1317..67f97f853 100644 --- a/ui/admin/tests/acceptance/managed-groups/read-test.js +++ b/ui/admin/tests/acceptance/managed-groups/read-test.js @@ -9,6 +9,7 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { TYPE_AUTH_METHOD_OIDC } from 'api/models/auth-method'; module('Acceptance | managed-groups | read', function (hooks) { setupApplicationTest(hooks); @@ -39,7 +40,7 @@ module('Acceptance | managed-groups | read', function (hooks) { }); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, - type: 'oidc', + type: TYPE_AUTH_METHOD_OIDC, }); instances.managedGroup = this.server.create('managed-group', { scope: instances.scopes.org, From 59183b9b209f57259bd263d3a5a97001a01d66bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 14:21:00 -0400 Subject: [PATCH 14/14] chore(deps): bump socket.io-parser from 4.2.1 to 4.2.3 (#1696) Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.2.1 to 4.2.3. - [Release notes](https://github.com/socketio/socket.io-parser/releases) - [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io-parser/compare/4.2.1...4.2.3) --- updated-dependencies: - dependency-name: socket.io-parser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 752a1568b..b966d0a4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23408,9 +23408,9 @@ socket.io-adapter@~2.5.2: ws "~8.11.0" socket.io-parser@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" - integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + version "4.2.3" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.3.tgz#926bcc6658e2ae0883dc9dee69acbdc76e4e3667" + integrity sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1"