From 92640b96aaa50754e7cc729536229e561e274bcf Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 15 Jan 2021 11:10:33 -0500 Subject: [PATCH 01/30] wip: OIDC authenticator --- addons/api/mirage/factories/auth-method.js | 8 ++++++ addons/api/mirage/scenarios/default.js | 2 +- addons/auth/addon/authenticators/oidc.js | 26 +++++++++++++++++++ .../tests/unit/authenticators/oidc-test.js | 8 ++++++ addons/core/addon/authenticators/oidc.js | 8 ++++++ .../form/authenticate/oidc/index.hbs | 13 ++++++++++ addons/core/app/authenticators/oidc.js | 1 + .../app/components/form/authenticate/oidc.js | 1 + .../components/form/authenticate/oidc-test.js | 26 +++++++++++++++++++ 9 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 addons/auth/addon/authenticators/oidc.js create mode 100644 addons/auth/tests/unit/authenticators/oidc-test.js create mode 100644 addons/core/addon/authenticators/oidc.js create mode 100644 addons/core/addon/components/form/authenticate/oidc/index.hbs create mode 100644 addons/core/app/authenticators/oidc.js create mode 100644 addons/core/app/components/form/authenticate/oidc.js create mode 100644 addons/core/tests/integration/components/form/authenticate/oidc-test.js diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js index 61d29726b..ec2c62064 100644 --- a/addons/api/mirage/factories/auth-method.js +++ b/addons/api/mirage/factories/auth-method.js @@ -1,9 +1,17 @@ import factory from '../generated/factories/auth-method'; import { trait } from 'ember-cli-mirage'; +const types = [ + 'password', + 'oidc' +]; + export default factory.extend({ id: (i) => `auth-method-id-${i}`, + // Cycle through available types + type: (i) => types[i % types.length], + /** * Adds accounts (with associated users) to auth method. */ diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index cc821e6dd..9bed5a325 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -14,7 +14,7 @@ export default function(server) { }, 'withChildren')[0]; // Auth - server.createList('auth-method', 1, { scope: globalScope }, 'withAccountsAndUsers'); + server.createList('auth-method', 2, { scope: globalScope }, 'withAccountsAndUsers'); server.createList('auth-method', 3, { scope: orgScope }, 'withAccountsAndUsers'); // Groups and Users diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js new file mode 100644 index 000000000..0d641da41 --- /dev/null +++ b/addons/auth/addon/authenticators/oidc.js @@ -0,0 +1,26 @@ +import BaseAuthenticator from './base'; +// import { resolve, reject } from 'rsvp'; +// import fetch from 'fetch'; +import { Promise } from 'rsvp'; + +/** + * + */ +export default class OIDCAuthenticator extends BaseAuthenticator { + + // =unimplemented methods + // TODO: implement ;-) + + buildAuthEndpointURL(/* { + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + } */) { } + + async authenticate( + creds, + requestCookies = true, + options + ) { + return false; + } +} diff --git a/addons/auth/tests/unit/authenticators/oidc-test.js b/addons/auth/tests/unit/authenticators/oidc-test.js new file mode 100644 index 000000000..0d4a9b492 --- /dev/null +++ b/addons/auth/tests/unit/authenticators/oidc-test.js @@ -0,0 +1,8 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Authenticator | OIDC', function (hooks) { + setupTest(hooks); + setupMirage(hooks); +}); diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js new file mode 100644 index 000000000..77dd7e29e --- /dev/null +++ b/addons/core/addon/authenticators/oidc.js @@ -0,0 +1,8 @@ +import BaseOIDCAuthenticator from 'auth/authenticators/oidc'; +//import { inject as service } from '@ember/service'; + +/** + * + */ +export default class OIDCAuthenticator extends BaseOIDCAuthenticator { +} diff --git a/addons/core/addon/components/form/authenticate/oidc/index.hbs b/addons/core/addon/components/form/authenticate/oidc/index.hbs new file mode 100644 index 000000000..e8e1294fe --- /dev/null +++ b/addons/core/addon/components/form/authenticate/oidc/index.hbs @@ -0,0 +1,13 @@ + + + + + diff --git a/addons/core/app/authenticators/oidc.js b/addons/core/app/authenticators/oidc.js new file mode 100644 index 000000000..2299b299d --- /dev/null +++ b/addons/core/app/authenticators/oidc.js @@ -0,0 +1 @@ +export { default } from 'core/authenticators/oidc'; diff --git a/addons/core/app/components/form/authenticate/oidc.js b/addons/core/app/components/form/authenticate/oidc.js new file mode 100644 index 000000000..cbb3b8ab4 --- /dev/null +++ b/addons/core/app/components/form/authenticate/oidc.js @@ -0,0 +1 @@ +export { default } from 'core/components/form/authenticate/oidc'; \ No newline at end of file diff --git a/addons/core/tests/integration/components/form/authenticate/oidc-test.js b/addons/core/tests/integration/components/form/authenticate/oidc-test.js new file mode 100644 index 000000000..3345d3e3d --- /dev/null +++ b/addons/core/tests/integration/components/form/authenticate/oidc-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/authenticate/oidc', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); From a3c12b36e764dbd706ea36c29dab8cd653ebdcd2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 15 Jan 2021 17:32:30 -0500 Subject: [PATCH 02/30] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20mock=20authent?= =?UTF-8?q?icate=20should=20handle=20multiple=20auth=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 36 +++++++++++-------- addons/auth/addon/authenticators/oidc.js | 8 ++--- .../tests/unit/authenticators/oidc-test.js | 2 +- .../components/form/authenticate/oidc-test.js | 26 -------------- .../tests/acceptance/authentication-test.js | 6 +++- yarn.lock | 27 +++----------- 6 files changed, 36 insertions(+), 69 deletions(-) delete mode 100644 addons/core/tests/integration/components/form/authenticate/oidc-test.js diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 96813a7d5..84d755324 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -23,28 +23,34 @@ import { Response } from 'miragejs'; // } // } +const methodHandlers = { + password: { + authenticate: (scopeAttrs) => + new Response(200, {}, { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }) + } +}; + export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); if (payload.credentials.login_name === 'error') { return new Response(400); } else { - const id = request.params.id_method.split(':')[0]; + const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); const authMethod = authMethods.find(id); const scope = scopes.find(authMethod.scopeId); - const scopeAttrs = - this.serialize(scopes.find(scope.id)); - return new Response(200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }); + const scopeAttrs = this.serialize(scopes.find(scope.id)); + return methodHandlers[authMethod.type][method](scopeAttrs); } } diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 0d641da41..4fd24845c 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -1,7 +1,7 @@ import BaseAuthenticator from './base'; // import { resolve, reject } from 'rsvp'; // import fetch from 'fetch'; -import { Promise } from 'rsvp'; +//import { Promise } from 'rsvp'; /** * @@ -17,9 +17,9 @@ export default class OIDCAuthenticator extends BaseAuthenticator { } */) { } async authenticate( - creds, - requestCookies = true, - options + // creds, + // requestCookies = true, + // options ) { return false; } diff --git a/addons/auth/tests/unit/authenticators/oidc-test.js b/addons/auth/tests/unit/authenticators/oidc-test.js index 0d4a9b492..c5498f123 100644 --- a/addons/auth/tests/unit/authenticators/oidc-test.js +++ b/addons/auth/tests/unit/authenticators/oidc-test.js @@ -1,4 +1,4 @@ -import { module, test } from 'qunit'; +import { module/*, test */} from 'qunit'; import { setupTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; diff --git a/addons/core/tests/integration/components/form/authenticate/oidc-test.js b/addons/core/tests/integration/components/form/authenticate/oidc-test.js deleted file mode 100644 index 3345d3e3d..000000000 --- a/addons/core/tests/integration/components/form/authenticate/oidc-test.js +++ /dev/null @@ -1,26 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/authenticate/oidc', function(hooks) { - setupRenderingTest(hooks); - - test('it renders', async function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.set('myAction', function(val) { ... }); - - await render(hbs``); - - assert.equal(this.element.textContent.trim(), ''); - - // Template block usage: - await render(hbs` - - template block text - - `); - - assert.equal(this.element.textContent.trim(), 'template block text'); - }); -}); diff --git a/ui/admin/tests/acceptance/authentication-test.js b/ui/admin/tests/acceptance/authentication-test.js index f39c4d9bd..82a7184db 100644 --- a/ui/admin/tests/acceptance/authentication-test.js +++ b/ui/admin/tests/acceptance/authentication-test.js @@ -66,8 +66,12 @@ module('Acceptance | authentication', function (hooks) { scope = { id: orgScope.id, type: orgScope.type }; globalAuthMethod = this.server.create('auth-method', { scope: globalScope, + type: 'password' + }); + authMethod = this.server.create('auth-method', { + scope: orgScope, + type: 'password' }); - authMethod = this.server.create('auth-method', { scope: orgScope }); orgScopeID = orgScope.id; globalAuthMethodID = globalAuthMethod.id; authMethodID = authMethod.id; diff --git a/yarn.lock b/yarn.lock index 9544a1b3b..7ecac33ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7503,7 +7503,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -12311,7 +12311,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -14004,11 +14004,6 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14017,16 +14012,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*, lodash._bindcallback@^3.0.0: +lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14036,19 +14026,12 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14261,7 +14244,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@*, lodash.restparam@^3.0.0: +lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= From af8947dc62bfd615aec7dd707a6baa054e5ebced Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 19 Jan 2021 15:43:45 -0500 Subject: [PATCH 03/30] chore: add mock authenticate:start endpoint --- addons/api/mirage/route-handlers/auth.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 84d755324..113ca5b40 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -38,6 +38,26 @@ const methodHandlers = { last_used_time: '', expiration_time: '' }) + }, + /** + * OIDC authentication is a two-step process: + * + * 1. `authenticate:start` kicks of authentication by requesting some details + * from the Boundary server about the request, including the third-party + * URL to which to redirect the user. + * 2. `authenticate` accepts the token_request_id and state parameters in + * order to retrieve a Boundary token. This endpoint may be polled until + * the authentication flow is completed. + */ + oidc: { + 'authenticate:start': () => + new Response(200, {}, { + authorization_request_url: 'https://www.duckduckgo.com', + retreival_url: '', + token_request_id: 'token_request_1234', + state: 'base_58_encoded_string' + }), + authenticate: () => new Response(200) // TODO } }; From 2f3478d043dc2a4c6e240be8da7ac85acaacfedd Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 21 Jan 2021 14:26:02 -0500 Subject: [PATCH 04/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20initial=20flow=20t?= =?UTF-8?q?o=20open=20new=20window=20to=203rd=20party=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 45 ++++++++++--------- addons/auth/addon/authenticators/oidc.js | 42 +++++++++++++++-- addons/core/addon/authenticators/oidc.js | 31 ++++++++++++- .../scopes/scope/authenticate/method.js | 32 ++++++++++--- 4 files changed, 117 insertions(+), 33 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 113ca5b40..8f5d48b3f 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -25,19 +25,24 @@ import { Response } from 'miragejs'; const methodHandlers = { password: { - authenticate: (scopeAttrs) => - new Response(200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }) + authenticate: (payload, scopeAttrs) => { + if (payload.credentials.login_name === 'error') { + return new Response(400); + } else { + return new Response(200, {}, { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }); + } + } }, /** * OIDC authentication is a two-step process: @@ -63,15 +68,11 @@ const methodHandlers = { export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - if (payload.credentials.login_name === 'error') { - return new Response(400); - } else { - const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); - const authMethod = authMethods.find(id); - const scope = scopes.find(authMethod.scopeId); - const scopeAttrs = this.serialize(scopes.find(scope.id)); - return methodHandlers[authMethod.type][method](scopeAttrs); - } + const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); + const authMethod = authMethods.find(id); + const scope = scopes.find(authMethod.scopeId); + const scopeAttrs = this.serialize(scopes.find(scope.id)); + return methodHandlers[authMethod.type][method](payload, scopeAttrs); } export function deauthHandler() { diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 4fd24845c..2f95f0517 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -1,7 +1,6 @@ import BaseAuthenticator from './base'; -// import { resolve, reject } from 'rsvp'; -// import fetch from 'fetch'; -//import { Promise } from 'rsvp'; +import { resolve, reject } from 'rsvp'; +import fetch from 'fetch'; /** * @@ -16,6 +15,43 @@ export default class OIDCAuthenticator extends BaseAuthenticator { authMethod: { id: authMethodID }, } */) { } + buildStartAuthEndpointURL(/* { + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + } */) { } + + /** + * + */ + async startAuthentication( + creds, + requestCookies = true, + options + ) { + const body = JSON.stringify({ + token_type: requestCookies ? 'cookie' : null + }); + const authEndpointURL = this.buildStartAuthEndpointURL(options); + const response = await fetch(authEndpointURL, { method: 'post', body }); + const json = await response.json(); + const normalized = this.normalizeData(json); + if (response.status < 400) { + await this.openExternalOIDCFlow(normalized.authorization_request_url); + resolve(normalized); + } else { + reject(); + } + } + + /** + * Opens the specified URL in a new tab or window. By default this uses + * `window.open`, but may be overriden. + * @param {string} url + */ + openExternalOIDCFlow(url) { + window.open(url); + } + async authenticate( // creds, // requestCookies = true, diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 77dd7e29e..a805b79d6 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -1,8 +1,37 @@ import BaseOIDCAuthenticator from 'auth/authenticators/oidc'; -//import { inject as service } from '@ember/service'; +import { inject as service } from '@ember/service'; /** * */ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { + + // =services + + @service store; + + // =attributes + + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildStartAuthEndpointURL({ + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + }) { + const adapter = this.store.adapterFor('application'); + const options = { + adapterOptions: { + scopeID, + method: 'authenticate:start' + } + }; + return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); + } + } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index cfb2c5acf..499c9a0a0 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -1,5 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { notifyError } from 'core/decorators/notify'; @@ -37,12 +38,29 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const authMethod = this.modelFor('scopes.scope.authenticate.method'); const authenticatorName = `authenticator:${authMethod.type}`; const requestCookies = false; - await this.session.authenticate( - authenticatorName, - creds, - requestCookies, - { scope, authMethod } - ); - this.transitionTo('index'); + switch (authMethod.type) { + case 'password': + await this.session.authenticate( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + this.transitionTo('index'); + break; + case 'oidc': + await this.startOIDCAuthentication( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + break; + } + } + + async startOIDCAuthentication(authenticatorName, creds, requestCookies, options) { + const oidc = getOwner(this).lookup(authenticatorName); + oidc.startAuthentication(creds, requestCookies, options); } } From 900f342894a31d7934bbc6ef1858c1764730239b Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 21 Jan 2021 18:19:03 -0500 Subject: [PATCH 05/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20move=20method=20t?= =?UTF-8?q?emplate=20to=20index=20to=20make=20room=20for=20oidc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/admin/app/router.js | 2 +- .../routes/scopes/scope/authenticate/method/index.js | 4 ++++ .../templates/scopes/scope/authenticate/method.hbs | 6 +----- .../scopes/scope/authenticate/method/index.hbs | 5 +++++ .../scopes/scope/authenticate/method/index-test.js | 11 +++++++++++ 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 ui/admin/app/routes/scopes/scope/authenticate/method/index.js create mode 100644 ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs create mode 100644 ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index ddfe7f488..0a3012f9d 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -10,7 +10,7 @@ Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { this.route('authenticate', function () { - this.route('method', { path: ':auth_method_id' }); + this.route('method', { path: ':auth_method_id' }, function() {}); }); this.route('scopes', function () {}); diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/index.js b/ui/admin/app/routes/scopes/scope/authenticate/method/index.js new file mode 100644 index 000000000..17d37582a --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthenticateMethodIndexRoute extends Route { +} diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method.hbs index 6428c8118..c24cd6895 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method.hbs @@ -1,5 +1 @@ - +{{outlet}} diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs new file mode 100644 index 000000000..6428c8118 --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs @@ -0,0 +1,5 @@ + diff --git a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js new file mode 100644 index 000000000..5f6cfcf5f --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/authenticate/method/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/authenticate/method/index'); + assert.ok(route); + }); +}); From ac92575b364a953edef93e62f0ccb27f464cf4f6 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 22 Jan 2021 10:34:05 -0500 Subject: [PATCH 06/30] chore: setup initial oidc polling route (polling to come) --- addons/auth/addon/authenticators/oidc.js | 4 ++-- ui/admin/app/router.js | 4 +++- .../app/routes/scopes/scope/authenticate/method.js | 1 + .../routes/scopes/scope/authenticate/method/oidc.js | 4 ++++ .../scopes/scope/authenticate/method/oidc.hbs | 1 + .../scopes/scope/authenticate/method/oidc-test.js | 11 +++++++++++ 6 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js create mode 100644 ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs create mode 100644 ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 2f95f0517..bd88bdb0b 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -48,8 +48,8 @@ export default class OIDCAuthenticator extends BaseAuthenticator { * `window.open`, but may be overriden. * @param {string} url */ - openExternalOIDCFlow(url) { - window.open(url); + async openExternalOIDCFlow(url) { + await window.open(url); } async authenticate( diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index 0a3012f9d..848cc8e8f 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -10,7 +10,9 @@ Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { this.route('authenticate', function () { - this.route('method', { path: ':auth_method_id' }, function() {}); + this.route('method', { path: ':auth_method_id' }, function() { + this.route('oidc'); + }); }); this.route('scopes', function () {}); diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 499c9a0a0..e5297c6bc 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -55,6 +55,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { requestCookies, { scope, authMethod } ); + this.transitionTo('scopes.scope.authenticate.method.oidc'); break; } } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js new file mode 100644 index 000000000..55d368443 --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { +} diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs new file mode 100644 index 000000000..c3aa832be --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs @@ -0,0 +1 @@ +OIDC yay! diff --git a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js new file mode 100644 index 000000000..a51f158cd --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/authenticate/method/oidc', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/authenticate/method/oidc'); + assert.ok(route); + }); +}); From 3084d6cf06a1de1b8c034aaff9e85ca1e92bc286 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 22 Jan 2021 17:01:12 -0500 Subject: [PATCH 07/30] feature: initial OIDC authentication flow --- addons/api/mirage/route-handlers/auth.js | 27 +++++- addons/auth/addon/authenticators/oidc.js | 87 +++++++++++++------ addons/core/addon/authenticators/oidc.js | 30 ++++++- .../scopes/scope/authenticate/method.js | 22 ++++- .../scopes/scope/authenticate/method/oidc.js | 63 ++++++++++++++ .../scopes/scope/authenticate/method/oidc.hbs | 2 +- ui/admin/config/environment.js | 1 + 7 files changed, 201 insertions(+), 31 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 8f5d48b3f..bc2deb61c 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -23,6 +23,12 @@ import { Response } from 'miragejs'; // } // } +// Quick and dirty counter system to simulate the polling behavior +// of the API. If the OIDC flow is pending, the route returns 100. +// If complete it returns 200 with the token JSON. +let oidcAttemptCounter = 0; +const oidcRequiredAttempts = 3; + const methodHandlers = { password: { authenticate: (payload, scopeAttrs) => { @@ -44,6 +50,7 @@ const methodHandlers = { } } }, + /** * OIDC authentication is a two-step process: * @@ -62,7 +69,25 @@ const methodHandlers = { token_request_id: 'token_request_1234', state: 'base_58_encoded_string' }), - authenticate: () => new Response(200) // TODO + authenticate: (_, scopeAttrs) => { + oidcAttemptCounter++; + if (oidcAttemptCounter < oidcRequiredAttempts) { + return new Response(100); + } else { + return new Response(200, {}, { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }); + } + } } }; diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index bd88bdb0b..4554a9357 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -1,5 +1,6 @@ import BaseAuthenticator from './base'; -import { resolve, reject } from 'rsvp'; +import { inject as service } from '@ember/service'; +import { reject } from 'rsvp'; import fetch from 'fetch'; /** @@ -7,56 +8,92 @@ import fetch from 'fetch'; */ export default class OIDCAuthenticator extends BaseAuthenticator { - // =unimplemented methods - // TODO: implement ;-) + // =services + @service session; + + // =methods + + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ buildAuthEndpointURL(/* { scope: { id: scopeID }, authMethod: { id: authMethodID }, } */) { } + /** + * Generates an auth method URL with which to start authentication. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ buildStartAuthEndpointURL(/* { scope: { id: scopeID }, authMethod: { id: authMethodID }, } */) { } /** + * Kicks-off the OIDC flow: calls the `authenticate:start` action on the + * specified auth method, which returns some meta data along with an + * authorization request URL. * + * @param {boolean} requestCookies request cookie tokens (default `true`) + * @param {object} options + * @return {Promise} */ - async startAuthentication( - creds, - requestCookies = true, - options - ) { + async startAuthentication(requestCookies = true, options) { + const url = this.buildStartAuthEndpointURL(options); const body = JSON.stringify({ token_type: requestCookies ? 'cookie' : null }); - const authEndpointURL = this.buildStartAuthEndpointURL(options); - const response = await fetch(authEndpointURL, { method: 'post', body }); + const response = await fetch(url, { method: 'post', body }); const json = await response.json(); - const normalized = this.normalizeData(json); if (response.status < 400) { - await this.openExternalOIDCFlow(normalized.authorization_request_url); - resolve(normalized); + // Store meta about the pending OIDC flow + this.session.set('data.pending', { + oidc: json + }); + return json; } else { - reject(); + return reject(); } } /** - * Opens the specified URL in a new tab or window. By default this uses - * `window.open`, but may be overriden. - * @param {string} url + * */ - async openExternalOIDCFlow(url) { - await window.open(url); + async authenticate(json) { + console.info('Authenticated with OIDC provider'); + return this.normalizeData(json); } - async authenticate( - // creds, - // requestCookies = true, - // options - ) { - return false; + /** + * + */ + async attemptOIDCAuthentication(options) { + const url = this.buildAuthEndpointURL(options); + const body = JSON.stringify({ + state: this.session.get('data.pending.oidc.state'), + token_request_id: this.session.get('data.pending.oidc.token_request_id') + }); + const response = await fetch(url, { method: 'post', body }); + const json = await response.json(); + if (response.status === 100) { + return false; + } else if (response.status < 400) { + // Authentication with OIDC is indirect... + await this.session.authenticate('authenticator:oidc', json); + return true; + } else { + return reject(); + } } } diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index a805b79d6..4df2735f9 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -10,7 +10,7 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { @service store; - // =attributes + // =methods /** * Generates an auth method URL with which to authenticate. @@ -34,4 +34,32 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); } + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildAuthEndpointURL({ + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + }) { + const adapter = this.store.adapterFor('application'); + const options = { adapterOptions: { scopeID, method: 'authenticate' } }; + return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); + } + + /** + * Generates an auth token validation URL used to check tokens on restoration. + * @override + * @param {string} tokenID + * @return {string} + */ + buildTokenValidationEndpointURL(tokenID) { + const adapter = this.store.adapterFor('auth-token'); + return adapter.buildURL('auth-token', tokenID, {}, 'findRecord'); + } + } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index e5297c6bc..787c42644 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -51,7 +51,6 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { case 'oidc': await this.startOIDCAuthentication( authenticatorName, - creds, requestCookies, { scope, authMethod } ); @@ -60,8 +59,25 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { } } - async startOIDCAuthentication(authenticatorName, creds, requestCookies, options) { + /** + * + */ + async startOIDCAuthentication(authenticatorName, requestCookies, options) { const oidc = getOwner(this).lookup(authenticatorName); - oidc.startAuthentication(creds, requestCookies, options); + // TODO: delegate this call from the session service so that we don't have + // to look up the authenticator directly + const json = await oidc.startAuthentication(requestCookies, options); + await this.openExternalOIDCFlow(json.authorization_request_url); } + + /** + * Opens the specified URL in a new tab or window. By default this uses + * `window.open`, but may be overriden. + * @param {string} url + */ + async openExternalOIDCFlow(url) { + // TODO don't use window directly + await window.open(url); + } + } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js index 55d368443..a31f44a98 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js @@ -1,4 +1,67 @@ import Route from '@ember/routing/route'; +import { getOwner } from '@ember/application'; +import { inject as service } from '@ember/service'; +import { task, timeout } from 'ember-concurrency'; +import config from '../../../../../config/environment'; + +const POLL_TIMEOUT_SECONDS = config.oidcPollingTimeoutSeconds; export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { + + // =services + + @service session; + + // =attributes + + /** + * A simple Ember Concurrency-based polling task that refreshes the route + * every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes + * to session `status` that may occur. + * + * NOTE: tasks are sort of attributes and sort of methods, but they are not + * language-level constructs. Thus we annotate this task as if it + * is an attribute. + * @type {Task} + */ + @task(function * () { + while(!this.session.isAuthenticated) { + yield timeout(POLL_TIMEOUT_SECONDS * 1000); + yield this.refresh(); + } + }).drop() poller; + + // =methods + + model() { + const scope = this.modelFor('scopes.scope'); + const authMethod = this.modelFor('scopes.scope.authenticate.method'); + const authenticatorName = `authenticator:${authMethod.type}`; + const oidc = getOwner(this).lookup(authenticatorName); + // TODO: delegate this call from the session service so that we don't have + // to look up the authenticator directly + return oidc.attemptOIDCAuthentication({ scope, authMethod }); + } + + /** + * If authenticated, redirects to index for further processing. + */ + redirect() { + if (this.session.isAuthenticated) this.replaceWith('index'); + } + + /** + * When this route is activated (entered), begin polling for changes. + */ + activate() { + this.poller.perform(); + } + + /** + * When this route is deactivated (exited), stop polling for changes. + */ + deactivate() { + this.poller.cancelAll(); + } + } diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs index c3aa832be..60c849cf2 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs @@ -1 +1 @@ -OIDC yay! +Checking for OIDC ready … diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js index 7cd8efa3e..00c5d66aa 100644 --- a/ui/admin/config/environment.js +++ b/ui/admin/config/environment.js @@ -36,6 +36,7 @@ module.exports = function (environment) { notifyTimeout: 4000, sessionPollingTimeoutSeconds: 2.5, + oidcPollingTimeoutSeconds: 1, documentation: { baseURL: 'https://boundaryproject.io/help/admin-ui', From e9c297b1cfd43b3d8dce899b76e5ab83647029a9 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 26 Feb 2021 10:42:19 -0500 Subject: [PATCH 08/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20token=20?= =?UTF-8?q?endpoint=20to=20match=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 2 +- addons/auth/addon/authenticators/oidc.js | 4 ++-- addons/core/addon/authenticators/oidc.js | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index bc2deb61c..59a2f896e 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -69,7 +69,7 @@ const methodHandlers = { token_request_id: 'token_request_1234', state: 'base_58_encoded_string' }), - authenticate: (_, scopeAttrs) => { + 'authenticate:token': (_, scopeAttrs) => { oidcAttemptCounter++; if (oidcAttemptCounter < oidcRequiredAttempts) { return new Response(100); diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 4554a9357..ae6e4fbcd 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -22,7 +22,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { * @param {string} options.authMethod.id * @return {string} */ - buildAuthEndpointURL(/* { + buildTokenAuthEndpointURL(/* { scope: { id: scopeID }, authMethod: { id: authMethodID }, } */) { } @@ -79,7 +79,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { * */ async attemptOIDCAuthentication(options) { - const url = this.buildAuthEndpointURL(options); + const url = this.buildTokenAuthEndpointURL(options); const body = JSON.stringify({ state: this.session.get('data.pending.oidc.state'), token_request_id: this.session.get('data.pending.oidc.token_request_id') diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 4df2735f9..964818720 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -35,19 +35,21 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { } /** - * Generates an auth method URL with which to authenticate. + * Generates a URL from which to obtain a ready Boundary token, after the + * OIDC authentication flow is completed. This endpoint may be polled by the + * client until the token becomes available. * @override * @param {object} options * @param {string} options.scope.scope_id * @param {string} options.authMethod.id * @return {string} */ - buildAuthEndpointURL({ + buildTokenAuthEndpointURL({ scope: { id: scopeID }, authMethod: { id: authMethodID }, }) { const adapter = this.store.adapterFor('application'); - const options = { adapterOptions: { scopeID, method: 'authenticate' } }; + const options = { adapterOptions: { scopeID, method: 'authenticate:token' } }; return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); } From 7e6c46de2b78950a632e759ee7b3655b8d3508ac Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 15 Mar 2021 16:18:22 -0400 Subject: [PATCH 09/30] docs: update doc comments --- ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js index a31f44a98..cb0a9b564 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js @@ -40,7 +40,7 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { const oidc = getOwner(this).lookup(authenticatorName); // TODO: delegate this call from the session service so that we don't have // to look up the authenticator directly - return oidc.attemptOIDCAuthentication({ scope, authMethod }); + return oidc.attemptFetchToken({ scope, authMethod }); } /** From ca9485053e871586bc44090342ba369617c6f5ad Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 15 Mar 2021 16:18:48 -0400 Subject: [PATCH 10/30] docs: update doc comments --- addons/auth/addon/authenticators/base.js | 8 +++ addons/auth/addon/authenticators/oidc.js | 71 +++++++++++++++++------- addons/core/addon/authenticators/oidc.js | 6 +- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index 641142222..a9b380813 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -4,6 +4,14 @@ import fetch from 'fetch'; /** * Encapsulates common authenticator functionality. + * + * Upon session invalidation, deauthentication should be attempted at the URL + * generated by `buildDeauthEndpointURL` on a best-effort basis. + * + * When a session is restored, a call should be made to the endpoint specified + * by `buildTokenValidationEndpointURL`. If this endpoint responds with 401 or + * 404, then the session is invalid and the restoration should be rejected. + * All other responses should resolve the session restoration successfully. */ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { // =unimplemented methods diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index ae6e4fbcd..c40e58be0 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -4,7 +4,22 @@ import { reject } from 'rsvp'; import fetch from 'fetch'; /** + * The OIDC base authenticator encapsulates the multistep OIDC flow. * + * 1. Start authentication flow: this step is actually a combination of two + * sub steps: + * 1. Initiate a call via `startAuthentication` to the API at + * `buildStartAuthEndpointURL` to fetch the URL of the external + * authentication provider (`authorization_request_url`). + * 2. Open `authorization_request_url`. This may be either inline (same tab) + * or external in a new tab or browser window. + * 2. Requests the token from `buildTokenAuthEndpointURL`. A token isn't + * guaranteed to be ready, so the OIDC flow can be in a "pending" state. + * + * This authenticator should not be used directly because it does not specify + * URL-building endpoints of its own. + * To use, generate an application authenticator in your app + * `authenticators/application.js` and extend this class. */ export default class OIDCAuthenticator extends BaseAuthenticator { @@ -14,19 +29,6 @@ export default class OIDCAuthenticator extends BaseAuthenticator { // =methods - /** - * Generates an auth method URL with which to authenticate. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildTokenAuthEndpointURL(/* { - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - } */) { } - /** * Generates an auth method URL with which to start authentication. * @override @@ -40,14 +42,28 @@ export default class OIDCAuthenticator extends BaseAuthenticator { authMethod: { id: authMethodID }, } */) { } + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildTokenAuthEndpointURL(/* { + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + } */) { } + /** * Kicks-off the OIDC flow: calls the `authenticate:start` action on the * specified auth method, which returns some meta data along with an - * authorization request URL. + * authorization request URL. The response data is saved for later and + * authentication enters a pending state. * * @param {boolean} requestCookies request cookie tokens (default `true`) * @param {object} options - * @return {Promise} + * @return {object} */ async startAuthentication(requestCookies = true, options) { const url = this.buildStartAuthEndpointURL(options); @@ -68,31 +84,48 @@ export default class OIDCAuthenticator extends BaseAuthenticator { } /** - * + * OIDC is a _process_ and does not have a singular "authenticate" step. Thus + * this authenticate method simply normalizes the data passed into it once + * `attemptFetchToken` completes. Notably, no requests are made by calling + * authenticate. A Boundary token must already have been obtained. + * @param {object} json + * @return {object} */ async authenticate(json) { - console.info('Authenticated with OIDC provider'); return this.normalizeData(json); } /** + * This request attempts to fetch a Boundary token. This method represents a + * single request the polling sequence used to wait for authentication. + * There are three possible outcomes: * + * 1. If the token is ready, it is persisted into the session, the session + * enters an authenticated state, and this method returns `true`. + * 2. If the token is not yet ready, this method returns false. + * 3. If the request errors, this method returns to a rejection, + * requiring appropriate error handling. */ - async attemptOIDCAuthentication(options) { + async attemptFetchToken(options) { + // Get the URL and setup the request body const url = this.buildTokenAuthEndpointURL(options); const body = JSON.stringify({ state: this.session.get('data.pending.oidc.state'), token_request_id: this.session.get('data.pending.oidc.token_request_id') }); + // Fetch the endpoint and get the response JSON const response = await fetch(url, { method: 'post', body }); const json = await response.json(); if (response.status === 100) { + // The token isn't ready yet, keep trying. return false; } else if (response.status < 400) { - // Authentication with OIDC is indirect... + // Response was successful, meaning a token was obtained. + // Authenticate with the session service using the response JSON. await this.session.authenticate('authenticator:oidc', json); return true; } else { + // Response errors, return a rejection for further handling. return reject(); } } diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 964818720..68ae846d4 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -2,7 +2,7 @@ import BaseOIDCAuthenticator from 'auth/authenticators/oidc'; import { inject as service } from '@ember/service'; /** - * + * An OIDC authenticator that handles flow kick-off and polling. */ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { @@ -36,8 +36,8 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { /** * Generates a URL from which to obtain a ready Boundary token, after the - * OIDC authentication flow is completed. This endpoint may be polled by the - * client until the token becomes available. + * OIDC authentication flow is completed. This endpoint may be polled + * until the token becomes available. * @override * @param {object} options * @param {string} options.scope.scope_id From 9e57f838d59388a1bab513e7e1ed2c48675d9496 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 15 Mar 2021 17:08:25 -0400 Subject: [PATCH 11/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20remove=20cookies?= =?UTF-8?q?=20flag=20from=20oidc=20start=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/auth/addon/authenticators/oidc.js | 7 ++----- ui/admin/app/routes/scopes/scope/authenticate/method.js | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index c40e58be0..ad3b1c760 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -65,12 +65,9 @@ export default class OIDCAuthenticator extends BaseAuthenticator { * @param {object} options * @return {object} */ - async startAuthentication(requestCookies = true, options) { + async startAuthentication(options) { const url = this.buildStartAuthEndpointURL(options); - const body = JSON.stringify({ - token_type: requestCookies ? 'cookie' : null - }); - const response = await fetch(url, { method: 'post', body }); + const response = await fetch(url, { method: 'post' }); const json = await response.json(); if (response.status < 400) { // Store meta about the pending OIDC flow diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 787c42644..075c4ae3a 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -51,7 +51,6 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { case 'oidc': await this.startOIDCAuthentication( authenticatorName, - requestCookies, { scope, authMethod } ); this.transitionTo('scopes.scope.authenticate.method.oidc'); @@ -62,11 +61,11 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { /** * */ - async startOIDCAuthentication(authenticatorName, requestCookies, options) { + async startOIDCAuthentication(authenticatorName, options) { const oidc = getOwner(this).lookup(authenticatorName); // TODO: delegate this call from the session service so that we don't have // to look up the authenticator directly - const json = await oidc.startAuthentication(requestCookies, options); + const json = await oidc.startAuthentication(options); await this.openExternalOIDCFlow(json.authorization_request_url); } From 6056b50939081370d8d5a9f642ee7ce97fcfd110 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 15 Mar 2021 17:08:47 -0400 Subject: [PATCH 12/30] chore: manual merge from main --- addons/api/mirage/factories/auth-method.js | 5 +- addons/api/mirage/route-handlers/auth.js | 82 +++++++++++-------- addons/auth/addon/authenticators/oidc.js | 9 +- .../tests/unit/authenticators/oidc-test.js | 2 +- addons/core/addon/authenticators/oidc.js | 10 +-- .../app/components/form/authenticate/oidc.js | 2 +- ui/admin/app/router.js | 2 +- .../scopes/scope/authenticate/method.js | 9 +- .../scopes/scope/authenticate/method/index.js | 3 +- .../scopes/scope/authenticate/method/oidc.js | 1 + .../tests/acceptance/authentication-test.js | 4 +- .../scope/authenticate/method/index-test.js | 19 +++-- .../scope/authenticate/method/oidc-test.js | 19 +++-- ui/desktop/electron-app/src/index.js | 14 ++-- 14 files changed, 102 insertions(+), 79 deletions(-) diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js index ed8ca6947..b7dc8ddda 100644 --- a/addons/api/mirage/factories/auth-method.js +++ b/addons/api/mirage/factories/auth-method.js @@ -1,10 +1,7 @@ import factory from '../generated/factories/auth-method'; import { trait } from 'ember-cli-mirage'; -const types = [ - 'password', - 'oidc' -]; +const types = ['password', 'oidc']; export default factory.extend({ id: (i) => `auth-method-id-${i}`, diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 59a2f896e..8873d5333 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -35,20 +35,24 @@ const methodHandlers = { if (payload.credentials.login_name === 'error') { return new Response(400); } else { - return new Response(200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }); + return new Response( + 200, + {}, + { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '', + } + ); } - } + }, }, /** @@ -63,37 +67,47 @@ const methodHandlers = { */ oidc: { 'authenticate:start': () => - new Response(200, {}, { - authorization_request_url: 'https://www.duckduckgo.com', - retreival_url: '', - token_request_id: 'token_request_1234', - state: 'base_58_encoded_string' - }), + new Response( + 200, + {}, + { + authorization_request_url: 'https://www.duckduckgo.com', + retreival_url: '', + token_request_id: 'token_request_1234', + state: 'base_58_encoded_string', + } + ), 'authenticate:token': (_, scopeAttrs) => { oidcAttemptCounter++; if (oidcAttemptCounter < oidcRequiredAttempts) { return new Response(100); } else { - return new Response(200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }); + return new Response( + 200, + {}, + { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '', + } + ); } - } - } + }, + }, }; export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); + const [, id, method] = request.params.id_method.match( + /(?.[^:]*)(?::)?(?.*)?/ + ); const authMethod = authMethods.find(id); const scope = scopes.find(authMethod.scopeId); const scopeAttrs = this.serialize(scopes.find(scope.id)); diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index ad3b1c760..583154b2a 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -22,7 +22,6 @@ import fetch from 'fetch'; * `authenticators/application.js` and extend this class. */ export default class OIDCAuthenticator extends BaseAuthenticator { - // =services @service session; @@ -40,7 +39,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { buildStartAuthEndpointURL(/* { scope: { id: scopeID }, authMethod: { id: authMethodID }, - } */) { } + } */) {} /** * Generates an auth method URL with which to authenticate. @@ -53,7 +52,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { buildTokenAuthEndpointURL(/* { scope: { id: scopeID }, authMethod: { id: authMethodID }, - } */) { } + } */) {} /** * Kicks-off the OIDC flow: calls the `authenticate:start` action on the @@ -72,7 +71,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { if (response.status < 400) { // Store meta about the pending OIDC flow this.session.set('data.pending', { - oidc: json + oidc: json, }); return json; } else { @@ -108,7 +107,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { const url = this.buildTokenAuthEndpointURL(options); const body = JSON.stringify({ state: this.session.get('data.pending.oidc.state'), - token_request_id: this.session.get('data.pending.oidc.token_request_id') + token_request_id: this.session.get('data.pending.oidc.token_request_id'), }); // Fetch the endpoint and get the response JSON const response = await fetch(url, { method: 'post', body }); diff --git a/addons/auth/tests/unit/authenticators/oidc-test.js b/addons/auth/tests/unit/authenticators/oidc-test.js index c5498f123..d40df7d15 100644 --- a/addons/auth/tests/unit/authenticators/oidc-test.js +++ b/addons/auth/tests/unit/authenticators/oidc-test.js @@ -1,4 +1,4 @@ -import { module/*, test */} from 'qunit'; +import { module /*, test */ } from 'qunit'; import { setupTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 68ae846d4..abb9179e3 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -5,7 +5,6 @@ import { inject as service } from '@ember/service'; * An OIDC authenticator that handles flow kick-off and polling. */ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { - // =services @service store; @@ -28,8 +27,8 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { const options = { adapterOptions: { scopeID, - method: 'authenticate:start' - } + method: 'authenticate:start', + }, }; return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); } @@ -49,7 +48,9 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { authMethod: { id: authMethodID }, }) { const adapter = this.store.adapterFor('application'); - const options = { adapterOptions: { scopeID, method: 'authenticate:token' } }; + const options = { + adapterOptions: { scopeID, method: 'authenticate:token' }, + }; return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); } @@ -63,5 +64,4 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { const adapter = this.store.adapterFor('auth-token'); return adapter.buildURL('auth-token', tokenID, {}, 'findRecord'); } - } diff --git a/addons/core/app/components/form/authenticate/oidc.js b/addons/core/app/components/form/authenticate/oidc.js index cbb3b8ab4..7e7fe7a85 100644 --- a/addons/core/app/components/form/authenticate/oidc.js +++ b/addons/core/app/components/form/authenticate/oidc.js @@ -1 +1 @@ -export { default } from 'core/components/form/authenticate/oidc'; \ No newline at end of file +export { default } from 'core/components/form/authenticate/oidc'; diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index 848cc8e8f..b300a1333 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -10,7 +10,7 @@ Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { this.route('authenticate', function () { - this.route('method', { path: ':auth_method_id' }, function() { + this.route('method', { path: ':auth_method_id' }, function () { this.route('oidc'); }); }); diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 075c4ae3a..7adf58b98 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -49,10 +49,10 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { this.transitionTo('index'); break; case 'oidc': - await this.startOIDCAuthentication( - authenticatorName, - { scope, authMethod } - ); + await this.startOIDCAuthentication(authenticatorName, { + scope, + authMethod, + }); this.transitionTo('scopes.scope.authenticate.method.oidc'); break; } @@ -78,5 +78,4 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { // TODO don't use window directly await window.open(url); } - } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/index.js b/ui/admin/app/routes/scopes/scope/authenticate/method/index.js index 17d37582a..fc9b10575 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method/index.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class ScopesScopeAuthenticateMethodIndexRoute extends Route { -} +export default class ScopesScopeAuthenticateMethodIndexRoute extends Route {} diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js index cb0a9b564..1e3f97e79 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js @@ -29,6 +29,7 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { yield timeout(POLL_TIMEOUT_SECONDS * 1000); yield this.refresh(); } + /* eslint-disable-next-line prettier/prettier */ }).drop() poller; // =methods diff --git a/ui/admin/tests/acceptance/authentication-test.js b/ui/admin/tests/acceptance/authentication-test.js index b51db943d..3578ae935 100644 --- a/ui/admin/tests/acceptance/authentication-test.js +++ b/ui/admin/tests/acceptance/authentication-test.js @@ -66,11 +66,11 @@ module('Acceptance | authentication', function (hooks) { scope = { id: orgScope.id, type: orgScope.type }; globalAuthMethod = this.server.create('auth-method', { scope: globalScope, - type: 'password' + type: 'password', }); authMethod = this.server.create('auth-method', { scope: orgScope, - type: 'password' + type: 'password', }); orgScopeID = orgScope.id; globalAuthMethodID = globalAuthMethod.id; diff --git a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js index 5f6cfcf5f..10a6a561c 100644 --- a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js +++ b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/index-test.js @@ -1,11 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/authenticate/method/index', function(hooks) { - setupTest(hooks); +module( + 'Unit | Route | scopes/scope/authenticate/method/index', + function (hooks) { + setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:scopes/scope/authenticate/method/index'); - assert.ok(route); - }); -}); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/authenticate/method/index' + ); + assert.ok(route); + }); + } +); diff --git a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js index a51f158cd..2c2764470 100644 --- a/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js +++ b/ui/admin/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js @@ -1,11 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/authenticate/method/oidc', function(hooks) { - setupTest(hooks); +module( + 'Unit | Route | scopes/scope/authenticate/method/oidc', + function (hooks) { + setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:scopes/scope/authenticate/method/oidc'); - assert.ok(route); - }); -}); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/authenticate/method/oidc' + ); + assert.ok(route); + }); + } +); diff --git a/ui/desktop/electron-app/src/index.js b/ui/desktop/electron-app/src/index.js index 4ed11956a..9dff06506 100644 --- a/ui/desktop/electron-app/src/index.js +++ b/ui/desktop/electron-app/src/index.js @@ -57,7 +57,8 @@ app.on('ready', async () => { // Register custom protocol // This file protocol is the exclusive protocol for this application. All // other protocols are disabled. - ses.protocol.registerFileProtocol(emberAppProtocol, (request, callback) => { /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ + ses.protocol.registerFileProtocol(emberAppProtocol, (request, callback) => { + /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ const isDir = request.url.endsWith('/'); const absolutePath = request.url.substr(emberAppURL.length); const normalizedPath = isDir @@ -71,7 +72,8 @@ app.on('ready', async () => { // Disallow all permissions requests, // per Electronegativity PERMISSION_REQUEST_HANDLER_GLOBAL_CHECK - ses.setPermissionRequestHandler((webContents, permission, callback) => { /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ + ses.setPermissionRequestHandler((webContents, permission, callback) => { + /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ return callback(false); }); @@ -119,7 +121,7 @@ app.on('ready', async () => { allowRunningInsecureContent: false, // The preload script establishes the message-based IPC pathway without // exposing new modules to the renderer. - preload: preloadPath, /* eng-disable PRELOAD_JS_CHECK */ + preload: preloadPath /* eng-disable PRELOAD_JS_CHECK */, disableBlinkFeatures: 'Auxclick', }, }); @@ -151,10 +153,12 @@ app.on('ready', async () => { // Prevent navigation outside of serve://boundary per // Electronegativity LIMIT_NAVIGATION_GLOBAL_CHECK - mainWindow.webContents.on('will-navigate', (event, url) => { /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ + mainWindow.webContents.on('will-navigate', (event, url) => { + /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ if (!url.startsWith('serve://boundary')) event.preventDefault(); }); - mainWindow.webContents.on('new-window', (event, url) => { /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ + mainWindow.webContents.on('new-window', (event, url) => { + /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ if (!url.startsWith('serve://boundary')) event.preventDefault(); }); From 0219b5c99a90b615a1047a4a59250fb916023e23 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 15 Mar 2021 17:14:23 -0400 Subject: [PATCH 13/30] fix: move electronegativity comments back to same line --- ui/desktop/electron-app/src/index.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ui/desktop/electron-app/src/index.js b/ui/desktop/electron-app/src/index.js index 9dff06506..c3223e422 100644 --- a/ui/desktop/electron-app/src/index.js +++ b/ui/desktop/electron-app/src/index.js @@ -57,8 +57,7 @@ app.on('ready', async () => { // Register custom protocol // This file protocol is the exclusive protocol for this application. All // other protocols are disabled. - ses.protocol.registerFileProtocol(emberAppProtocol, (request, callback) => { - /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ + ses.protocol.registerFileProtocol(emberAppProtocol, (request, callback) => { /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ const isDir = request.url.endsWith('/'); const absolutePath = request.url.substr(emberAppURL.length); const normalizedPath = isDir @@ -72,8 +71,7 @@ app.on('ready', async () => { // Disallow all permissions requests, // per Electronegativity PERMISSION_REQUEST_HANDLER_GLOBAL_CHECK - ses.setPermissionRequestHandler((webContents, permission, callback) => { - /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ + ses.setPermissionRequestHandler((webContents, permission, callback) => { /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ return callback(false); }); @@ -153,12 +151,10 @@ app.on('ready', async () => { // Prevent navigation outside of serve://boundary per // Electronegativity LIMIT_NAVIGATION_GLOBAL_CHECK - mainWindow.webContents.on('will-navigate', (event, url) => { - /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ + mainWindow.webContents.on('will-navigate', (event, url) => { /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ if (!url.startsWith('serve://boundary')) event.preventDefault(); }); - mainWindow.webContents.on('new-window', (event, url) => { - /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ + mainWindow.webContents.on('new-window', (event, url) => { /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ if (!url.startsWith('serve://boundary')) event.preventDefault(); }); From 760710ae2397300551429d6a822a8da013a48f47 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 16 Mar 2021 14:02:34 -0400 Subject: [PATCH 14/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20polling=20er?= =?UTF-8?q?ror=20notification=20and=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scopes/scope/authenticate/method/oidc.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js index 1e3f97e79..b6cb354c4 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method/oidc.js @@ -2,6 +2,8 @@ import Route from '@ember/routing/route'; import { getOwner } from '@ember/application'; import { inject as service } from '@ember/service'; import { task, timeout } from 'ember-concurrency'; +import { action } from '@ember/object'; +import { notifyError } from 'core/decorators/notify'; import config from '../../../../../config/environment'; const POLL_TIMEOUT_SECONDS = config.oidcPollingTimeoutSeconds; @@ -65,4 +67,16 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { this.poller.cancelAll(); } + /** + * An error in this route indicates authentication failed. The user is + * notified and returned to the index. + */ + @action + @notifyError(() => 'errors.authentication-failed.title', { catch: true }) + error(e) { + this.transitionTo('scopes.scope.authenticate.method.index'); + // rethrow the error to activate the notifyError decorator + throw e; + } + } From f406fc38610e7e3f2c220a4937db73197f35ed18 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 16 Mar 2021 15:41:13 -0400 Subject: [PATCH 15/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20oidc=20auth=20met?= =?UTF-8?q?hods=20are=20not=20yet=20editable=20in=20admin=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/models/auth-method.js | 18 +++++++++++++++++- addons/core/translations/resources/en-us.yaml | 1 + .../scopes/scope/auth-methods/index.hbs | 14 +++++++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index ad795fd7d..a47019a23 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -1,3 +1,19 @@ import GeneratedAuthMethodModel from '../generated/models/auth-method'; -export default class AuthMethodModel extends GeneratedAuthMethodModel {} +export default class AuthMethodModel extends GeneratedAuthMethodModel { + // =attributes + + /** + * @type {boolean} + */ + get isPassword() { + return this.type === 'password'; + } + + /** + * @type {boolean} + */ + get isOIDC() { + return this.type === 'oidc'; + } +} diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index e670a70d7..4a9aaeca1 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -26,6 +26,7 @@ auth-method: description: No auth methods were found for this scope. types: password: Password + oidc: OIDC actions: create: Create Auth Method delete: Delete Auth Method 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 e0c150dfa..a400f20c4 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -43,12 +43,16 @@ {{#each @model as |authMethod|}} - + {{#if authMethod.isPassword}} + + {{authMethod.displayName}} + + {{else}} {{authMethod.displayName}} - + {{/if}}

{{authMethod.description}}

From 96ef767e6548e69c85c39d6daa32f1dbdd17f47a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 19 Mar 2021 17:03:03 -0400 Subject: [PATCH 16/30] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20start=20auth?= =?UTF-8?q?=20oidc=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/dummy/app/authenticators/oidc.js | 15 ++++++++++ .../tests/unit/authenticators/oidc-test.js | 30 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 addons/auth/tests/dummy/app/authenticators/oidc.js diff --git a/addons/auth/tests/dummy/app/authenticators/oidc.js b/addons/auth/tests/dummy/app/authenticators/oidc.js new file mode 100644 index 000000000..f8ac3d57f --- /dev/null +++ b/addons/auth/tests/dummy/app/authenticators/oidc.js @@ -0,0 +1,15 @@ +import OIDCAuthenticator from 'auth/authenticators/oidc'; + +export default class ApplicationOIDCAuthenticator extends OIDCAuthenticator { + buildTokenValidationEndpointURL(id) { + return `/validate/${id}`; + } + + buildStartAuthEndpointURL() { + return '/authenticate:start'; + } + + buildDeauthEndpointURL() { + return '/deauthenticate'; + } +} diff --git a/addons/auth/tests/unit/authenticators/oidc-test.js b/addons/auth/tests/unit/authenticators/oidc-test.js index d40df7d15..e8dfd7216 100644 --- a/addons/auth/tests/unit/authenticators/oidc-test.js +++ b/addons/auth/tests/unit/authenticators/oidc-test.js @@ -1,8 +1,36 @@ -import { module /*, test */ } from 'qunit'; +import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import { Response } from 'miragejs'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; module('Unit | Authenticator | OIDC', function (hooks) { setupTest(hooks); setupMirage(hooks); + + test('`startAuthentication` initiates the authentication flow and returns the response', async function (assert) { + assert.expect(2); + const authenticator = this.owner.lookup('authenticator:oidc'); + this.server.post( + authenticator.buildStartAuthEndpointURL(), + () => new Response(200, {}, { foo: 'bar' }) + ); + const response = await authenticator.startAuthentication(); + assert.ok(true, 'authentication succeeded'); + assert.deepEqual(response, { foo: 'bar' }); + }); + + test('`startAuthentication` rejects/errors if API returns an error response', async function (assert) { + assert.expect(1); + const authenticator = this.owner.lookup('authenticator:oidc'); + this.server.post( + authenticator.buildStartAuthEndpointURL(), + () => new Response(400) + ); + // Because assert.throws doesn't work with async errors/promise rejections + try { + await authenticator.startAuthentication(); + } catch (e) { + assert.ok(true); + } + }); }); From 692700804125c075754f8c6712c6f8c6120d4efa Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Apr 2021 16:16:46 -0400 Subject: [PATCH 17/30] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20use=20command?= =?UTF-8?q?=20instead=20of=20action=20for=20auth=20endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 15 +++---- addons/auth/addon/authenticators/base.js | 13 ++++++ addons/auth/addon/authenticators/oidc.js | 43 +++++-------------- addons/auth/addon/authenticators/password.js | 16 +------ .../tests/dummy/app/authenticators/oidc.js | 4 +- .../tests/unit/authenticators/oidc-test.js | 4 +- .../unit/authenticators/password-test.js | 1 + addons/core/addon/authenticators/oidc.js | 30 +------------ 8 files changed, 38 insertions(+), 88 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 8873d5333..b96411ee3 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -29,9 +29,9 @@ import { Response } from 'miragejs'; let oidcAttemptCounter = 0; const oidcRequiredAttempts = 3; -const methodHandlers = { +const commandHandlers = { password: { - authenticate: (payload, scopeAttrs) => { + login: (payload, scopeAttrs) => { if (payload.credentials.login_name === 'error') { return new Response(400); } else { @@ -66,7 +66,7 @@ const methodHandlers = { * the authentication flow is completed. */ oidc: { - 'authenticate:start': () => + start: () => new Response( 200, {}, @@ -77,7 +77,7 @@ const methodHandlers = { state: 'base_58_encoded_string', } ), - 'authenticate:token': (_, scopeAttrs) => { + token: (_, scopeAttrs) => { oidcAttemptCounter++; if (oidcAttemptCounter < oidcRequiredAttempts) { return new Response(100); @@ -105,13 +105,12 @@ const methodHandlers = { export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - const [, id, method] = request.params.id_method.match( - /(?.[^:]*)(?::)?(?.*)?/ - ); + const [, id] = request.params.id_method.match(/(?.[^:]*)/); + const { command } = payload; const authMethod = authMethods.find(id); const scope = scopes.find(authMethod.scopeId); const scopeAttrs = this.serialize(scopes.find(scope.id)); - return methodHandlers[authMethod.type][method](payload, scopeAttrs); + return commandHandlers[authMethod.type][command](payload, scopeAttrs); } export function deauthHandler() { diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index a9b380813..1611dd6ec 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -16,6 +16,19 @@ import fetch from 'fetch'; export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { // =unimplemented methods + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildAuthEndpointURL(/* { + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + } */) {} + /** * Generates a scope URL with which to deauthenticate. * @override diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 583154b2a..28c6213d9 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -9,12 +9,13 @@ import fetch from 'fetch'; * 1. Start authentication flow: this step is actually a combination of two * sub steps: * 1. Initiate a call via `startAuthentication` to the API at - * `buildStartAuthEndpointURL` to fetch the URL of the external - * authentication provider (`authorization_request_url`). + * `buildAuthEndpointURL` using the `start` command to fetch the URL of + * the external authentication provider (`authorization_request_url`). * 2. Open `authorization_request_url`. This may be either inline (same tab) * or external in a new tab or browser window. - * 2. Requests the token from `buildTokenAuthEndpointURL`. A token isn't - * guaranteed to be ready, so the OIDC flow can be in a "pending" state. + * 2. Requests the token from `buildAuthEndpointURL` using the `token` command. + * A token isn't guaranteed to be ready, so the OIDC flow can be in a + * "pending" state. * * This authenticator should not be used directly because it does not specify * URL-building endpoints of its own. @@ -28,32 +29,6 @@ export default class OIDCAuthenticator extends BaseAuthenticator { // =methods - /** - * Generates an auth method URL with which to start authentication. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildStartAuthEndpointURL(/* { - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - } */) {} - - /** - * Generates an auth method URL with which to authenticate. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildTokenAuthEndpointURL(/* { - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - } */) {} - /** * Kicks-off the OIDC flow: calls the `authenticate:start` action on the * specified auth method, which returns some meta data along with an @@ -65,8 +40,9 @@ export default class OIDCAuthenticator extends BaseAuthenticator { * @return {object} */ async startAuthentication(options) { - const url = this.buildStartAuthEndpointURL(options); - const response = await fetch(url, { method: 'post' }); + const url = this.buildAuthEndpointURL(options); + const body = JSON.stringify({ command: 'start' }); + const response = await fetch(url, { method: 'post', body }); const json = await response.json(); if (response.status < 400) { // Store meta about the pending OIDC flow @@ -104,8 +80,9 @@ export default class OIDCAuthenticator extends BaseAuthenticator { */ async attemptFetchToken(options) { // Get the URL and setup the request body - const url = this.buildTokenAuthEndpointURL(options); + const url = this.buildAuthEndpointURL(options); const body = JSON.stringify({ + command: 'token', state: this.session.get('data.pending.oidc.state'), token_request_id: this.session.get('data.pending.oidc.token_request_id'), }); diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js index e130a23ee..6b22ee61b 100644 --- a/addons/auth/addon/authenticators/password.js +++ b/addons/auth/addon/authenticators/password.js @@ -34,21 +34,6 @@ import fetch from 'fetch'; * */ export default class PasswordAuthenticator extends BaseAuthenticator { - // =unimplemented methods - - /** - * Generates an auth method URL with which to authenticate. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildAuthEndpointURL(/* { - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - } */) {} - // =methods /** @@ -71,6 +56,7 @@ export default class PasswordAuthenticator extends BaseAuthenticator { options ) { const body = JSON.stringify({ + command: 'login', token_type: requestCookies ? 'cookie' : null, credentials: { login_name, password }, }); diff --git a/addons/auth/tests/dummy/app/authenticators/oidc.js b/addons/auth/tests/dummy/app/authenticators/oidc.js index f8ac3d57f..b6508934b 100644 --- a/addons/auth/tests/dummy/app/authenticators/oidc.js +++ b/addons/auth/tests/dummy/app/authenticators/oidc.js @@ -5,8 +5,8 @@ export default class ApplicationOIDCAuthenticator extends OIDCAuthenticator { return `/validate/${id}`; } - buildStartAuthEndpointURL() { - return '/authenticate:start'; + buildAuthEndpointURL() { + return '/authenticate'; } buildDeauthEndpointURL() { diff --git a/addons/auth/tests/unit/authenticators/oidc-test.js b/addons/auth/tests/unit/authenticators/oidc-test.js index e8dfd7216..e2e7bb0a9 100644 --- a/addons/auth/tests/unit/authenticators/oidc-test.js +++ b/addons/auth/tests/unit/authenticators/oidc-test.js @@ -11,7 +11,7 @@ module('Unit | Authenticator | OIDC', function (hooks) { assert.expect(2); const authenticator = this.owner.lookup('authenticator:oidc'); this.server.post( - authenticator.buildStartAuthEndpointURL(), + authenticator.buildAuthEndpointURL(), () => new Response(200, {}, { foo: 'bar' }) ); const response = await authenticator.startAuthentication(); @@ -23,7 +23,7 @@ module('Unit | Authenticator | OIDC', function (hooks) { assert.expect(1); const authenticator = this.owner.lookup('authenticator:oidc'); this.server.post( - authenticator.buildStartAuthEndpointURL(), + authenticator.buildAuthEndpointURL(), () => new Response(400) ); // Because assert.throws doesn't work with async errors/promise rejections diff --git a/addons/auth/tests/unit/authenticators/password-test.js b/addons/auth/tests/unit/authenticators/password-test.js index 3bac47edb..a444702bb 100644 --- a/addons/auth/tests/unit/authenticators/password-test.js +++ b/addons/auth/tests/unit/authenticators/password-test.js @@ -47,6 +47,7 @@ module('Unit | Authenticator | password', function (hooks) { (schema, request) => { const json = JSON.parse(request.requestBody); assert.deepEqual(json, { + command: 'login', token_type: 'cookie', credentials: { login_name: 'foo', diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index abb9179e3..b23bea9bd 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -19,38 +19,12 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { * @param {string} options.authMethod.id * @return {string} */ - buildStartAuthEndpointURL({ + buildAuthEndpointURL({ scope: { id: scopeID }, authMethod: { id: authMethodID }, }) { const adapter = this.store.adapterFor('application'); - const options = { - adapterOptions: { - scopeID, - method: 'authenticate:start', - }, - }; - return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); - } - - /** - * Generates a URL from which to obtain a ready Boundary token, after the - * OIDC authentication flow is completed. This endpoint may be polled - * until the token becomes available. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildTokenAuthEndpointURL({ - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - }) { - const adapter = this.store.adapterFor('application'); - const options = { - adapterOptions: { scopeID, method: 'authenticate:token' }, - }; + const options = { adapterOptions: { scopeID, method: 'authenticate' } }; return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); } From 3d13d8f412ee60f01f7d7ceeff825eca58201e25 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 1 Apr 2021 17:41:30 -0400 Subject: [PATCH 18/30] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20auth=20payload?= =?UTF-8?q?s=20now=20have=20"attributes"=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 58 ++++++----- addons/auth/addon/authenticators/base.js | 11 ++- addons/auth/addon/authenticators/oidc.js | 8 +- addons/auth/addon/authenticators/password.js | 2 +- .../tests/unit/authenticators/base-test.js | 42 ++++---- .../unit/authenticators/password-test.js | 2 +- addons/core/addon/authenticators/password.js | 4 +- .../unit/authenticators/password-test.js | 12 +-- .../scopes/scope/authenticate/method.js | 2 +- ui/desktop/app/authenticators/password.js | 98 ------------------- .../unit/authenticators/password-test.js | 53 ---------- 11 files changed, 74 insertions(+), 218 deletions(-) delete mode 100644 ui/desktop/app/authenticators/password.js delete mode 100644 ui/desktop/tests/unit/authenticators/password-test.js diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index b96411ee3..4c7c3f1d0 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -12,7 +12,7 @@ import { Response } from 'miragejs'; // */ // export default function authenticateHandler(schema, request) { // const payload = JSON.parse(request.requestBody); -// if (payload.credentials.login_name === 'error') { +// if (payload.attributes.login_name === 'error') { // return new Response(400); // } else { // const cookieName = config.auth.passwordCookieName; @@ -32,23 +32,25 @@ const oidcRequiredAttempts = 3; const commandHandlers = { password: { login: (payload, scopeAttrs) => { - if (payload.credentials.login_name === 'error') { + if (payload.attributes.login_name === 'error') { return new Response(400); } else { return new Response( 200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '', + attributes: { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '', + }, } ); } @@ -71,10 +73,12 @@ const commandHandlers = { 200, {}, { - authorization_request_url: 'https://www.duckduckgo.com', - retreival_url: '', - token_request_id: 'token_request_1234', - state: 'base_58_encoded_string', + attributes: { + authorization_request_url: 'https://www.duckduckgo.com', + retreival_url: '', + token_request_id: 'token_request_1234', + state: 'base_58_encoded_string', + }, } ), token: (_, scopeAttrs) => { @@ -86,16 +90,18 @@ const commandHandlers = { 200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '', + attributes: { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '', + }, } ); } diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index 1611dd6ec..bdc69d504 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -72,9 +72,10 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { */ async restore(data) { if (!data) return reject(); - return this.validateToken(data.token, data.id).then(() => - this.normalizeData(data) - ); + return this.validateToken( + data.attributes.token, + data.attributes.id + ).then(() => this.normalizeData(data)); } /** @@ -86,6 +87,10 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { * @return {object} */ normalizeData(data, username) { + // Pull fields up from `data.attributes` for easier access in JavaScript. + // The `attributes` field exists on the Go side for its convenience but is + // unnecessary here. + Object.assign(data, data.attributes); // Add booleans indicated the scope type data.isGlobal = data?.scope?.type === 'global'; data.isOrg = data?.scope?.type === 'org'; diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 28c6213d9..87c2ce198 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -83,8 +83,12 @@ export default class OIDCAuthenticator extends BaseAuthenticator { const url = this.buildAuthEndpointURL(options); const body = JSON.stringify({ command: 'token', - state: this.session.get('data.pending.oidc.state'), - token_request_id: this.session.get('data.pending.oidc.token_request_id'), + attributes: { + state: this.session.get('data.pending.oidc.state'), + token_request_id: this.session.get( + 'data.pending.oidc.token_request_id' + ), + }, }); // Fetch the endpoint and get the response JSON const response = await fetch(url, { method: 'post', body }); diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js index 6b22ee61b..3e0eb53e4 100644 --- a/addons/auth/addon/authenticators/password.js +++ b/addons/auth/addon/authenticators/password.js @@ -58,7 +58,7 @@ export default class PasswordAuthenticator extends BaseAuthenticator { const body = JSON.stringify({ command: 'login', token_type: requestCookies ? 'cookie' : null, - credentials: { login_name, password }, + attributes: { login_name, password }, }); const authEndpointURL = this.buildAuthEndpointURL(options); const response = await fetch(authEndpointURL, { method: 'post', body }); diff --git a/addons/auth/tests/unit/authenticators/base-test.js b/addons/auth/tests/unit/authenticators/base-test.js index 726e76162..31d2b3603 100644 --- a/addons/auth/tests/unit/authenticators/base-test.js +++ b/addons/auth/tests/unit/authenticators/base-test.js @@ -32,29 +32,25 @@ module('Unit | Authenticator | base', function (hooks) { test('it passes token validation on restoration if receiving 200', async function (assert) { assert.expect(1); - const mockData = { id: 'token_123' }; + const id = 'token_123'; + const mockData = { attributes: { id } }; const authenticator = this.owner.lookup('authenticator:base'); - this.server.get( - authenticator.buildTokenValidationEndpointURL(mockData.id), - () => { - assert.ok(true, 'token validation was requested'); - return new Response(200); - } - ); + this.server.get(authenticator.buildTokenValidationEndpointURL(id), () => { + assert.ok(true, 'token validation was requested'); + return new Response(200); + }); await authenticator.restore(mockData); }); test('it fails token validation on restoration if receiving 401', async function (assert) { assert.expect(2); - const mockData = { id: 'token_123' }; + const id = 'token_123'; + const mockData = { attributes: { id } }; const authenticator = this.owner.lookup('authenticator:base'); - this.server.get( - authenticator.buildTokenValidationEndpointURL(mockData.id), - () => { - assert.ok(true, 'token validation was requested'); - return new Response(401); - } - ); + this.server.get(authenticator.buildTokenValidationEndpointURL(id), () => { + assert.ok(true, 'token validation was requested'); + return new Response(401); + }); try { await authenticator.restore(mockData); } catch (e) { @@ -64,15 +60,13 @@ module('Unit | Authenticator | base', function (hooks) { test('it fails token validation on restoration if receiving 404', async function (assert) { assert.expect(2); - const mockData = { id: 'token_123' }; + const id = 'token_123'; + const mockData = { attributes: { id } }; const authenticator = this.owner.lookup('authenticator:base'); - this.server.get( - authenticator.buildTokenValidationEndpointURL(mockData.id), - () => { - assert.ok(true, 'token validation was requested'); - return new Response(404); - } - ); + this.server.get(authenticator.buildTokenValidationEndpointURL(id), () => { + assert.ok(true, 'token validation was requested'); + return new Response(404); + }); try { await authenticator.restore(mockData); } catch (e) { diff --git a/addons/auth/tests/unit/authenticators/password-test.js b/addons/auth/tests/unit/authenticators/password-test.js index a444702bb..9799101e7 100644 --- a/addons/auth/tests/unit/authenticators/password-test.js +++ b/addons/auth/tests/unit/authenticators/password-test.js @@ -49,7 +49,7 @@ module('Unit | Authenticator | password', function (hooks) { assert.deepEqual(json, { command: 'login', token_type: 'cookie', - credentials: { + attributes: { login_name: 'foo', password: 'bar', }, diff --git a/addons/core/addon/authenticators/password.js b/addons/core/addon/authenticators/password.js index c76874ce9..186731a7d 100644 --- a/addons/core/addon/authenticators/password.js +++ b/addons/core/addon/authenticators/password.js @@ -62,7 +62,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { */ authenticate() { return super.authenticate(...arguments).then((data) => { - const token = data?.token; + const token = data?.attributes?.token; this.addTokenToAuthorization(token); return data; }); @@ -76,7 +76,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @return {object} */ restore(data) { - const token = data?.token; + const token = data?.attributes?.token; this.addTokenToAuthorization(token); return super.restore(data); } diff --git a/addons/core/tests/unit/authenticators/password-test.js b/addons/core/tests/unit/authenticators/password-test.js index a7580ef7d..2542aed56 100644 --- a/addons/core/tests/unit/authenticators/password-test.js +++ b/addons/core/tests/unit/authenticators/password-test.js @@ -26,13 +26,11 @@ module('Unit | Authenticator | password', function (hooks) { test('it adds an authorization header to application adapter on restore', async function (assert) { assert.expect(1); - const mockData = { id: 'token1234', token: 'token1234' }; - this.server.get( - authenticator.buildTokenValidationEndpointURL(mockData.id), - () => { - return new Response(200); - } - ); + const id = 'token1234'; + const mockData = { attributes: { id, token: 'token1234' } }; + this.server.get(authenticator.buildTokenValidationEndpointURL(id), () => { + return new Response(200); + }); authenticator.restore(mockData); assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); }); diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 7adf58b98..302f405a1 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -66,7 +66,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { // TODO: delegate this call from the session service so that we don't have // to look up the authenticator directly const json = await oidc.startAuthentication(options); - await this.openExternalOIDCFlow(json.authorization_request_url); + await this.openExternalOIDCFlow(json.attributes.authorization_request_url); } /** diff --git a/ui/desktop/app/authenticators/password.js b/ui/desktop/app/authenticators/password.js deleted file mode 100644 index f9d97033e..000000000 --- a/ui/desktop/app/authenticators/password.js +++ /dev/null @@ -1,98 +0,0 @@ -import BasePasswordAuthenticator from 'auth/authenticators/password'; -import { inject as service } from '@ember/service'; - -/** - * A username/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 PasswordAuthenticator extends BasePasswordAuthenticator { - // =services - - @service store; - - // =attributes - - /** - * Generates an auth method URL with which to authenticate. - * @override - * @param {object} options - * @param {string} options.scope.scope_id - * @param {string} options.authMethod.id - * @return {string} - */ - buildAuthEndpointURL({ - scope: { id: scopeID }, - authMethod: { id: authMethodID }, - }) { - const adapter = this.store.adapterFor('application'); - const options = { adapterOptions: { scopeID, method: 'authenticate' } }; - return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); - } - - /** - * Generates a scope URL with which to deauthenticate. - * @override - * @param {object} options - * @param {string} scopeID - * @return {string} - */ - buildDeauthEndpointURL({ scope: { id: scopeID } }) { - const adapter = this.store.adapterFor('application'); - const options = { adapterOptions: { method: 'deauthenticate' } }; - return adapter.buildURL('scope', scopeID, options, 'findRecord'); - } - - /** - * Generates an auth token validation URL used to check tokens on restoration. - * @override - * @param {string} tokenID - * @return {string} - */ - buildTokenValidationEndpointURL(tokenID) { - const adapter = this.store.adapterFor('auth-token'); - return adapter.buildURL('auth-token', tokenID, {}, 'findRecord'); - } - - /** - * Intercepts the authenticate response, if any, and assigns the returned - * token to all future requests via `addTokenToAuthorization`. - * Returns the response data as normal. - * @override - */ - authenticate() { - return super.authenticate(...arguments).then((data) => { - const token = data?.token; - this.addTokenToAuthorization(token); - return data; - }); - } - - /** - * When restoring the session (say, on load), assigns the session token - * to all future requests via `addTokenToAuthorization`. - * @override - * @param {object} data - * @return {object} - */ - restore(data) { - const token = data?.token; - this.addTokenToAuthorization(token); - return super.restore(data); - } - - /** - * Assigns a token string all future requests via the `Authorization` header - * on the application adapter prototype. Only application and adapters that - * extend the application adapter receive this header (which is most). - * @param {string} token - */ - addTokenToAuthorization(token) { - const adapterPrototype = this.store.adapterFor('application').constructor - .prototype; - const headers = adapterPrototype?.headers; - if (!headers) adapterPrototype.headers = {}; - adapterPrototype.headers.Authorization = null; - adapterPrototype.headers.Authorization = `Bearer ${token}`; - } -} diff --git a/ui/desktop/tests/unit/authenticators/password-test.js b/ui/desktop/tests/unit/authenticators/password-test.js deleted file mode 100644 index 35e872b48..000000000 --- a/ui/desktop/tests/unit/authenticators/password-test.js +++ /dev/null @@ -1,53 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import { Response } from 'miragejs'; - -module('Unit | Authenticator | password', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - let store; - let applicationAdapter; - let session; - let authenticator; - - hooks.beforeEach(function () { - store = this.owner.lookup('service:store'); - applicationAdapter = store.adapterFor('application'); - session = this.owner.lookup('service:session'); - authenticator = this.owner.lookup('authenticator:password'); - session.invalidate(); - }); - - hooks.afterEach(function () { - session.invalidate(); - }); - - test('it adds an authorization header to application adapter on restore', async function (assert) { - assert.expect(1); - const mockData = { id: 'token1234', token: 'token1234' }; - this.server.get( - authenticator.buildTokenValidationEndpointURL(mockData.id), - () => { - return new Response(200); - } - ); - authenticator.restore(mockData); - assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); - }); - - test('it adds an authorization header to application adapter on authenticate', async function (assert) { - assert.expect(1); - const creds = { identification: 'foo', password: 'bar' }; - const scope = { id: 'global' }; - const authMethod = { id: 'paum_1234' }; - this.server.create('scope', { id: 'global', type: 'global' }); - this.server.create('auth-method', { id: authMethod.id, scopeId: 'global' }); - await authenticator.authenticate(creds, null, { scope, authMethod }); - assert.equal( - applicationAdapter.headers.Authorization, - 'Bearer thetokenstring' - ); - }); -}); From 721d09899aa91f90837848752e926cb69b8c605f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 9 Apr 2021 14:32:35 -0400 Subject: [PATCH 19/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20pending=20mo?= =?UTF-8?q?dal=20with=20retry=20and=20cancel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/core/translations/resources/en-us.yaml | 4 ++++ .../styles/rose/components/dialog/_cover.scss | 3 +++ .../scopes/scope/authenticate/method/oidc.hbs | 21 ++++++++++++++++++- ui/admin/public/logo-app-full.svg | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 ui/admin/public/logo-app-full.svg diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 44ece485e..7432ac466 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -27,6 +27,9 @@ auth-method: no-primary-auth-method: title: "Recommendation: Select a Primary Auth Method" description: A primary auth method auto-creates users when they authenticate for the first time. Scopes without primary auth methods may experience authentication failures. + pending: + title: Authentication Pending + description: Complete authentication in the newly-opened window. types: password: Password oidc: OIDC @@ -43,6 +46,7 @@ auth-method: remove-as-primary: Remove primary auth method? make-primary-confirm: "A primary auth method auto-creates users on their first authentication attempt if needed. Note: if you change the primary auth method, the previous primary will no longer auto-create users." remove-as-primary-confirm: Removing the primary auth method may lead to authentication failures for this scope. + no-see-window: Don't see the new window? group: title: Group title_plural: Groups diff --git a/addons/rose/addon/styles/rose/components/dialog/_cover.scss b/addons/rose/addon/styles/rose/components/dialog/_cover.scss index 04ea3d566..7652dc3b2 100644 --- a/addons/rose/addon/styles/rose/components/dialog/_cover.scss +++ b/addons/rose/addon/styles/rose/components/dialog/_cover.scss @@ -1,6 +1,7 @@ .rose-dialog-cover { position: fixed; top: 0; + left: 0; width: 100vw; height: 100vh; display: flex; @@ -16,6 +17,8 @@ .rose-dialog-cover-header, .rose-dialog-cover-body { + position: relative; + top: -10vh; max-width: 650px; } } diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs index 14ffff8f4..10422cc66 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs @@ -1 +1,20 @@ -{{! Checking for OIDC ready … }} + + <:header> + {{inline-svg "logo-app-full"}} +

{{t 'resources.auth-method.messages.pending.title'}}

+ + <:body> +

{{t 'resources.auth-method.messages.pending.description'}}

+

+ {{t 'resources.auth-method.questions.no-see-window'}} + + {{t 'actions.retry'}} + +

+

+ + {{t 'actions.cancel'}} + +

+ +
diff --git a/ui/admin/public/logo-app-full.svg b/ui/admin/public/logo-app-full.svg new file mode 100644 index 000000000..fd38d1aa7 --- /dev/null +++ b/ui/admin/public/logo-app-full.svg @@ -0,0 +1 @@ + \ No newline at end of file From f782d90a7eb3aadc8e8036a801c703fa550f89e5 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 9 Apr 2021 14:55:58 -0400 Subject: [PATCH 20/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20OIDC=20feature=20f?= =?UTF-8?q?lag=20(disabled=20in=20production)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/core/package.json | 1 + .../templates/scopes/scope/authenticate.hbs | 34 ++++++++++++++----- ui/admin/config/environment.js | 2 ++ ui/admin/package.json | 1 - .../templates/scopes/scope/authenticate.hbs | 22 +++++++++--- ui/desktop/config/environment.js | 7 ++++ 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/addons/core/package.json b/addons/core/package.json index 63edf135d..dc0e15fbb 100644 --- a/addons/core/package.json +++ b/addons/core/package.json @@ -37,6 +37,7 @@ "ember-loading": "^0.4.0", "ember-page-title": "^6.0.3", "ember-route-action-helper": "^2.0.8", + "ember-feature-flags": "^6.0.0", "rose": "*" }, "devDependencies": { diff --git a/ui/admin/app/templates/scopes/scope/authenticate.hbs b/ui/admin/app/templates/scopes/scope/authenticate.hbs index 15118750e..2edc1abd9 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate.hbs @@ -28,15 +28,31 @@ - {{#each @model.authMethods as |authMethod|}} - - {{#if authMethod.name}} - {{authMethod.name}} - {{else}} - {{t (concat "resources.auth-method.types." authMethod.type)}} - {{/if}} - - {{/each}} + {{#if (feature-flag 'oidc')}} + {{! OIDC enable: list all auth methods }} + {{#each @model.authMethods as |authMethod|}} + + {{#if authMethod.name}} + {{authMethod.name}} + {{else}} + {{t (concat "resources.auth-method.types." authMethod.type)}} + {{/if}} + + {{/each}} + {{else}} + {{! OIDC disabled: filter out OIDC auth methods}} + {{#each @model.authMethods as |authMethod|}} + {{#unless authMethod.isOIDC}} + + {{#if authMethod.name}} + {{authMethod.name}} + {{else}} + {{t (concat "resources.auth-method.types." authMethod.type)}} + {{/if}} + + {{/unless}} + {{/each}} + {{/if}} {{#if @model.authMethods}} diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js index adc6342a6..1f1d8de4c 100644 --- a/ui/admin/config/environment.js +++ b/ui/admin/config/environment.js @@ -86,6 +86,7 @@ module.exports = function (environment) { }, featureFlags: { + oidc: false, 'primary-auth-method': false, }, }; @@ -123,6 +124,7 @@ module.exports = function (environment) { // Enable features in development ENV.featureFlags['primary-auth-method'] = true; + ENV.featureFlags['oidc'] = true; } if (environment === 'test') { diff --git a/ui/admin/package.json b/ui/admin/package.json index ce1f51482..beeac116f 100644 --- a/ui/admin/package.json +++ b/ui/admin/package.json @@ -65,7 +65,6 @@ "ember-concurrency": "^1.3.0", "ember-exam": "^6.0.1", "ember-export-application-global": "^2.0.1", - "ember-feature-flags": "^6.0.0", "ember-inline-svg": "^1.0.0", "ember-load-initializers": "^2.1.2", "ember-maybe-import-regenerator": "^0.1.6", diff --git a/ui/desktop/app/templates/scopes/scope/authenticate.hbs b/ui/desktop/app/templates/scopes/scope/authenticate.hbs index 3d7d834bd..027736bc3 100644 --- a/ui/desktop/app/templates/scopes/scope/authenticate.hbs +++ b/ui/desktop/app/templates/scopes/scope/authenticate.hbs @@ -21,11 +21,23 @@ - {{#each @model.authMethods as |authMethod|}} - - {{t (concat "resources.auth-method.types." authMethod.type)}} - - {{/each}} + {{#if (feature-flag 'oidc')}} + {{! OIDC enable: list all auth methods }} + {{#each @model.authMethods as |authMethod|}} + + {{t (concat "resources.auth-method.types." authMethod.type)}} + + {{/each}} + {{else}} + {{! OIDC disabled: filter out OIDC auth methods}} + {{#each @model.authMethods as |authMethod|}} + {{#unless authMethod.isOIDC}} + + {{t (concat "resources.auth-method.types." authMethod.type)}} + + {{/unless}} + {{/each}} + {{/if}} {{#if @model.authMethods}} diff --git a/ui/desktop/config/environment.js b/ui/desktop/config/environment.js index 8e854404a..50394aa0e 100644 --- a/ui/desktop/config/environment.js +++ b/ui/desktop/config/environment.js @@ -52,6 +52,10 @@ module.exports = function (environment) { sessions: '/sessions', }, }, + + featureFlags: { + oidc: false, + }, }; if (process.env.ENABLE_MIRAGE) { @@ -69,6 +73,9 @@ module.exports = function (environment) { // of the UI, which only makes sense in development where the origin is // usually the same as the application origin. ENV.autoOrigin = true; + + // Enable features in development + ENV.featureFlags['oidc'] = true; } if (environment === 'test') { From 53423739d30b84818dde3926479622c7225343cf Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 9 Apr 2021 15:41:32 -0400 Subject: [PATCH 21/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20hide=20auth=20for?= =?UTF-8?q?m=20if=20OIDC=20and=20OIDC=20is=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scope/authenticate/method/index.hbs | 22 ++++++++++++++----- .../scopes/scope/authenticate/method.hbs | 22 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs index 6428c8118..e05057cee 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/index.hbs @@ -1,5 +1,17 @@ - +{{#if (feature-flag 'oidc')}} + {{! OIDC enabled: show form for any auth method}} + +{{else}} + {{! OIDC disabled: hide OIDC auth form}} + {{#unless @model.isOIDC}} + + {{/unless}} +{{/if}} diff --git a/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs b/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs index 6428c8118..e05057cee 100644 --- a/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs @@ -1,5 +1,17 @@ - +{{#if (feature-flag 'oidc')}} + {{! OIDC enabled: show form for any auth method}} + +{{else}} + {{! OIDC disabled: hide OIDC auth form}} + {{#unless @model.isOIDC}} + + {{/unless}} +{{/if}} From 5105d80a9de3cc805bc000856c0290f4b31e237f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Sat, 10 Apr 2021 12:34:13 -0400 Subject: [PATCH 22/30] =?UTF-8?q?fix:=20=F0=9F=90=9B=20fix=20request=20att?= =?UTF-8?q?ributes=20for=20OIDC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 8 +- addons/auth/addon/authenticators/oidc.js | 14 +- .../scopes/scope/authenticate/method.js | 2 +- yarn.lock | 752 +++++++++--------- 4 files changed, 398 insertions(+), 378 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 4c7c3f1d0..bc43c04ca 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -63,7 +63,7 @@ const commandHandlers = { * 1. `authenticate:start` kicks of authentication by requesting some details * from the Boundary server about the request, including the third-party * URL to which to redirect the user. - * 2. `authenticate` accepts the token_request_id and state parameters in + * 2. `authenticate` accepts the token_id and state parameters in * order to retrieve a Boundary token. This endpoint may be polled until * the authentication flow is completed. */ @@ -74,10 +74,8 @@ const commandHandlers = { {}, { attributes: { - authorization_request_url: 'https://www.duckduckgo.com', - retreival_url: '', - token_request_id: 'token_request_1234', - state: 'base_58_encoded_string', + auth_url: 'https://www.duckduckgo.com', + token_id: 'token_1234', }, } ), diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 87c2ce198..adfb03639 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -10,8 +10,8 @@ import fetch from 'fetch'; * sub steps: * 1. Initiate a call via `startAuthentication` to the API at * `buildAuthEndpointURL` using the `start` command to fetch the URL of - * the external authentication provider (`authorization_request_url`). - * 2. Open `authorization_request_url`. This may be either inline (same tab) + * the external authentication provider (`auth_url`). + * 2. Open `auth_url`. This may be either inline (same tab) * or external in a new tab or browser window. * 2. Requests the token from `buildAuthEndpointURL` using the `token` command. * A token isn't guaranteed to be ready, so the OIDC flow can be in a @@ -84,21 +84,19 @@ export default class OIDCAuthenticator extends BaseAuthenticator { const body = JSON.stringify({ command: 'token', attributes: { - state: this.session.get('data.pending.oidc.state'), - token_request_id: this.session.get( - 'data.pending.oidc.token_request_id' - ), + //state: this.session.get('data.pending.oidc.attributes.state'), + token_id: this.session.get('data.pending.oidc.attributes.token_id'), }, }); // Fetch the endpoint and get the response JSON const response = await fetch(url, { method: 'post', body }); - const json = await response.json(); - if (response.status === 100) { + if (response.status === 204) { // The token isn't ready yet, keep trying. return false; } else if (response.status < 400) { // Response was successful, meaning a token was obtained. // Authenticate with the session service using the response JSON. + const json = await response.json(); await this.session.authenticate('authenticator:oidc', json); return true; } else { diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 302f405a1..930c09d35 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -66,7 +66,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { // TODO: delegate this call from the session service so that we don't have // to look up the authenticator directly const json = await oidc.startAuthentication(options); - await this.openExternalOIDCFlow(json.attributes.authorization_request_url); + await this.openExternalOIDCFlow(json.attributes.auth_url); } /** diff --git a/yarn.lock b/yarn.lock index 227033b3a..3851d6409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,10 +23,10 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" - integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.15.tgz#7e8eea42d0b64fda2b375b22d06c605222e848f4" + integrity sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA== "@babel/core@7.12.9": version "7.12.9" @@ -51,18 +51,18 @@ source-map "^0.5.0" "@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.10.2", "@babel/core@^7.12.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.12.9", "@babel/core@^7.3.4", "@babel/core@^7.7.5", "@babel/core@^7.9.6": - version "7.13.14" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06" - integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA== + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.15.tgz#a6d40917df027487b54312202a06812c4f7792d0" + integrity sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ== dependencies: "@babel/code-frame" "^7.12.13" "@babel/generator" "^7.13.9" "@babel/helper-compilation-targets" "^7.13.13" "@babel/helper-module-transforms" "^7.13.14" "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.13" + "@babel/parser" "^7.13.15" "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.13" + "@babel/traverse" "^7.13.15" "@babel/types" "^7.13.14" convert-source-map "^1.7.0" debug "^4.1.0" @@ -95,7 +95,7 @@ "@babel/helper-explode-assignable-expression" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/helper-compilation-targets@^7.10.2", "@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8": +"@babel/helper-compilation-targets@^7.10.2", "@babel/helper-compilation-targets@^7.12.0", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8": version "7.13.13" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5" integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ== @@ -105,7 +105,7 @@ browserslist "^4.14.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.8.3": +"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.13.11", "@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.8.3": version "7.13.11" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.11.tgz#30d30a005bca2c953f5653fc25091a492177f4f6" integrity sha512-ays0I7XYq9xbjCSvT+EvysLgfc3tOkwCULHjrnscGT3A9qD4sk3wXnJ3of0MAWsWGjdinFvajHU2smYuqXKMrw== @@ -138,6 +138,20 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-define-polyfill-provider@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz#a640051772045fedaaecc6f0c6c69f02bdd34bf1" + integrity sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -292,10 +306,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.11", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.12.7", "@babel/parser@^7.13.13", "@babel/parser@^7.13.4", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" - integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== +"@babel/parser@^7.12.11", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.12.7", "@babel/parser@^7.13.15", "@babel/parser@^7.13.4", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8" + integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ== "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" @@ -306,10 +320,10 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" "@babel/plugin-proposal-optional-chaining" "^7.13.12" -"@babel/plugin-proposal-async-generator-functions@^7.13.8": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1" - integrity sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA== +"@babel/plugin-proposal-async-generator-functions@^7.13.15": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz#80e549df273a3b3050431b148c892491df1bcc5b" + integrity sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA== dependencies: "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-remap-async-to-generator" "^7.13.0" @@ -324,11 +338,11 @@ "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-proposal-decorators@^7.10.1", "@babel/plugin-proposal-decorators@^7.12.12", "@babel/plugin-proposal-decorators@^7.13.5": - version "7.13.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.13.5.tgz#d28071457a5ba8ee1394b23e38d5dcf32ea20ef7" - integrity sha512-i0GDfVNuoapwiheevUOuSW67mInqJ8qw7uWfpjNVeHMn143kXblEy/bmL9AdZ/0yf/4BMQeWXezK0tQIvNPqag== + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.13.15.tgz#e91ccfef2dc24dd5bd5dcc9fc9e2557c684ecfb8" + integrity sha512-ibAMAqUm97yzi+LPgdr5Nqb9CMkeieGHvwPg1ywSGjZrZHQEGqE01HmOio8kxRpA/+VtOHouIVy2FMpBbtltjA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" + "@babel/helper-create-class-features-plugin" "^7.13.11" "@babel/helper-plugin-utils" "^7.13.0" "@babel/plugin-syntax-decorators" "^7.12.13" @@ -783,10 +797,10 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz#b628bcc9c85260ac1aeb05b45bde25210194a2f5" - integrity sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA== +"@babel/plugin-transform-regenerator@^7.13.15": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz#e5eb28945bf8b6563e7f818945f966a8d2997f39" + integrity sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ== dependencies: regenerator-transform "^0.14.2" @@ -798,15 +812,15 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-transform-runtime@^7.10.1", "@babel/plugin-transform-runtime@^7.12.1", "@babel/plugin-transform-runtime@^7.13.9": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz#a1e40d22e2bf570c591c9c7e5ab42d6bf1e419e1" - integrity sha512-Y5k8ipgfvz5d/76tx7JYbKQTcgFSU6VgJ3kKQv4zGTKr+a9T/KBvfRvGtSFgKDQGt/DBykQixV0vNWKIdzWErA== + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.15.tgz#2eddf585dd066b84102517e10a577f24f76a9cd7" + integrity sha512-d+ezl76gx6Jal08XngJUkXM4lFXK/5Ikl9Mh4HKDxSfGJXmZ9xG64XT2oivBzfxb/eQ62VfvoMkaCZUKJMVrBA== dependencies: - "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-module-imports" "^7.13.12" "@babel/helper-plugin-utils" "^7.13.0" - babel-plugin-polyfill-corejs2 "^0.1.4" - babel-plugin-polyfill-corejs3 "^0.1.3" - babel-plugin-polyfill-regenerator "^0.1.2" + babel-plugin-polyfill-corejs2 "^0.2.0" + babel-plugin-polyfill-corejs3 "^0.2.0" + babel-plugin-polyfill-regenerator "^0.2.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.12.13": @@ -904,16 +918,16 @@ regenerator-runtime "^0.13.4" "@babel/preset-env@^7.10.2", "@babel/preset-env@^7.12.0", "@babel/preset-env@^7.12.11": - version "7.13.12" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237" - integrity sha512-JzElc6jk3Ko6zuZgBtjOd01pf9yYDEIH8BcqVuYIuOkzOwDesoa/Nz4gIo4lBG6K861KTV9TvIgmFuT6ytOaAA== + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.15.tgz#c8a6eb584f96ecba183d3d414a83553a599f478f" + integrity sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA== dependencies: - "@babel/compat-data" "^7.13.12" - "@babel/helper-compilation-targets" "^7.13.10" + "@babel/compat-data" "^7.13.15" + "@babel/helper-compilation-targets" "^7.13.13" "@babel/helper-plugin-utils" "^7.13.0" "@babel/helper-validator-option" "^7.12.17" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12" - "@babel/plugin-proposal-async-generator-functions" "^7.13.8" + "@babel/plugin-proposal-async-generator-functions" "^7.13.15" "@babel/plugin-proposal-class-properties" "^7.13.0" "@babel/plugin-proposal-dynamic-import" "^7.13.8" "@babel/plugin-proposal-export-namespace-from" "^7.12.13" @@ -961,7 +975,7 @@ "@babel/plugin-transform-object-super" "^7.12.13" "@babel/plugin-transform-parameters" "^7.13.0" "@babel/plugin-transform-property-literals" "^7.12.13" - "@babel/plugin-transform-regenerator" "^7.12.13" + "@babel/plugin-transform-regenerator" "^7.13.15" "@babel/plugin-transform-reserved-words" "^7.12.13" "@babel/plugin-transform-shorthand-properties" "^7.12.13" "@babel/plugin-transform-spread" "^7.13.0" @@ -971,10 +985,10 @@ "@babel/plugin-transform-unicode-escapes" "^7.12.13" "@babel/plugin-transform-unicode-regex" "^7.12.13" "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.13.12" - babel-plugin-polyfill-corejs2 "^0.1.4" - babel-plugin-polyfill-corejs3 "^0.1.3" - babel-plugin-polyfill-regenerator "^0.1.2" + "@babel/types" "^7.13.14" + babel-plugin-polyfill-corejs2 "^0.2.0" + babel-plugin-polyfill-corejs3 "^0.2.0" + babel-plugin-polyfill-regenerator "^0.2.0" core-js-compat "^3.9.0" semver "^6.3.0" @@ -1044,21 +1058,21 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" - integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== +"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.13.15", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": + version "7.13.15" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.15.tgz#c38bf7679334ddd4028e8e1f7b3aa5019f0dada7" + integrity sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ== dependencies: "@babel/code-frame" "^7.12.13" "@babel/generator" "^7.13.9" "@babel/helper-function-name" "^7.12.13" "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.13" - "@babel/types" "^7.13.13" + "@babel/parser" "^7.13.15" + "@babel/types" "^7.13.14" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.1.6", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": +"@babel/types@^7.1.6", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.14", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": version "7.13.14" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== @@ -2203,9 +2217,9 @@ integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" - integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== dependencies: type-detect "4.0.8" @@ -2231,16 +2245,16 @@ integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== "@storybook/addon-actions@^6.0.26": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.2.5.tgz#3f8be42446f5fa4eea177ec729052e672873c0c6" - integrity sha512-b5yml5XnKvoPIlmz8pdjm0SjkSoChuK3oqqs4XCzLm2tmaqBHuVOhWqQqOJzLsYHqY5o+OBM21aoIxL4izosjw== + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.2.7.tgz#ca0500f5eb7017d3e2b4f6ee570c3650e495c7d9" + integrity sha512-v0zgJfrgTY5di7pCUZnA5CLSu8LNcq2rwLuQ2I3qiLYszZ9Gdst5kdekMPgiz7Dk8c6GOpml/IMISnEUKBskVQ== dependencies: - "@storybook/addons" "6.2.5" - "@storybook/api" "6.2.5" - "@storybook/client-api" "6.2.5" - "@storybook/components" "6.2.5" - "@storybook/core-events" "6.2.5" - "@storybook/theming" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/api" "6.2.7" + "@storybook/client-api" "6.2.7" + "@storybook/components" "6.2.7" + "@storybook/core-events" "6.2.7" + "@storybook/theming" "6.2.7" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2254,9 +2268,9 @@ uuid-browser "^3.1.0" "@storybook/addon-docs@^6.0.26": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.2.5.tgz#a2ab8f957c73328fdd617ff346a713901f2f18b8" - integrity sha512-wsHhHK5kEN5+0u4T1nEcBAD64snCPqPqt+a8E42H9ojp1HSJ8qsMVqV9vh2rhLn4YlVwubyZW8wgI4oV0ndI1A== + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.2.7.tgz#1bcc9b0ac6e1fdb96a8614c93ae2bf11fccb189e" + integrity sha512-IJUldTCrV3zXU9WVRBWDrhB2Gqh1zqXefhCIBt3JznglBZ8ns5XcI6BLPNSLGyzHnP8vv0n4GaWbK2oUq4FbnA== dependencies: "@babel/core" "^7.12.10" "@babel/generator" "^7.12.11" @@ -2267,19 +2281,19 @@ "@mdx-js/loader" "^1.6.22" "@mdx-js/mdx" "^1.6.22" "@mdx-js/react" "^1.6.22" - "@storybook/addons" "6.2.5" - "@storybook/api" "6.2.5" - "@storybook/builder-webpack4" "6.2.5" - "@storybook/client-api" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/components" "6.2.5" - "@storybook/core" "6.2.5" - "@storybook/core-events" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/api" "6.2.7" + "@storybook/builder-webpack4" "6.2.7" + "@storybook/client-api" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/components" "6.2.7" + "@storybook/core" "6.2.7" + "@storybook/core-events" "6.2.7" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.2.5" - "@storybook/postinstall" "6.2.5" - "@storybook/source-loader" "6.2.5" - "@storybook/theming" "6.2.5" + "@storybook/node-logger" "6.2.7" + "@storybook/postinstall" "6.2.7" + "@storybook/source-loader" "6.2.7" + "@storybook/theming" "6.2.7" acorn "^7.4.1" acorn-jsx "^5.3.1" acorn-walk "^7.2.0" @@ -2302,17 +2316,17 @@ util-deprecate "^1.0.2" "@storybook/addon-knobs@^6.0.26": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-6.2.5.tgz#ec883f20bdb5940c061789e84134fb76ec32fc01" - integrity sha512-QaZQZaz8qB5TxeWnXKP0yDwvLgClCf2nfFjo0BEBcio6fZCZ1vXUz9v/ELu/vdGL0G03vkoYjGXYUXmcw+94+A== + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-6.2.7.tgz#8eda92cea5d2f7e483e4b2ca8790d01e9f73eaef" + integrity sha512-bnQpd20FWUpnUOgw0vKVouWszWEaKTRm9bWhli7RbQkHBIiWBN8rEi7Pg/x/H5lO87LBPHywT6wbKy/c6pweZQ== dependencies: - "@storybook/addons" "6.2.5" - "@storybook/api" "6.2.5" - "@storybook/channels" "6.2.5" - "@storybook/client-api" "6.2.5" - "@storybook/components" "6.2.5" - "@storybook/core-events" "6.2.5" - "@storybook/theming" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/api" "6.2.7" + "@storybook/channels" "6.2.7" + "@storybook/client-api" "6.2.7" + "@storybook/components" "6.2.7" + "@storybook/core-events" "6.2.7" + "@storybook/theming" "6.2.7" copy-to-clipboard "^3.3.1" core-js "^3.8.2" escape-html "^1.0.3" @@ -2326,34 +2340,34 @@ react-select "^3.2.0" regenerator-runtime "^0.13.7" -"@storybook/addons@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.2.5.tgz#28370a547b8d2f69a34c29520aa841961bac6b27" - integrity sha512-tRZYTxJ6FlOCoWG7mVJAzH4BIBPWyvryGprawdkWHUaVYQRMH58pU3ma7gxdYdgvV80eZe2BfEOLzvZJeV/GgA== +"@storybook/addons@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.2.7.tgz#f31343654e564dd91f1ec103f130fa23e996745b" + integrity sha512-OHVGzkS2al/jirChr2otampV3oBvODbisUpHMhgUJQ+fcjKdBOXualJFBHhAI2TNwTODfjld+TsKQlBtiMDNDw== dependencies: - "@storybook/api" "6.2.5" - "@storybook/channels" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/core-events" "6.2.5" - "@storybook/router" "6.2.5" - "@storybook/theming" "6.2.5" + "@storybook/api" "6.2.7" + "@storybook/channels" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/core-events" "6.2.7" + "@storybook/router" "6.2.7" + "@storybook/theming" "6.2.7" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.2.5.tgz#fb22363c87c5e9dfb779d01513b8013bbdf2f560" - integrity sha512-iVRCPZ+QRAQs6ukBTiBEvEHhAQJJ3lxRz096grTZ5LZpY+89GcKlwaS1kAJ903+rFOxLDezJpHhS1cwSW2Kc8g== +"@storybook/api@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.2.7.tgz#5417dfe0c5a7a49a4e47f5a4c2be7a1bbdb7d5b3" + integrity sha512-GKD9b6OkQi7eDlaPmpjSz7MJsPIY+q7eGvBAWkovLXzvfEY6sE6VqUt9aWn3i6IkzqjYF7c+wgQzpA0JBTzH1Q== dependencies: "@reach/router" "^1.3.4" - "@storybook/channels" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/core-events" "6.2.5" + "@storybook/channels" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/core-events" "6.2.7" "@storybook/csf" "0.0.1" - "@storybook/router" "6.2.5" + "@storybook/router" "6.2.7" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.2.5" + "@storybook/theming" "6.2.7" "@types/reach__router" "^1.3.7" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -2367,10 +2381,10 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/builder-webpack4@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.2.5.tgz#0c58b89175ad68ff081cb496fff917c13d867088" - integrity sha512-pVN/in4TfZ9xUuegHleyAly9dA6xbQurC4FCfwXC8aelz9eoaxRfHeN8OATANgv0VNCG6a8w5WZ3rw4LrvSQZQ== +"@storybook/builder-webpack4@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.2.7.tgz#5d10454788d412340e63403e9b7a2defafe377f4" + integrity sha512-cNw/QCauKuye1wmzySHUSaBmtvxK0v627RzX8YDv3M+1vkjZTRPkhhLE6uCZerNETck7doxTKk7x+X0z8nWSeQ== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -2393,20 +2407,20 @@ "@babel/preset-env" "^7.12.11" "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" - "@storybook/addons" "6.2.5" - "@storybook/api" "6.2.5" - "@storybook/channel-postmessage" "6.2.5" - "@storybook/channels" "6.2.5" - "@storybook/client-api" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/components" "6.2.5" - "@storybook/core-common" "6.2.5" - "@storybook/core-events" "6.2.5" - "@storybook/node-logger" "6.2.5" - "@storybook/router" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/api" "6.2.7" + "@storybook/channel-postmessage" "6.2.7" + "@storybook/channels" "6.2.7" + "@storybook/client-api" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/components" "6.2.7" + "@storybook/core-common" "6.2.7" + "@storybook/core-events" "6.2.7" + "@storybook/node-logger" "6.2.7" + "@storybook/router" "6.2.7" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.2.5" - "@storybook/ui" "6.2.5" + "@storybook/theming" "6.2.7" + "@storybook/ui" "6.2.7" "@types/node" "^14.0.10" "@types/webpack" "^4.41.26" autoprefixer "^9.8.6" @@ -2443,38 +2457,38 @@ webpack-hot-middleware "^2.25.0" webpack-virtual-modules "^0.2.2" -"@storybook/channel-postmessage@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.2.5.tgz#22b7069fe8961c23d4dbdb2a25d627f619ab90df" - integrity sha512-OK9xTWc13p9VwyA7t/fW8mJ2nhSpA08STFoe3aFH34v/r6LF+inLd+8Wg08S0XLaxd6lMjVoUapA24xUtGK9jw== +"@storybook/channel-postmessage@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.2.7.tgz#4025d3746fac9b1c2264001606c7a361d1144417" + integrity sha512-l+62q/oZrDeuU3xhqVKnyk7FoUZKwr+4ZG6fXc/uXKCryoACiHT1RvStoX4AZqua9xIxMPpQr36UpHUElNHtyg== dependencies: - "@storybook/channels" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/core-events" "6.2.5" + "@storybook/channels" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/core-events" "6.2.7" core-js "^3.8.2" global "^4.4.0" qs "^6.10.0" telejson "^5.1.0" -"@storybook/channels@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.2.5.tgz#08814f20e6027afc091d3187c2984ae790bff17c" - integrity sha512-vjWALSurfPtc56Vc2E4N0mKniMA6axU0gH8LYEgUBmmicrzzwYX9fCPfEe0NT2GNW83HpU1h2UH/ojuty/NtMg== +"@storybook/channels@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.2.7.tgz#b6b64e9f7318afe3a005042395356fd0a878001c" + integrity sha512-s79ghVFrbhj4hEt/HPNz7VgOqKQx8S8JozndHQuiE/KSNxfcgGihUXWxIVr7RGAzQcMT3HsAZ0nDbKdusV9jAg== dependencies: core-js "^3.8.2" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.2.5.tgz#886f5cf2be59a7fcdbd49df5d9ead9b6ae4c22cb" - integrity sha512-p2T+WwiiNmn1xQDSha2fnDDDoOqvjKRUanyrozy9B9LhJefMFlJgpgm/N/0FYN/nsyKfDbJAXYqc0zY8DSDfxQ== +"@storybook/client-api@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.2.7.tgz#7e788314582b9fc09fbd150ef95d0714d5d22b9c" + integrity sha512-ZeKNkI1lXW55RnREDYt/wmHi34iMYdRNpybE9QqPBEcFLyvpB7m1bifXxFSgqyMbK+eSTYOfPB1NGQJi77P/iQ== dependencies: - "@storybook/addons" "6.2.5" - "@storybook/channel-postmessage" "6.2.5" - "@storybook/channels" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/core-events" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/channel-postmessage" "6.2.7" + "@storybook/channels" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/core-events" "6.2.7" "@storybook/csf" "0.0.1" "@types/qs" "^6.9.5" "@types/webpack-env" "^1.16.0" @@ -2489,23 +2503,23 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.2.5.tgz#1cedc94dafce95c0388c0e643ab0743bf22160df" - integrity sha512-3sM2nCpa9ZYTLhOOpcWA2LfpV37TLJQG1XnJW+63IyBPJWn05thnJ3PVeazlit7JnY9o+i2VzjoHoPPk6TKpKQ== +"@storybook/client-logger@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.2.7.tgz#deca6d5b157d6ea22ec8f271399e9d10bb293a2c" + integrity sha512-IhTaZ3NPrV2g7vnoSuq5yb6QLuXQgD5+iuwzOAXlP8vU5X/Tm3zNr5PtqNjrKg1A3ls6A0pCBa5QctWdN/tnuQ== dependencies: core-js "^3.8.2" global "^4.4.0" -"@storybook/components@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.2.5.tgz#e495a1095a99f81b94da7ee3a4cd73255f7216fa" - integrity sha512-wL5cUAkYAHXJ+mhfQUl1FTZYsu0iaRT6uvawiainGsp/ccFw42l49fUuSOtRIdU77IwNU+WfK8l3Vu4MJOVJLA== +"@storybook/components@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.2.7.tgz#03ee5e22c48631fe17829299d2da61bb4e0598ce" + integrity sha512-ccKOwGIkuhEHFLzjZvaZG11GIbMvk56bNdCAG8iINKYUjJE3l0PsO5xPhNsyTlwpDScZGwEBKB48Vn54UjiS8g== dependencies: "@popperjs/core" "^2.6.0" - "@storybook/client-logger" "6.2.5" + "@storybook/client-logger" "6.2.7" "@storybook/csf" "0.0.1" - "@storybook/theming" "6.2.5" + "@storybook/theming" "6.2.7" "@types/color-convert" "^2.0.0" "@types/overlayscrollbars" "^1.12.0" "@types/react-syntax-highlighter" "11.0.5" @@ -2527,18 +2541,18 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/core-client@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.2.5.tgz#5a50c18dceac1926f9feccb21c9ca183524b1d95" - integrity sha512-fCZBNH0lahI+vNU0AnfpfC1nv1zdi7c1cxsEGa+jCfB1kserPuj2P+yfK/fKTSNk1NHLEjacUORvos9oQw8BmA== +"@storybook/core-client@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.2.7.tgz#3ee8c85035cf0006ef2151f25f215779f20580a4" + integrity sha512-te3gyGehNanWZDOoevH93Wl+iLTUBLK2jjw/B7Wf8sNegUauDQYXPR9YmI0wUQrn1Dz8Xz8bdMnD/GwzyXpGVQ== dependencies: - "@storybook/addons" "6.2.5" - "@storybook/channel-postmessage" "6.2.5" - "@storybook/client-api" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/core-events" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/channel-postmessage" "6.2.7" + "@storybook/client-api" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/core-events" "6.2.7" "@storybook/csf" "0.0.1" - "@storybook/ui" "6.2.5" + "@storybook/ui" "6.2.7" ansi-to-html "^0.6.11" core-js "^3.8.2" global "^4.4.0" @@ -2549,10 +2563,10 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/core-common@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.2.5.tgz#76ca7f600a8c04ccb4984e018a855d4a189ef2ec" - integrity sha512-ZQUX2isWWsCAjdfFOI5tPoZ5BxNxqlgpD5R2PSDPm6tB9w9tgP0BH45lkMkng5Tvl0wpXHtegPAA8ruOPkbaoA== +"@storybook/core-common@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.2.7.tgz#596bfc3e87f5d8299f0e3bd7390de1c54d05a5e6" + integrity sha512-44pbzV0dyRsypWaS7Nstv4TXiLXaTX2Wif0otwtOudmSz3eYbWt3JQMBrQnkkEegJo7XPRFI9HzT1SvdbwTR+w== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -2575,7 +2589,7 @@ "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" "@babel/register" "^7.12.1" - "@storybook/node-logger" "6.2.5" + "@storybook/node-logger" "6.2.7" "@storybook/semver" "^7.3.2" "@types/glob-base" "^0.3.0" "@types/micromatch" "^4.0.1" @@ -2603,29 +2617,29 @@ util-deprecate "^1.0.2" webpack "4" -"@storybook/core-events@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.2.5.tgz#67a30feff3893efb78ef5ecf7bee242a3252d348" - integrity sha512-cEFUrhnd9fgsfqwFHS+eXL6AQQ7Z9ZCa2r79QFJf0cEmQ53BY8ay0x8TzHpF9REGzX/TlqAhEAVo2iQn7ngFiw== +"@storybook/core-events@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.2.7.tgz#47f9bbbf7f5959eab9ffcf6e2893f09014297608" + integrity sha512-CwMftGVwWDqFva48rs5TlTSkimO7Q42dAuKKmUVHaN2tPacs8NdxhjqHmj+ls+5cxZuDnJy1x3JAo1PyHGyWiw== dependencies: core-js "^3.8.2" -"@storybook/core-server@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.2.5.tgz#eb06a878a809985122250d1cea693c18f8cc93bb" - integrity sha512-MmI7rtg+Q2IDw+fnfKzT3vVsw0DqLzfgy67ELjk9ymjkEtLI2Sx1B4Kr0RjosdLk11Ynl/Vt92Ao/6uHUr9VhA== +"@storybook/core-server@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.2.7.tgz#15408617d5bd98187938d5ac79a9456cfea2bc3d" + integrity sha512-WAHZo9xiTxbMvQtyLYBz88hXRqGsYWM6xFUAK+orSOQrmM/8P6zvTqc8p1Pf2Kfly7TnQuNcARvrQR0HGAeO0g== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.2.5" - "@storybook/builder-webpack4" "6.2.5" - "@storybook/core-client" "6.2.5" - "@storybook/core-common" "6.2.5" - "@storybook/node-logger" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/builder-webpack4" "6.2.7" + "@storybook/core-client" "6.2.7" + "@storybook/core-common" "6.2.7" + "@storybook/node-logger" "6.2.7" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.2.5" - "@storybook/ui" "6.2.5" + "@storybook/theming" "6.2.7" + "@storybook/ui" "6.2.7" "@types/node" "^14.0.10" "@types/node-fetch" "^2.5.7" "@types/pretty-hrtime" "^1.0.0" @@ -2669,13 +2683,13 @@ webpack-dev-middleware "^3.7.3" webpack-virtual-modules "^0.2.2" -"@storybook/core@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.2.5.tgz#6207b62dd5f1c3639ddbd0b1471556921955fdbc" - integrity sha512-cnlEKi2ny3NSKmrIlNJT2cUNWHkEOzpI5H4A04+lNHKnA12dZZ5+C7wDSSu4girDZ410uV5FkZkoe5zp1uQbew== +"@storybook/core@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.2.7.tgz#7eb7727523134303ab2733a196bb71b53dea4e9f" + integrity sha512-E40ezV3/bNn3y2I76n0LTmw4FLd5RrRbx4YPe5/2rOWozwErd7qHbDFRaAq6fW5dYQOC07iQNK5nneckD7id3w== dependencies: - "@storybook/core-client" "6.2.5" - "@storybook/core-server" "6.2.5" + "@storybook/core-client" "6.2.7" + "@storybook/core-server" "6.2.7" "@storybook/csf@0.0.1": version "0.0.1" @@ -2695,13 +2709,13 @@ ember-cli-babel "^7.1.2" "@storybook/ember@^6.0.26": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/ember/-/ember-6.2.5.tgz#85156832a728d52d4fb50d1baea883a76d6a5cd1" - integrity sha512-5fyTs6lx6zMVinRrx1fjOqUlLAK/Z8yomE14526k4h71ARO3ERnF4g/EiSoywq0R9UwWbKgKLZPOTPbrL8f4Pw== + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/ember/-/ember-6.2.7.tgz#0520b21e617c17dd8a8544c8131d3b837d9c8dea" + integrity sha512-JEiwhMJEV8HpVueumHWTvAl/pH7x4mOQYyhoLddSSWR9Q+sMdxh1QWmgnj5lUIR+ZQUFQcHtkDo0ugdrjWmxbg== dependencies: "@ember/test-helpers" "^2.1.4" - "@storybook/core" "6.2.5" - "@storybook/core-common" "6.2.5" + "@storybook/core" "6.2.7" + "@storybook/core-common" "6.2.7" core-js "^3.8.2" global "^4.4.0" react "16.14.0" @@ -2710,10 +2724,10 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/node-logger@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.2.5.tgz#a1488dc75b0e359fd32937d9d27132c7f6a73874" - integrity sha512-vActH+XxtxUn0E/uxZbFgiFZ6fjnfkw9imLPrIhBzLuFU+HZP8INkP/SAIqPf5SzJ7k24fkZFaEq6QakB8mI2A== +"@storybook/node-logger@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.2.7.tgz#2329042e44eba1efcc6bb0689c8566a555d5b0ee" + integrity sha512-dzsFKn/3as6Nf21/9whh4HHfKOFfKlZaG5FxuhRlB3g3j61sAlE5LI4DXHgc0NoyfV5Ymbj3BPbtlCsNOy2i5A== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.1.0" @@ -2721,20 +2735,20 @@ npmlog "^4.1.2" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.2.5.tgz#4ee6056db8e706713937cd0a1abeb02c18bf30e0" - integrity sha512-Uv8d2Lj34YqnLgOPeY360qUYt2YdxpQCeNaPVX3F9NXvr4WSMElhOrRWHAJEC2ySQqGlHf9u2ADjYbgsdrbMaA== +"@storybook/postinstall@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.2.7.tgz#2d3a5d63fb9413452f05a9030d3f02febc48ef9a" + integrity sha512-Sj5FLHlbkeYYnkfs11EYyDzxMn59EeToZzNiwzXNDc2zDyyLNrV9ouVwW2Fb/hHwf9wypD1maHUShhXCKGc+cw== dependencies: core-js "^3.8.2" -"@storybook/router@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.2.5.tgz#51b2f8786d7b08c77e1453998605082a1b7d4437" - integrity sha512-Cs6t3eSZ1Pqbld+cKx8K7YFVyrXvJtOcqG9NMCmx9NZjWRfMlorstWSDdTnk4uTHenXH99Y/uUNZCHRgf+Jt/A== +"@storybook/router@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.2.7.tgz#b6bfedc237f0b3834f8fa9eb724d91bd3eaff832" + integrity sha512-ZHz5T/IvpsXq0O/frdZN6kNltzy7QiL1TgekwY5evjtRWFAX6vRBQXfQH9opGQ3dThZIhMUi7kA9h9utrHp8HA== dependencies: "@reach/router" "^1.3.4" - "@storybook/client-logger" "6.2.5" + "@storybook/client-logger" "6.2.7" "@types/reach__router" "^1.3.7" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -2752,13 +2766,13 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.2.5.tgz#d91bcaceccfd18f05ca829b5cfd85b75dd87127f" - integrity sha512-wjS05u7an8NG62QdSLDdD+b0YZUHHz0THKlZaOJXtudtgrEllPJDeZ6byLHvAC4RSRS+Y22SRazJMBFr+TrwtQ== +"@storybook/source-loader@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.2.7.tgz#0e66eea339c3829030ec63e7729dc68f1d531885" + integrity sha512-fngRG28agjVleUBLhXhF4VzCmHzlq3XmSRB/IbT+kxgdWr9+QP9+i75HRQx5YIy4znnNkWojpv+0LVkVqQBHZw== dependencies: - "@storybook/addons" "6.2.5" - "@storybook/client-logger" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/client-logger" "6.2.7" "@storybook/csf" "0.0.1" core-js "^3.8.2" estraverse "^5.2.0" @@ -2768,15 +2782,15 @@ prettier "~2.2.1" regenerator-runtime "^0.13.7" -"@storybook/theming@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.2.5.tgz#c72ea11e307440e49b4b8ff86916bac8998e0c88" - integrity sha512-DXLYggY6XUYD3cuFFas+7JZZP54a3MkKgQbxm3hYzS0D1mbG2gbjukuXtuN9WMoBEXgdRtYfySHX45NF29ZxzA== +"@storybook/theming@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.2.7.tgz#7dbbcde537099b1fe65ec63a517fd54a3dd587f5" + integrity sha512-0zTw1DhPWuNsk/fSg7rqLa/99+dxUfwjDnCYu4WJAUjfBWP/++0VWtQJVtt8ogyHKsY2dUzm5mhUg8EtqnGePg== dependencies: "@emotion/core" "^10.1.1" "@emotion/is-prop-valid" "^0.8.6" "@emotion/styled" "^10.0.27" - "@storybook/client-logger" "6.2.5" + "@storybook/client-logger" "6.2.7" core-js "^3.8.2" deep-object-diff "^1.1.0" emotion-theming "^10.0.27" @@ -2786,21 +2800,21 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" -"@storybook/ui@6.2.5": - version "6.2.5" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.2.5.tgz#1192790da26be789357bd88512fe24856fcd151e" - integrity sha512-1qitL9SGgROIurdW4P5h0cG20jH/2j6lDgmk9wHh2ZvVPLd+Thg/47JDZ8hIxackYCe2HwfIhvNdzz/unq8rig== +"@storybook/ui@6.2.7": + version "6.2.7" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.2.7.tgz#ff526d02d5785dd9cfbcb9bd6688b7293abe271f" + integrity sha512-Ys60G+g4leag/cNPTTC9wp9JRYY2LgXREs4HE4tybo4Ga/ZhQZrWzgKXuEeqM3GJZgLcsKz+qtPVQJibDBbOMg== dependencies: "@emotion/core" "^10.1.1" - "@storybook/addons" "6.2.5" - "@storybook/api" "6.2.5" - "@storybook/channels" "6.2.5" - "@storybook/client-logger" "6.2.5" - "@storybook/components" "6.2.5" - "@storybook/core-events" "6.2.5" - "@storybook/router" "6.2.5" + "@storybook/addons" "6.2.7" + "@storybook/api" "6.2.7" + "@storybook/channels" "6.2.7" + "@storybook/client-logger" "6.2.7" + "@storybook/components" "6.2.7" + "@storybook/core-events" "6.2.7" + "@storybook/router" "6.2.7" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.2.5" + "@storybook/theming" "6.2.7" "@types/markdown-to-jsx" "^6.11.3" copy-to-clipboard "^3.3.1" core-js "^3.8.2" @@ -3092,9 +3106,9 @@ integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== "@types/node-fetch@^2.5.7": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.9.tgz#c04a12115aa436f189e39579272b305e477621b4" - integrity sha512-6cUyqLK+JBsATAqNQqk10jURoBFrzfRCDh4kaYxg8ivKhRPIpyBgAvuY7zM/3E4AwsYJSh5HCHBCJRM4DsCTaQ== + version "2.5.10" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" + integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== dependencies: "@types/node" "*" form-data "^3.0.0" @@ -4525,16 +4539,16 @@ babel-plugin-module-resolver@^4.0.0: reselect "^4.0.0" resolve "^1.13.1" -babel-plugin-polyfill-corejs2@^0.1.4: - version "0.1.10" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz#a2c5c245f56c0cac3dbddbf0726a46b24f0f81d1" - integrity sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA== +babel-plugin-polyfill-corejs2@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz#686775bf9a5aa757e10520903675e3889caeedc4" + integrity sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg== dependencies: - "@babel/compat-data" "^7.13.0" - "@babel/helper-define-polyfill-provider" "^0.1.5" + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.0" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.1.0, babel-plugin-polyfill-corejs3@^0.1.3: +babel-plugin-polyfill-corejs3@^0.1.0: version "0.1.7" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz#80449d9d6f2274912e05d9e182b54816904befd0" integrity sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw== @@ -4542,12 +4556,20 @@ babel-plugin-polyfill-corejs3@^0.1.0, babel-plugin-polyfill-corejs3@^0.1.3: "@babel/helper-define-polyfill-provider" "^0.1.5" core-js-compat "^3.8.1" -babel-plugin-polyfill-regenerator@^0.1.2: - version "0.1.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz#0fe06a026fe0faa628ccc8ba3302da0a6ce02f3f" - integrity sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg== +babel-plugin-polyfill-corejs3@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz#f4b4bb7b19329827df36ff56f6e6d367026cb7a2" + integrity sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.1.5" + "@babel/helper-define-polyfill-provider" "^0.2.0" + core-js-compat "^3.9.1" + +babel-plugin-polyfill-regenerator@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz#853f5f5716f4691d98c84f8069c7636ea8da7ab8" + integrity sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.0" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" @@ -6397,29 +6419,28 @@ charm@^1.0.0: dependencies: inherits "^2.0.1" -cheerio-select-tmp@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646" - integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ== +cheerio-select@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.3.0.tgz#26a50968260b7e4281238c1e7da7ed2766652f3b" + integrity sha512-mLgqdHxVOQyhOIkG5QnRkDg7h817Dkf0dAvlCio2TJMmR72cJKH0bF28SHXvLkVrGcGOiub0/Bs/CMnPeQO7qw== dependencies: - css-select "^3.1.2" - css-what "^4.0.0" - domelementtype "^2.1.0" - domhandler "^4.0.0" - domutils "^2.4.4" + css-select "^4.0.0" + css-what "^5.0.0" + domelementtype "^2.2.0" + domhandler "^4.1.0" + domutils "^2.5.2" cheerio@^1.0.0-rc.2, cheerio@^1.0.0-rc.5: - version "1.0.0-rc.5" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f" - integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw== + version "1.0.0-rc.6" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.6.tgz#a5ae81ab483aeefa1280c325543c601145506240" + integrity sha512-hjx1XE1M/D5pAtMgvWwE21QClmAEeGHOIDfycgmndisdNgI6PE1cGRQkMGBcsbUbmEQyWu5PJLUcAOjtQS8DWw== dependencies: - cheerio-select-tmp "^0.1.0" - dom-serializer "~1.2.0" - domhandler "^4.0.0" - entities "~2.1.0" - htmlparser2 "^6.0.0" - parse5 "^6.0.0" - parse5-htmlparser2-tree-adapter "^6.0.0" + cheerio-select "^1.3.0" + dom-serializer "^1.3.1" + domhandler "^4.1.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" chokidar@3.5.1, "chokidar@>=2.0.0 <4.0.0", chokidar@^3.4.1, chokidar@^3.4.2: version "3.5.1" @@ -6466,11 +6487,9 @@ chownr@^2.0.0: integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== chromium-pickle-js@^0.2.0: version "0.2.0" @@ -7107,18 +7126,18 @@ copy-to-clipboard@^3.3.1: dependencies: toggle-selection "^1.0.6" -core-js-compat@^3.8.1, core-js-compat@^3.9.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.10.0.tgz#3600dc72869673c110215ee7a005a8609dea0fe1" - integrity sha512-9yVewub2MXNYyGvuLnMHcN1k9RkvB7/ofktpeKTIaASyB88YYqGzUnu0ywMMhJrDHOMiTjSHWGzR+i7Wb9Z1kQ== +core-js-compat@^3.8.1, core-js-compat@^3.9.0, core-js-compat@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.10.1.tgz#62183a3a77ceeffcc420d907a3e6fc67d9b27f1c" + integrity sha512-ZHQTdTPkqvw2CeHiZC970NNJcnwzT6YIueDMASKt+p3WbZsLXOcoD392SkcWhkC0wBBHhlfhqGKKsNCQUozYtg== dependencies: browserslist "^4.16.3" semver "7.0.0" core-js-pure@^3.8.2: - version "3.10.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.10.0.tgz#dab9d6b141779b622b40567e7a536d2276646c15" - integrity sha512-CC582enhrFZStO4F8lGI7QL3SYx7/AIRc+IdSi3btrQGrVsTawo5K/crmKbRrQ+MOMhNX4v+PATn0k2NN6wI7A== + version "3.10.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.10.1.tgz#28642697dfcf02e0fd9f4d9891bd03a22df28ecf" + integrity sha512-PeyJH2SE0KuxY5eCGNWA+W+CeDpB6M1PN3S7Am7jSv/Ttuxz2SnWbIiVQOn/TDaGaGtxo8CRWHkXwJscbUHtVw== core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5: version "2.6.12" @@ -7126,9 +7145,9 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: - version "3.10.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.0.tgz#9a020547c8b6879f929306949e31496bbe2ae9b3" - integrity sha512-MQx/7TLgmmDVamSyfE+O+5BHvG1aUGj/gHhLn1wVtm2B5u1eVIPvh7vkfjwWKNCjrTJB8+He99IntSQ1qP+vYQ== + version "3.10.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.10.1.tgz#e683963978b6806dcc6c0a4a8bd4ab0bdaf3f21a" + integrity sha512-pwCxEXnj27XG47mu7SXAwhLP3L5CrlvCB91ANUkIz40P27kUcvNfSdvyZJ9CLHiVoKSp+TTChMQMSKQEH/IQxA== core-object@^3.1.5: version "3.1.5" @@ -7342,15 +7361,15 @@ css-select@^2.0.0, css-select@^2.0.2: domutils "^1.7.0" nth-check "^1.0.2" -css-select@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8" - integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== +css-select@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.0.0.tgz#9b7b53bd82e4b348a6e0924ce37645e5db43af8e" + integrity sha512-I7favumBlDP/nuHBKLfL5RqvlvRdn/W29evvWJ+TaoGPm7QD+xSIN5eY2dyGjtkUmemh02TZrqJb4B8DWo6PoQ== dependencies: boolbase "^1.0.0" - css-what "^4.0.0" - domhandler "^4.0.0" - domutils "^2.4.3" + css-what "^5.0.0" + domhandler "^4.1.0" + domutils "^2.5.1" nth-check "^2.0.0" css-tree@1.0.0-alpha.28: @@ -7387,10 +7406,10 @@ css-what@^3.2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== -css-what@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" - integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== +css-what@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47" + integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA== cssesc@^3.0.0: version "3.0.0" @@ -7552,7 +7571,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@^1.0.1: +debuglog@*, debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -7864,10 +7883,10 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@^1.0.1, dom-serializer@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" - integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== +dom-serializer@^1.0.1, dom-serializer@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be" + integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q== dependencies: domelementtype "^2.0.1" domhandler "^4.0.0" @@ -7888,7 +7907,7 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1, domelementtype@^2.1.0, domelementtype@^2.2.0: +domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== @@ -7922,10 +7941,10 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.4.3, domutils@^2.4.4: - version "2.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.1.tgz#9b8e84b5d9f788499ae77506ea832e9b4f9aa1c0" - integrity sha512-hO1XwHMGAthA/1KL7c83oip/6UWo3FlUNIuWiWKltoiQ5oCOiqths8KknvY2jpOohUoUgnwa/+Rm7UpwpSbY/Q== +domutils@^2.5.1, domutils@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.2.tgz#37ef8ba087dff1a17175e7092e8a042e4b050e6c" + integrity sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ== dependencies: dom-serializer "^1.0.1" domelementtype "^2.2.0" @@ -8115,9 +8134,9 @@ electron-rebuild@^2.0.3: yargs "^16.0.0" electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.649: - version "1.3.709" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.709.tgz#d7be0b5686a2fdfe8bad898faa3a428d04d8f656" - integrity sha512-LolItk2/ikSGQ7SN8UkuKVNMBZp3RG7Itgaxj1npsHRzQobj9JjMneZOZfLhtwlYBe5fCJ75k+cVCiDFUs23oA== + version "1.3.711" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.711.tgz#92c3caf7ffed5e18bf63f66b4b57b4db2409c450" + integrity sha512-XbklBVCDiUeho0PZQCjC25Ha6uBwqqJeyDhPLwLwfWRAo4x+FZFsmu1pPPkXT+B4MQMQoQULfyaMltDopfeiHQ== element-resize-detector@^1.2.2: version "1.2.2" @@ -10228,9 +10247,9 @@ fake-xml-http-request@^2.1.1: integrity sha512-HaFMBi7r+oEC9iJNpc3bvcW7Z7iLmM26hPDmlb0mFwyANSsOQAtJxbdWsXITKOzZUyMYK0zYCv3h5yDj9TsiXg== faker@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.2.tgz#d6f99923fb757b26733a6d2396ddb448ac5bb446" - integrity sha512-6G3lzZXWjWfqTJDS9KhHFIislZMGdrzDqews3T14E/dsANVbs3YT4A3jSNDrbA/gbtmjLuKJx9DzcLucdXBqBw== + version "5.5.3" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -10733,9 +10752,9 @@ fork-ts-checker-webpack-plugin@4.1.6, fork-ts-checker-webpack-plugin@^4.1.6: worker-rpc "^0.1.0" fork-ts-checker-webpack-plugin@^6.0.4: - version "6.2.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.2.0.tgz#d13af02e24d1b17f769af6bdf41c1e849e1615cc" - integrity sha512-DTNbOhq6lRdjYprukX54JMeYJgQ0zMow+R5BMLwWxEX2NAXthIkwnV8DBmsWjwNLSUItKZM4TCCJbtgrtKBu2Q== + version "6.2.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.2.1.tgz#e3a7e64c90e5490a75d43d86d47f02e538c0a13e" + integrity sha512-Pyhn2kav/Y2g6I7aInABgcph/B78jjdXc4kGHzaAUBL4UVthknxM6aMH47JwpnuTJmdOuf6p5vMbIahsBHuWGg== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -11434,9 +11453,9 @@ globals@^12.1.0: type-fest "^0.8.1" globals@^13.6.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.7.0.tgz#aed3bcefd80ad3ec0f0be2cf0c895110c0591795" - integrity sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA== + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== dependencies: type-fest "^0.20.2" @@ -11987,12 +12006,7 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - -hosted-git-info@^2.8.8: +hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.8: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== @@ -12078,14 +12092,14 @@ htmlparser2@^3.10.0, htmlparser2@^3.10.1: inherits "^2.0.1" readable-stream "^3.1.1" -htmlparser2@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446" - integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w== +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== dependencies: domelementtype "^2.0.1" domhandler "^4.0.0" - domutils "^2.4.4" + domutils "^2.5.2" entities "^2.0.0" htmlparser2@~3.9.2: @@ -12322,7 +12336,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@^0.1.4: +imurmurhash@*, imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -12716,9 +12730,9 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: kind-of "^6.0.2" is-docker@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.0.tgz#b037c8815281edaad6c2562648a5f5f18839d5f7" - integrity sha512-K4GwB4i/HzhAzwP/XSlspzRdFTI9N8OxJOyOU7Y5Rz+p+WBokXWVWblaJeBkggthmoSV0OoGTH5thJNvplpkvQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-dom@^1.0.0: version "1.1.0" @@ -14020,6 +14034,11 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" +lodash._baseindexof@*: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" + integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= + lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14028,11 +14047,16 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@^3.0.0: +lodash._bindcallback@*, lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= +lodash._cacheindexof@*: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" + integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= + lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14042,12 +14066,19 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" +lodash._createcache@*: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" + integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= + dependencies: + lodash._getnative "^3.0.0" + lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@^3.0.0: +lodash._getnative@*, lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14265,7 +14296,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@^3.0.0: +lodash.restparam@*, lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= @@ -14928,12 +14959,12 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: to-regex "^3.0.2" micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + version "4.0.3" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.3.tgz#fdad8352bf0cbeb89b391b5d244bc22ff3dd4ec8" + integrity sha512-ueuSaP4i67F/FAUac9zzZ0Dz/5KeKDkITYIS/k4fps+9qeh1SkeH6gbljcqz97mNBOsaWZ+iv2UobMKK/yD+aw== dependencies: braces "^3.0.1" - picomatch "^2.0.5" + picomatch "^2.2.1" miller-rabin@^4.0.0: version "4.0.1" @@ -16600,7 +16631,7 @@ parse-static-imports@^1.1.0: resolved "https://registry.yarnpkg.com/parse-static-imports/-/parse-static-imports-1.1.0.tgz#ae2f18f18da1a993080ae406a5219455c0bbad5d" integrity sha512-HlxrZcISCblEV0lzXmAHheH/8qEkKgmqkdxyHTPbSqsTUV8GzqmN1L+SSti+VbNPfbBO3bYLPHDiUs2avbAdbA== -parse5-htmlparser2-tree-adapter@^6.0.0: +parse5-htmlparser2-tree-adapter@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== @@ -16750,9 +16781,9 @@ path-type@^4.0.0: integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pbkdf2@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" - integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -16765,7 +16796,7 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -17511,9 +17542,9 @@ rcedit@^2.0.0: integrity sha512-h1gNEl9Oai1oijwyJ1WYqYSXTStHnOcv1KYljg/8WM4NAg3H1KBK3azIaKkQ1WQl+d7PoJpcBMscPfLXVKgCLQ== react-colorful@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.1.1.tgz#b242a3a59dc2e740fef9ee072c1d8b49d3465b62" - integrity sha512-CNnpzPOMDUors/WcN23IUkBvdHuzJfr5UuZxW02TyVW5hCmFME3cbxucF26EujKyTt4ageBrLDyQ6JAtjGIzgQ== + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.1.2.tgz#5cb1506b8f9104b88d02d34984a36c2d1e477e9e" + integrity sha512-FRt9jz6xjiPqQ6bIAQ26kd0oJhHbGBwsA4BDz/F8FDCFuQJDiEl0wVUARNiqRyvQjwfKuhM42P/bMYI0l92hRw== react-dev-utils@^11.0.3, react-dev-utils@~11.0.4: version "11.0.4" @@ -18239,9 +18270,9 @@ renderkid@^2.0.4: strip-ansi "^3.0.0" repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== repeat-string@^1.0.0, repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" @@ -19418,20 +19449,13 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^6.0.0: +ssri@^6.0.0, ssri@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== dependencies: figgy-pudding "^3.5.1" -ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - ssri@^7.0.0, ssri@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" @@ -20718,9 +20742,9 @@ underscore.string@^3.2.2, underscore.string@~3.3.4: util-deprecate "^1.0.2" underscore@>=1.8.3: - version "1.12.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" - integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + version "1.13.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.0.tgz#3ccdcbb824230fc6bf234ad0ddcd83dff4eafe5f" + integrity sha512-sCs4H3pCytsb5K7i072FAEC9YlSYFIbosvM0tAKAlpSSUgD7yC1iXSEGdl5XrDKQ1YUB+p/HDzYrSG2H2Vl36g== underscore@~1.8.3: version "1.8.3" @@ -21798,14 +21822,14 @@ y18n@^3.2.1: integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== y18n@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.2.tgz#c504495ba9b59230dd60226d1dd89c3c0a1b745e" - integrity sha512-DnBDwcL54b5xWMM/7RfFg4xs5amYxq2ot49aUfLjQSAracXkGvlZq0txzqr3Pa6Q0ayuCxBcwTzrPUScKY0O8w== + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: - version "5.0.7" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.7.tgz#0c514aba53fc40e2db911aeb8b51566a3374efe7" - integrity sha512-oOhslryvNcA1lB9WYr+M6TMyLkLg81Dgmyb48ZDU0lvR+5bmNDTMz7iobM1QXooaLhbbrcHrlNaABhI6Vo6StQ== + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^2.1.2: version "2.1.2" From 4cb7985ac117d170a9bfc1b80cec85f82d0d15a2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 13:47:58 -0400 Subject: [PATCH 23/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20token=20pending?= =?UTF-8?q?=20status=20is=20now=20202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/config.js | 2 +- addons/auth/addon/authenticators/oidc.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 243467bc7..769e7e810 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -69,7 +69,7 @@ export default function () { if (scope.primaryAuthMethodId === id) { scope.update({ primaryAuthMethodId: null }); } - return new Response(204); + return new Response(202); }); // Auth Method Accounts diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index adfb03639..2de7f1184 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -90,7 +90,7 @@ export default class OIDCAuthenticator extends BaseAuthenticator { }); // Fetch the endpoint and get the response JSON const response = await fetch(url, { method: 'post', body }); - if (response.status === 204) { + if (response.status === 202) { // The token isn't ready yet, keep trying. return false; } else if (response.status < 400) { From 32ec1f0f704f333ba433ebc0f96c2a99fed9592c Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 15:16:52 -0400 Subject: [PATCH 24/30] chore: oidc should use 202 --- addons/api/mirage/route-handlers/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index bc43c04ca..bd4fca7ac 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -82,7 +82,7 @@ const commandHandlers = { token: (_, scopeAttrs) => { oidcAttemptCounter++; if (oidcAttemptCounter < oidcRequiredAttempts) { - return new Response(100); + return new Response(202); } else { return new Response( 200, From c2b66b74eb6186f5ab48eaacdc38c7652e238681 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 15:31:42 -0400 Subject: [PATCH 25/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20oidc-crud=20?= =?UTF-8?q?feature=20flag=20to=20disable=20admin=20list=20links?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scopes/scope/auth-methods/index.hbs | 20 ++++++++++++++++--- ui/admin/config/environment.js | 2 ++ 2 files changed, 19 insertions(+), 3 deletions(-) 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 039602e23..f8038c37b 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -58,15 +58,29 @@ {{#each @model as |authMethod|}} - {{#if authMethod.isPassword}} + {{! + Show link to auth method route if non-OIDC and when + oidc-crud feature is enabled, otherwise show just the name + (when the method is OIDC and oidc-crud is disabled) + }} + {{#if authMethod.isOIDC}} + {{#if (feature-flag 'oidc-crud')}} + + {{authMethod.displayName}} + + {{else}} + {{authMethod.displayName}} + {{/if}} + {{else}} {{authMethod.displayName}} - {{else}} - {{authMethod.displayName}} {{/if}}

{{authMethod.description}}

diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js index 1f1d8de4c..cfbcb155b 100644 --- a/ui/admin/config/environment.js +++ b/ui/admin/config/environment.js @@ -87,6 +87,7 @@ module.exports = function (environment) { featureFlags: { oidc: false, + 'oidc-crud': false, 'primary-auth-method': false, }, }; @@ -125,6 +126,7 @@ module.exports = function (environment) { // Enable features in development ENV.featureFlags['primary-auth-method'] = true; ENV.featureFlags['oidc'] = true; + //ENV.featureFlags['oidc-crud'] = true; } if (environment === 'test') { From 3468acce39eb9e7fdf40ef59900d2b6a0b4558cc Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 16:54:13 -0400 Subject: [PATCH 26/30] =?UTF-8?q?chore:=20=F0=9F=A4=96=20OIDC=20tokens=20m?= =?UTF-8?q?ust=20be=20added=20to=20request=20auth=20headers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/core/addon/authenticators/oidc.js | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index b23bea9bd..f159fa87f 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -38,4 +38,49 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { const adapter = this.store.adapterFor('auth-token'); return adapter.buildURL('auth-token', tokenID, {}, 'findRecord'); } + + /** + * Intercepts the authenticate response, if any, and assigns the returned + * token to all future requests via `addTokenToAuthorization`. + * Returns the response data as normal. + * @override + */ + authenticate() { + return super.authenticate(...arguments).then((data) => { + const token = data?.attributes?.token; + this.addTokenToAuthorization(token); + return data; + }); + } + + /** + * When restoring the session (say, on load), assigns the session token + * to all future requests via `addTokenToAuthorization`. + * @override + * @param {object} data + * @return {object} + */ + restore(data) { + const token = data?.attributes?.token; + this.addTokenToAuthorization(token); + return super.restore(data); + } + + /** + * Assigns a token string all future requests via the `Authorization` header + * on the application adapter prototype. Only application and adapters that + * extend the application adapter receive this header (which is most). + * + * TODO: if cookies were requested, this step should be skipped. + * + * @param {string} token + */ + addTokenToAuthorization(token) { + const adapterPrototype = this.store.adapterFor('application').constructor + .prototype; + const headers = adapterPrototype?.headers; + if (!headers) adapterPrototype.headers = {}; + adapterPrototype.headers.Authorization = null; + adapterPrototype.headers.Authorization = `Bearer ${token}`; + } } From 5f342a0ee874ea8bd14a2f4adf2d5c1abc10d250 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 17:10:55 -0400 Subject: [PATCH 27/30] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20"retry"=20is?= =?UTF-8?q?=20a=20link=20instead=20of=20an=20action=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scopes/scope/authenticate/method/oidc.js | 18 ++++++++ .../scopes/scope/authenticate/method.js | 42 +++++++++---------- .../scopes/scope/authenticate/method/oidc.hbs | 6 ++- .../scope/authenticate/method/oidc-test.js | 17 ++++++++ 4 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 ui/admin/app/controllers/scopes/scope/authenticate/method/oidc.js create mode 100644 ui/admin/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js diff --git a/ui/admin/app/controllers/scopes/scope/authenticate/method/oidc.js b/ui/admin/app/controllers/scopes/scope/authenticate/method/oidc.js new file mode 100644 index 000000000..119f54ed0 --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/authenticate/method/oidc.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthenticateMethodOidcController extends Controller { + // =services + + @service session; + + // =attributes + + /** + * Authetnication URL for the pending OIDC flow, if any. + * @type {?string} + */ + get authURL() { + return this.session.data.pending.oidc.attributes.auth_url; + } +} diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 930c09d35..33dec88f9 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -24,6 +24,27 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { return this.store.findRecord('auth-method', id, adapterOptions); } + /** + * + */ + async startOIDCAuthentication(authenticatorName, options) { + const oidc = getOwner(this).lookup(authenticatorName); + // TODO: delegate this call from the session service so that we don't have + // to look up the authenticator directly + const json = await oidc.startAuthentication(options); + await this.openExternalOIDCFlow(json.attributes.auth_url); + } + + /** + * Opens the specified URL in a new tab or window. By default this uses + * `window.open`, but may be overriden. + * @param {string} url + */ + async openExternalOIDCFlow(url) { + // TODO don't use window directly + await window.open(url); + } + // =actions /** @@ -57,25 +78,4 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { break; } } - - /** - * - */ - async startOIDCAuthentication(authenticatorName, options) { - const oidc = getOwner(this).lookup(authenticatorName); - // TODO: delegate this call from the session service so that we don't have - // to look up the authenticator directly - const json = await oidc.startAuthentication(options); - await this.openExternalOIDCFlow(json.attributes.auth_url); - } - - /** - * Opens the specified URL in a new tab or window. By default this uses - * `window.open`, but may be overriden. - * @param {string} url - */ - async openExternalOIDCFlow(url) { - // TODO don't use window directly - await window.open(url); - } } diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs index 10422cc66..dedca3ab3 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method/oidc.hbs @@ -7,7 +7,11 @@

{{t 'resources.auth-method.messages.pending.description'}}

{{t 'resources.auth-method.questions.no-see-window'}} - + {{t 'actions.retry'}}

diff --git a/ui/admin/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js b/ui/admin/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js new file mode 100644 index 000000000..0a98d8794 --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Controller | scopes/scope/authenticate/method/oidc', + function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/authenticate/method/oidc' + ); + assert.ok(controller); + }); + } +); From 586a4dbe23e4d5624e03eb36ac7b2aacafb34d8f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 18:10:08 -0400 Subject: [PATCH 28/30] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20roll=20out=20OIDC?= =?UTF-8?q?=20to=20desktop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scopes/scope/authenticate/method/oidc.js | 18 ++++ ui/desktop/app/router.js | 4 +- .../scopes/scope/authenticate/method.js | 45 ++++++++-- .../scopes/scope/authenticate/method/index.js | 3 + .../scopes/scope/authenticate/method/oidc.js | 82 +++++++++++++++++++ .../scopes/scope/authenticate/method.hbs | 18 +--- .../scope/authenticate/method/index.hbs | 17 ++++ .../scopes/scope/authenticate/method/oidc.hbs | 23 ++++++ ui/desktop/config/environment.js | 1 + ui/desktop/electron-app/src/handlers.js | 6 +- ui/desktop/package.json | 3 + .../tests/acceptance/authentication-test.js | 2 + .../scope/authenticate/method/oidc-test.js | 17 ++++ .../scope/authenticate/method/index-test.js | 16 ++++ .../scope/authenticate/method/oidc-test.js | 16 ++++ yarn.lock | 27 ++---- 16 files changed, 251 insertions(+), 47 deletions(-) create mode 100644 ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js create mode 100644 ui/desktop/app/routes/scopes/scope/authenticate/method/index.js create mode 100644 ui/desktop/app/routes/scopes/scope/authenticate/method/oidc.js create mode 100644 ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs create mode 100644 ui/desktop/app/templates/scopes/scope/authenticate/method/oidc.hbs create mode 100644 ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js create mode 100644 ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/index-test.js create mode 100644 ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js diff --git a/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js b/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js new file mode 100644 index 000000000..119f54ed0 --- /dev/null +++ b/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthenticateMethodOidcController extends Controller { + // =services + + @service session; + + // =attributes + + /** + * Authetnication URL for the pending OIDC flow, if any. + * @type {?string} + */ + get authURL() { + return this.session.data.pending.oidc.attributes.auth_url; + } +} diff --git a/ui/desktop/app/router.js b/ui/desktop/app/router.js index 041868e34..71ea1f191 100644 --- a/ui/desktop/app/router.js +++ b/ui/desktop/app/router.js @@ -10,7 +10,9 @@ Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { this.route('authenticate', function () { - this.route('method', { path: ':auth_method_id' }); + this.route('method', { path: ':auth_method_id' }, function () { + this.route('oidc'); + }); }); this.route('projects', function () { this.route('targets', function () { diff --git a/ui/desktop/app/routes/scopes/scope/authenticate/method.js b/ui/desktop/app/routes/scopes/scope/authenticate/method.js index 33b7fd324..66fc52286 100644 --- a/ui/desktop/app/routes/scopes/scope/authenticate/method.js +++ b/ui/desktop/app/routes/scopes/scope/authenticate/method.js @@ -1,5 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { notifyError } from 'core/decorators/notify'; @@ -9,6 +10,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { @service session; @service notify; + @service ipc; @service intl; // =methods @@ -23,6 +25,26 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { return this.store.findRecord('auth-method', id, adapterOptions); } + /** + * + */ + async startOIDCAuthentication(authenticatorName, options) { + const oidc = getOwner(this).lookup(authenticatorName); + // TODO: delegate this call from the session service so that we don't have + // to look up the authenticator directly + const json = await oidc.startAuthentication(options); + await this.openExternalOIDCFlow(json.attributes.auth_url); + } + + /** + * Opens the specified URL in a new tab or window. By default this uses + * `window.open`, but may be overriden. + * @param {string} url + */ + async openExternalOIDCFlow(url) { + await this.ipc.invoke('openExternal', url); + } + // =actions /** @@ -37,10 +59,23 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const authMethod = this.modelFor('scopes.scope.authenticate.method'); const authenticatorName = `authenticator:${authMethod.type}`; const requestCookies = false; - await this.session.authenticate(authenticatorName, creds, requestCookies, { - scope, - authMethod, - }); - this.replaceWith('index'); + switch (authMethod.type) { + case 'password': + await this.session.authenticate( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + this.transitionTo('index'); + break; + case 'oidc': + await this.startOIDCAuthentication(authenticatorName, { + scope, + authMethod, + }); + this.transitionTo('scopes.scope.authenticate.method.oidc'); + break; + } } } diff --git a/ui/desktop/app/routes/scopes/scope/authenticate/method/index.js b/ui/desktop/app/routes/scopes/scope/authenticate/method/index.js new file mode 100644 index 000000000..fc9b10575 --- /dev/null +++ b/ui/desktop/app/routes/scopes/scope/authenticate/method/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthenticateMethodIndexRoute extends Route {} diff --git a/ui/desktop/app/routes/scopes/scope/authenticate/method/oidc.js b/ui/desktop/app/routes/scopes/scope/authenticate/method/oidc.js new file mode 100644 index 000000000..b6cb354c4 --- /dev/null +++ b/ui/desktop/app/routes/scopes/scope/authenticate/method/oidc.js @@ -0,0 +1,82 @@ +import Route from '@ember/routing/route'; +import { getOwner } from '@ember/application'; +import { inject as service } from '@ember/service'; +import { task, timeout } from 'ember-concurrency'; +import { action } from '@ember/object'; +import { notifyError } from 'core/decorators/notify'; +import config from '../../../../../config/environment'; + +const POLL_TIMEOUT_SECONDS = config.oidcPollingTimeoutSeconds; + +export default class ScopesScopeAuthenticateMethodOidcRoute extends Route { + + // =services + + @service session; + + // =attributes + + /** + * A simple Ember Concurrency-based polling task that refreshes the route + * every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes + * to session `status` that may occur. + * + * NOTE: tasks are sort of attributes and sort of methods, but they are not + * language-level constructs. Thus we annotate this task as if it + * is an attribute. + * @type {Task} + */ + @task(function * () { + while(!this.session.isAuthenticated) { + yield timeout(POLL_TIMEOUT_SECONDS * 1000); + yield this.refresh(); + } + /* eslint-disable-next-line prettier/prettier */ + }).drop() poller; + + // =methods + + model() { + const scope = this.modelFor('scopes.scope'); + const authMethod = this.modelFor('scopes.scope.authenticate.method'); + const authenticatorName = `authenticator:${authMethod.type}`; + const oidc = getOwner(this).lookup(authenticatorName); + // TODO: delegate this call from the session service so that we don't have + // to look up the authenticator directly + return oidc.attemptFetchToken({ scope, authMethod }); + } + + /** + * If authenticated, redirects to index for further processing. + */ + redirect() { + if (this.session.isAuthenticated) this.replaceWith('index'); + } + + /** + * When this route is activated (entered), begin polling for changes. + */ + activate() { + this.poller.perform(); + } + + /** + * When this route is deactivated (exited), stop polling for changes. + */ + deactivate() { + this.poller.cancelAll(); + } + + /** + * An error in this route indicates authentication failed. The user is + * notified and returned to the index. + */ + @action + @notifyError(() => 'errors.authentication-failed.title', { catch: true }) + error(e) { + this.transitionTo('scopes.scope.authenticate.method.index'); + // rethrow the error to activate the notifyError decorator + throw e; + } + +} diff --git a/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs b/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs index e05057cee..c24cd6895 100644 --- a/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/desktop/app/templates/scopes/scope/authenticate/method.hbs @@ -1,17 +1 @@ -{{#if (feature-flag 'oidc')}} - {{! OIDC enabled: show form for any auth method}} - -{{else}} - {{! OIDC disabled: hide OIDC auth form}} - {{#unless @model.isOIDC}} - - {{/unless}} -{{/if}} +{{outlet}} diff --git a/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs b/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs new file mode 100644 index 000000000..e05057cee --- /dev/null +++ b/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs @@ -0,0 +1,17 @@ +{{#if (feature-flag 'oidc')}} + {{! OIDC enabled: show form for any auth method}} + +{{else}} + {{! OIDC disabled: hide OIDC auth form}} + {{#unless @model.isOIDC}} + + {{/unless}} +{{/if}} diff --git a/ui/desktop/app/templates/scopes/scope/authenticate/method/oidc.hbs b/ui/desktop/app/templates/scopes/scope/authenticate/method/oidc.hbs new file mode 100644 index 000000000..c6e38e882 --- /dev/null +++ b/ui/desktop/app/templates/scopes/scope/authenticate/method/oidc.hbs @@ -0,0 +1,23 @@ + + <:header> +

{{t 'resources.auth-method.messages.pending.title'}}

+ + <:body> +

{{t 'resources.auth-method.messages.pending.description'}}

+

+ {{t 'resources.auth-method.questions.no-see-window'}} + + {{t 'actions.retry'}} + +

+

+ + {{t 'actions.cancel'}} + +

+ +
diff --git a/ui/desktop/config/environment.js b/ui/desktop/config/environment.js index 50394aa0e..3f6883324 100644 --- a/ui/desktop/config/environment.js +++ b/ui/desktop/config/environment.js @@ -44,6 +44,7 @@ module.exports = function (environment) { notifyTimeout: 4000, sessionPollingTimeoutSeconds: 2.5, + oidcPollingTimeoutSeconds: 1, documentation: { baseURL: 'https://boundaryproject.io/help/desktop', diff --git a/ui/desktop/electron-app/src/handlers.js b/ui/desktop/electron-app/src/handlers.js index 6a251d742..d3b1d4d30 100644 --- a/ui/desktop/electron-app/src/handlers.js +++ b/ui/desktop/electron-app/src/handlers.js @@ -1,4 +1,5 @@ const { shell } = require('electron'); +const isDev = require('electron-is-dev'); const handle = require('./ipc-handler'); const boundaryCli = require('./boundary-cli'); const origin = require('./origin.js'); @@ -26,10 +27,11 @@ handle('resetOrigin', async (requestOrigin) => { }); /** - * Opens the specified URL in an external browser. Only HTTPS URLs are allowed. + * Opens the specified URL in an external browser. Only HTTPS URLs are allowed, + * except in development where all protocols are allowed. */ handle('openExternal', async (href) => { - if (href.startsWith('https://')) { + if (href.startsWith('https://') || isDev) { // openExternal is necessary in order to display documentation and to // support arbitrary OIDC flows. The protocol is validated. shell.openExternal(href); /* eng-disable OPEN_EXTERNAL_JS_CHECK */ diff --git a/ui/desktop/package.json b/ui/desktop/package.json index a34495516..aa2e7152b 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -36,6 +36,9 @@ "postinstall": "cd electron-app && yarn install", "posttest": "ember coverage-merge && yarn clean:coverage" }, + "dependencies": { + "ember-named-blocks-polyfill": "^0.2.4" + }, "devDependencies": { "@doyensec/electronegativity": "^1.8.1", "@ember/jquery": "^1.1.0", diff --git a/ui/desktop/tests/acceptance/authentication-test.js b/ui/desktop/tests/acceptance/authentication-test.js index fa34de896..514d1d2b7 100644 --- a/ui/desktop/tests/acceptance/authentication-test.js +++ b/ui/desktop/tests/acceptance/authentication-test.js @@ -87,9 +87,11 @@ module('Acceptance | authentication', function (hooks) { // create other resources instances.authMethods.global = this.server.create('auth-method', { scope: instances.scopes.global, + type: 'password', }); instances.authMethods.org = this.server.create('auth-method', { scope: instances.scopes.org, + type: 'password', }); urls.scopes.global = `/scopes/${instances.scopes.global.id}`; diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js new file mode 100644 index 000000000..0a98d8794 --- /dev/null +++ b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Controller | scopes/scope/authenticate/method/oidc', + function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/authenticate/method/oidc' + ); + assert.ok(controller); + }); + } +); diff --git a/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/index-test.js b/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/index-test.js new file mode 100644 index 000000000..10a6a561c --- /dev/null +++ b/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/index-test.js @@ -0,0 +1,16 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Route | scopes/scope/authenticate/method/index', + function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/authenticate/method/index' + ); + assert.ok(route); + }); + } +); diff --git a/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js b/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js new file mode 100644 index 000000000..2c2764470 --- /dev/null +++ b/ui/desktop/tests/unit/routes/scopes/scope/authenticate/method/oidc-test.js @@ -0,0 +1,16 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Route | scopes/scope/authenticate/method/oidc', + function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/authenticate/method/oidc' + ); + assert.ok(route); + }); + } +); diff --git a/yarn.lock b/yarn.lock index 3851d6409..13ab98965 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7571,7 +7571,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -12336,7 +12336,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -14034,11 +14034,6 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14047,16 +14042,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*, lodash._bindcallback@^3.0.0: +lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14066,19 +14056,12 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14296,7 +14279,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@*, lodash.restparam@^3.0.0: +lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= From 1ed685d81fb7dfe3344090a460c17d190b6bd3d2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 12 Apr 2021 18:12:23 -0400 Subject: [PATCH 29/30] chore: enable OIDC feature flags --- ui/admin/config/environment.js | 2 +- ui/desktop/config/environment.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js index cfbcb155b..f36c204a5 100644 --- a/ui/admin/config/environment.js +++ b/ui/admin/config/environment.js @@ -86,7 +86,7 @@ module.exports = function (environment) { }, featureFlags: { - oidc: false, + oidc: true, 'oidc-crud': false, 'primary-auth-method': false, }, diff --git a/ui/desktop/config/environment.js b/ui/desktop/config/environment.js index 3f6883324..c45790089 100644 --- a/ui/desktop/config/environment.js +++ b/ui/desktop/config/environment.js @@ -55,7 +55,7 @@ module.exports = function (environment) { }, featureFlags: { - oidc: false, + oidc: true, }, }; From 8d3aea2e23949d8c796cba19ab1b011163a9a26d Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 13 Apr 2021 11:20:29 -0400 Subject: [PATCH 30/30] chore: allow insecure localhost external links to open --- ui/desktop/electron-app/src/handlers.js | 12 +++++++---- yarn.lock | 27 +++++-------------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/ui/desktop/electron-app/src/handlers.js b/ui/desktop/electron-app/src/handlers.js index d3b1d4d30..4e102212a 100644 --- a/ui/desktop/electron-app/src/handlers.js +++ b/ui/desktop/electron-app/src/handlers.js @@ -27,13 +27,17 @@ handle('resetOrigin', async (requestOrigin) => { }); /** - * Opens the specified URL in an external browser. Only HTTPS URLs are allowed, - * except in development where all protocols are allowed. + * Opens the specified URL in an external browser. Only secure HTTPs URLs are + * allowed except in the case of localhost (which enables development and + * testing workflows). Insecure URLs are allowed in dev mode. */ handle('openExternal', async (href) => { - if (href.startsWith('https://') || isDev) { + const isSecure = href.startsWith('https://'); + const isLocalhost = href.startsWith('http://localhost') + || href.startsWith('http://127.0.0.1'); + if (isSecure || isLocalhost || isDev) { // openExternal is necessary in order to display documentation and to - // support arbitrary OIDC flows. The protocol is validated. + // support arbitrary OIDC flows. The protocol is validated (see above). shell.openExternal(href); /* eng-disable OPEN_EXTERNAL_JS_CHECK */ } else { throw new Error( diff --git a/yarn.lock b/yarn.lock index daf05ce0d..249fbe42c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7578,7 +7578,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -12343,7 +12343,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -14041,11 +14041,6 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14054,16 +14049,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*, lodash._bindcallback@^3.0.0: +lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14073,19 +14063,12 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14303,7 +14286,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@*, lodash.restparam@^3.0.0: +lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=