From 7a5d439d417a3405926209cf22e8eeecab59f563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Martins=20N=C3=A1poli?= Date: Mon, 10 Aug 2020 16:35:05 -0300 Subject: [PATCH 1/7] Revert "Revert/new login 2 (#924)" This reverts commit 1649e2edc447d1f6493e76f4afd3791b27cb319a. --- CHANGELOG.md | 3 - package.json | 7 +- src/api/clients/IOClients/external/VTEXID.ts | 34 +++ .../auth/AuthProviders/OAuthAuthenticator.ts | 64 ----- .../OAuthAuthenticator/LoginServer.ts | 174 +++++++++++++ .../AuthProviders/OAuthAuthenticator/index.ts | 101 ++++++++ src/lib/sse/index.ts | 38 +-- src/lib/utils/randomCryptoString.test.ts | 10 + src/lib/utils/randomCryptoString.ts | 18 ++ yarn.lock | 229 +++++++++++++++++- 10 files changed, 566 insertions(+), 112 deletions(-) delete mode 100644 src/lib/auth/AuthProviders/OAuthAuthenticator.ts create mode 100644 src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts create mode 100644 src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts create mode 100644 src/lib/utils/randomCryptoString.test.ts create mode 100644 src/lib/utils/randomCryptoString.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c4aa38b1a..3dfe31922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [vtex workspace abtest] Update command plugin version - Remove `CommandError` class and move to `FLOW_ISSUE_ERROR` kind inside `ErrorReport` class. -### Fixed - - [vtex login] Revert to use old login flow due to issues on local server port. - ## [2.106.4] - 2020-07-30 ### Changed - [vtex login] Use new VTEX ID login flow. diff --git a/package.json b/package.json index 3ccf4a621..3cf278b8f 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "bin": "bin/run", "main": "build/api/index.js", "scripts": { - "exe": "node scripts/make-executable.js", - "watch": "bash ./scripts/symlink.sh && yarn nodemon && yarn oclif-dev manifest", + "watch": "yarn build-clean && bash ./scripts/symlink.sh && yarn nodemon", "format": "prettier --config ./.prettierrc --write \"./src/**/*.{ts,tsx,js,jsx,json}\"", "lint:node": "yarn eslint ./src --cache --ext ts --config .eslintrc", "format-lint": "yarn format && yarn lint:node", @@ -75,6 +74,7 @@ "cli-table": "~0.3.1", "cli-table2": "~0.2.0", "clipboardy": "~2.1.0", + "co-body": "^6.0.0", "configstore": "^5.0.1", "csvtojson": "~2.0.10", "debounce": "~1.2.0", @@ -83,6 +83,7 @@ "eventsource": "~1.0.7", "extendable-error": "~0.1.5", "fs-extra": "~7.0.0", + "get-port": "^5.1.1", "get-stream": "~4.0.0", "globby": "~8.0.1", "graphql": "^14.2.1", @@ -90,6 +91,7 @@ "is-wsl": "^2.1.1", "js-yaml": "~3.13.1", "jsonwebtoken": "~8.5.1", + "koa": "^2.13.0", "latest-version": "^4.0.0", "moment": "~2.24.0", "node-notifier": "^6.0.0", @@ -123,6 +125,7 @@ "devDependencies": { "@oclif/dev-cli": "^1", "@types/async-retry": "1.4.1", + "@types/co-body": "^5.1.0", "@types/configstore": "^4.0.0", "@types/debounce": "^1.2.0", "@types/eventsource": "^1.1.2", diff --git a/src/api/clients/IOClients/external/VTEXID.ts b/src/api/clients/IOClients/external/VTEXID.ts index f10c40b78..8b777556b 100644 --- a/src/api/clients/IOClients/external/VTEXID.ts +++ b/src/api/clients/IOClients/external/VTEXID.ts @@ -1,5 +1,6 @@ import { InstanceOptions, IOClient, IOContext } from '@vtex/api' import opn from 'opn' +import querystring from 'querystring' import { storeUrl } from '../../../storeUrl' import { IOClientFactory } from '../IOClientFactory' @@ -8,6 +9,7 @@ export class VTEXID extends IOClient { private static readonly DEFAULT_RETRIES = 2 private static readonly BASE_URL = 'https://vtexid.vtex.com.br' private static readonly API_PATH_PREFIX = '/api/vtexid' + private static readonly TOOLBELT_API_PATH_PREFIX = `${VTEXID.API_PATH_PREFIX}/toolbelt` private static readonly VTEX_ID_AUTH_COOKIE = 'VtexIdClientAutCookie' public static createClient(customContext: Partial = {}, customOptions: Partial = {}) { @@ -30,6 +32,25 @@ export class VTEXID extends IOClient { }) } + public startToolbeltLogin({ account, secretHash, loopbackUrl }: StartToolbeltLoginInput) { + const body = querystring.stringify({ + secretHash, + loopbackUrl, + }) + + return this.http.post(`${VTEXID.TOOLBELT_API_PATH_PREFIX}/start?an=${account}`, body) + } + + public validateToolbeltLogin({ account, state, secret, ott }: ValidateToolbeltLoginInput) { + const body = querystring.stringify({ + state, + secret, + ott, + }) + + return this.http.post<{ token: string }>(`${VTEXID.TOOLBELT_API_PATH_PREFIX}/validate?an=${account}`, body) + } + public invalidateToolbeltToken(token: string) { return this.http.get(`/api/vtexid/pub/logout?scope=`, { headers: { @@ -38,3 +59,16 @@ export class VTEXID extends IOClient { }) } } + +interface StartToolbeltLoginInput { + account: string + secretHash: string + loopbackUrl: string +} + +interface ValidateToolbeltLoginInput { + account: string + state: string + secret: string + ott: string +} diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator.ts deleted file mode 100644 index 5e8620667..000000000 --- a/src/lib/auth/AuthProviders/OAuthAuthenticator.ts +++ /dev/null @@ -1,64 +0,0 @@ -import axios from 'axios' -import jwt from 'jsonwebtoken' -import opn from 'opn' -import { join } from 'path' -import randomstring from 'randomstring' -import { clusterIdDomainInfix, publicEndpoint } from '../../../api/env' -import { onAuth } from '../../sse' -import { spawnUnblockingChildProcess } from '../../utils/spawnUnblockingChildProcess' -import { AuthProviderBase } from './AuthProviderBase' - -export class OAuthAuthenticator extends AuthProviderBase { - public static readonly AUTH_TYPE = 'oauth' - - public async login(account: string, workspace: string) { - const [token, returnUrl] = await this.startUserAuth(account, workspace) - const decodedToken = jwt.decode(token) - const login: string = decodedToken.sub - this.closeChromeTabIfMac(returnUrl) - return { login, token, returnUrl } - } - - private closeChromeTabIfMac(returnUrl: string) { - if (process.platform === 'darwin') { - spawnUnblockingChildProcess('osascript', [join(__dirname, '../../../../scripts/closeChrome.scpt'), returnUrl]) - } - } - - private async startUserAuth(account: string, workspace: string): Promise { - const state = randomstring.generate() - const [url, fullReturnUrl] = await this.getLoginUrl(account, workspace, state) - opn(url, { wait: false }) - return onAuth(account, workspace, state, fullReturnUrl) - } - - private getOldLoginUrls(workspace: string, state: string) { - const returnUrl = `/_v/private/auth-server/v1/callback?workspace=${workspace}&state=${state}` - const url = `/_v/private/auth-server/v1/login/?workspace=${workspace}` - return [url, returnUrl] - } - - private getNewLoginUrls(workspace: string, state: string) { - const returnUrl = `/_v/private/auth-server/v1/callback?workspace=${workspace}&state=${state}` - const url = `/_v/segment/admin-login/v1/login?workspace=${workspace}` - return [url, returnUrl] - } - - private async getLoginUrl(account: string, workspace: string, state: string): Promise<[string, string]> { - const baseUrl = `https://${account}${clusterIdDomainInfix()}.${publicEndpoint()}` - let [url, returnUrl] = this.getNewLoginUrls(workspace, state) - try { - const response = await axios.get(`${baseUrl}${url}`) - if (!response.data.match(/vtex\.admin-login/)) { - throw new Error('Unexpected response from admin-login') - } - } catch (e) { - const oldUrls = this.getOldLoginUrls(workspace, state) - url = oldUrls[0] - returnUrl = oldUrls[1] - } - const fullReturnUrl = baseUrl + returnUrl - const returnUrlEncoded = encodeURIComponent(returnUrl) - return [`${baseUrl}${url}&returnUrl=${returnUrlEncoded}`, fullReturnUrl] - } -} diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts new file mode 100644 index 000000000..a87da237b --- /dev/null +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts @@ -0,0 +1,174 @@ +import asyncRetry from 'async-retry' +import coBody from 'co-body' +import getPort from 'get-port' +import { Server } from 'http' +import Koa from 'koa' +import { logger } from '../../../../api' +import { VTEXID } from '../../../../api/clients/IOClients/external/VTEXID' +import { ErrorReport } from '../../../../api/error/ErrorReport' + +const SUCCESS_PAGE = ` + + + + Success + + + +

Você já pode fechar essa janela.

+

You may now close this window.

+

Ahora puedes cerrar esta ventana.

+ +` + +export class LoginServer { + private static readonly LOGIN_CALLBACK_PATH = '/login_callback' + + public static async create(loginConfig: LoginConfig) { + const loginServer = new LoginServer(loginConfig) + await loginServer.start() + return loginServer + } + + private app: Koa + private port: number + private server: Server + + private loginState?: string + + private tokenPromise: Promise + private resolveTokenPromise: (val: string) => void + private rejectTokenPromise: (err: any) => void + + constructor(private loginConfig: LoginConfig) { + this.app = new Koa() + this.registerLoginHandler() + + this.tokenPromise = new Promise((resolve, reject) => { + this.resolveTokenPromise = resolve + this.rejectTokenPromise = reject + }) + } + + get token() { + return this.tokenPromise + } + + get loginCallbackUrl() { + if (!this.port) { + throw new Error('LoginServer not initialized') + } + + return `http://127.0.0.1:${this.port}${LoginServer.LOGIN_CALLBACK_PATH}` + } + + public setLoginState(val: string) { + this.loginState = val + } + + public start() { + return asyncRetry( + async bail => { + try { + const port = await getPort({ + port: getPort.makeRange(3000, 3050), + }) + + this.server = await this.initServer(port) + this.port = port + logger.debug(`LoginServer started on http://localhost:${this.port}`) + } catch (err) { + logger.debug(`LoginServer failed to start on port:${this.port}. Reason: ${err.message}.`) + if (err.code !== 'EADDRINUSE') { + return bail(err) + } + } + }, + { retries: 2, maxTimeout: 50, minTimeout: 50 } + ) + } + + public close() { + this.server.unref() + this.server.close() + } + + private initServer(port: number): Promise { + return new Promise((resolve, reject) => { + this.app.on('error', reject) + const server = this.app.listen(port, () => { + server.on('connection', socket => { + socket.unref() + }) + + resolve(server) + }) + }) + } + + private registerLoginHandler() { + this.app.use(async ctx => { + ctx.set('connection', 'close') + + if (ctx.path !== LoginServer.LOGIN_CALLBACK_PATH) { + ctx.status = 404 + ctx.body = 'Not found' + return + } + + ctx.socket.ref() + logger.debug(`Received ${ctx.method} login callback`) + if (!this.loginState) { + return this.handleError(ctx, new Error('Received login callback before setting login state')) + } + + let body + if (ctx.method.toLowerCase() === 'post') { + try { + body = await coBody(ctx.req) + } catch (err) { + return this.handleError(ctx, err) + } + } else { + body = { ott: ctx.query?.ott } + } + + if (!body.ott) { + return this.handleError(ctx, new Error('Missing ott on VTEX ID callback call'), { body }) + } + + const vtexId = VTEXID.createClient({ account: this.loginConfig.account }) + try { + const { token } = await vtexId.validateToolbeltLogin({ + account: this.loginConfig.account, + secret: this.loginConfig.secret, + ott: body.ott, + state: this.loginState, + }) + + this.resolveTokenPromise(token) + ctx.status = 200 + ctx.set('content-type', 'text/html') + ctx.body = SUCCESS_PAGE + } catch (err) { + return this.handleError(ctx, err) + } + }) + } + + private handleError(ctx: Koa.ParameterizedContext, err: any, details?: Record) { + const errReport = ErrorReport.createAndMaybeRegisterOnTelemetry({ originalError: err, details }) + ctx.status = 500 + ctx.body = { + errorId: errReport.metadata.errorId, + message: errReport.message, + } + + this.rejectTokenPromise(errReport) + } +} + +interface LoginConfig { + account: string + secret: string +} diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts new file mode 100644 index 000000000..f67832f08 --- /dev/null +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts @@ -0,0 +1,101 @@ +import axios from 'axios' +import { createHash } from 'crypto' +import jwt from 'jsonwebtoken' +import opn from 'opn' +import { join } from 'path' +import { VTEXID } from '../../../../api/clients/IOClients/external/VTEXID' +import { storeUrl } from '../../../../api/storeUrl' +import { randomCryptoString } from '../../../utils/randomCryptoString' +import { spawnUnblockingChildProcess } from '../../../utils/spawnUnblockingChildProcess' +import { AuthProviderBase } from '../AuthProviderBase' +import { LoginServer } from './LoginServer' + +export class OAuthAuthenticator extends AuthProviderBase { + public static readonly AUTH_TYPE = 'oauth' + private static readonly SECRET_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' + + private static ADMIN_LOGIN_URL_PATH = '/_v/segment/admin-login/v1/login' + private static FALLBACK_AUTH_SERVER_LOGIN_URL_PATH = '/_v/private/auth-server/v1/login' + + public async login(account: string) { + const secret = randomCryptoString(128, OAuthAuthenticator.SECRET_ALPHABET) + const secretHash = this.hashSecret(secret) + const vtexId = VTEXID.createClient({ account }) + + const loginServer = await LoginServer.create({ + account, + secret, + }) + + try { + const loginState = await vtexId.startToolbeltLogin({ + account, + secretHash, + loopbackUrl: loginServer.loginCallbackUrl, + }) + + loginServer.setLoginState(loginState) + + const url = await this.loginUrl(account, loginState) + opn(url, { wait: false }) + + const token = await loginServer.token + const decodedToken = jwt.decode(token) + const login: string = decodedToken.sub + this.closeChromeTabIfMac(loginServer.loginCallbackUrl) + return { login, token } + } finally { + loginServer.close() + } + } + + private hashSecret(secret: string) { + return createHash('sha256') + .update(secret) + .digest('base64') + } + + private async loginUrl(account: string, loginState: string) { + const hasAdminLogin = await this.hasAdminLoginInstalled(account) + const returnUrl = `/api/vtexid/toolbelt/callback?state=${encodeURIComponent(loginState)}` + + let loginPathPrefix: string + if (!hasAdminLogin) { + // If for some reason vtex.admin-login is not installed in the account, fallback to use auth-server login url + loginPathPrefix = storeUrl({ + account, + addWorkspace: false, + path: OAuthAuthenticator.FALLBACK_AUTH_SERVER_LOGIN_URL_PATH, + }) + } else { + loginPathPrefix = storeUrl({ account, addWorkspace: false, path: OAuthAuthenticator.ADMIN_LOGIN_URL_PATH }) + } + + return `${loginPathPrefix}?returnUrl=${encodeURIComponent(returnUrl)}` + } + + private async hasAdminLoginInstalled(account: string) { + try { + const { data } = await axios.get( + storeUrl({ account, addWorkspace: false, path: '/_v/segment/admin-login/v1/login' }) + ) + + return data.includes('vtex.admin-login') + } catch (err) { + if (err.response?.status === 404) { + return false + } + + throw err + } + } + + private closeChromeTabIfMac(loginCallbackUrl: string) { + if (process.platform === 'darwin') { + spawnUnblockingChildProcess('osascript', [ + join(__dirname, '../../../../scripts/closeChrome.scpt'), + loginCallbackUrl, + ]) + } + } +} diff --git a/src/lib/sse/index.ts b/src/lib/sse/index.ts index 5d21a6bfc..c8c4f2cdc 100644 --- a/src/lib/sse/index.ts +++ b/src/lib/sse/index.ts @@ -1,12 +1,11 @@ import chalk from 'chalk' import { compose, contains, forEach, path, pathOr } from 'ramda' -import { colossusEndpoint, publicEndpoint } from '../../api/env' -import { SSEConnectionError } from '../../api/error/errors' +import { colossusEndpoint } from '../../api/env' +import { ErrorKinds } from '../../api/error/ErrorKinds' +import { ErrorReport } from '../../api/error/ErrorReport' import { removeVersion } from '../../api/locator' import log from '../../api/logger' import { isVerbose } from '../../api/verbose' -import { ErrorKinds } from '../../api/error/ErrorKinds' -import { ErrorReport } from '../../api/error/ErrorReport' import { CustomEventSource } from './CustomEventSource' import { EventSourceError } from './EventSourceError' @@ -152,34 +151,3 @@ export const logAll = (context: Context, logLevel: string, id: string, senders?: return onLog(context, id, logLevel, callback, senders) } - -export const onAuth = ( - account: string, - workspace: string, - state: string, - returnUrl: string -): Promise<[string, string]> => { - const source = `https://${workspace}--${account}.${publicEndpoint()}/_v/private/auth-server/v1/sse/${state}` - log.debug(`Listening for auth events from: ${source}`) - const es = CustomEventSource.create({ source }) - return new Promise((resolve, reject) => { - es.onmessage = (msg: MessageJSON) => { - const { body: token } = JSON.parse(msg.data) as { body: string } - es.close() - resolve([token, returnUrl]) - } - - es.onerror = err => { - es.close() - const errMessage = `Connection to login server has failed${ - err.event.status ? ` with status ${err.event.status}` : '' - }` - ErrorReport.createAndMaybeRegisterOnTelemetry({ - kind: ErrorKinds.SSE_ERROR, - originalError: err, - }).logErrorForUser({ coreLogLevelDefault: 'debug', logLevels: { core: { errorId: 'error' } } }) - - reject(new SSEConnectionError(errMessage, err.event.status)) - } - }) -} diff --git a/src/lib/utils/randomCryptoString.test.ts b/src/lib/utils/randomCryptoString.test.ts new file mode 100644 index 000000000..4298a7e20 --- /dev/null +++ b/src/lib/utils/randomCryptoString.test.ts @@ -0,0 +1,10 @@ +import { randomCryptoString } from './randomCryptoString' + +it('Creates a string of length specified and only uses characters from alphabet', () => { + const alph = ['a', 'b', 'c', 'd', 'e', '_', '~', '-'] + const str = randomCryptoString(60, alph.join('')) + expect(str.length).toEqual(60) + for (let i = 0; i < str.length; i++) { + expect(alph).toContain(str.charAt(i)) + } +}) diff --git a/src/lib/utils/randomCryptoString.ts b/src/lib/utils/randomCryptoString.ts new file mode 100644 index 000000000..25149b293 --- /dev/null +++ b/src/lib/utils/randomCryptoString.ts @@ -0,0 +1,18 @@ +import { randomBytes } from 'crypto' + +export function randomCryptoString(stringLength: number, alphabet: string) { + if (alphabet.length > 256) { + throw new Error("Argument 'alphabet' can't have more than 256 characters") + } + + const bytes = randomBytes(stringLength) + const result = new Array(stringLength) + + let cursor = 0 + for (let i = 0; i < stringLength; i++) { + cursor += bytes[i] + result[i] = alphabet[cursor % alphabet.length] + } + + return result.join('') +} diff --git a/yarn.lock b/yarn.lock index 2a6fb3f1e..ed3e4039e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -706,6 +706,14 @@ "@types/connect" "*" "@types/node" "*" +"@types/co-body@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/co-body/-/co-body-5.1.0.tgz#69c505dd759d619a8c78b1a88222e63014c9f417" + integrity sha512-iRL97yYTJNGFv495U63ikKG9r60thDtQ403jEzBEFR4IY6kMxw2IfcPoxI8+HY3nRNLrwHFYuCnWGEB/0hFVwg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1230,6 +1238,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@^1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + acorn-globals@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" @@ -2285,6 +2301,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -2795,6 +2819,13 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= +content-disposition@~0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + content-type@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -2812,6 +2843,14 @@ cookie@^0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookies@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -2941,7 +2980,7 @@ debounce@~1.2.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== -debug@=3.1.0: +debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== @@ -2986,6 +3025,11 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -3042,16 +3086,31 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + deprecated-decorator@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= +destroy@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + detect-indent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" @@ -3171,6 +3230,11 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + "emoji-regex@>=6.0.0 <=6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" @@ -3208,6 +3272,11 @@ enabled@1.0.x: dependencies: env-variable "0.0.x" +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3265,6 +3334,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -3890,6 +3964,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + from2@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -3981,6 +4060,11 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -4360,6 +4444,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-assert@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878" + integrity sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.7.2" + http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" @@ -4382,7 +4474,7 @@ http-call@^5.1.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" -http-errors@1.7.3, http-errors@^1.7.3: +http-errors@1.7.3, http-errors@^1.7.3, http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -4393,6 +4485,17 @@ http-errors@1.7.3, http-errors@^1.7.3: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@^1.6.3: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -4699,6 +4802,11 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -5498,6 +5606,13 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -5541,11 +5656,55 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +koa-compose@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= + dependencies: + any-promise "^1.1.0" + koa-compose@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + +koa@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.0.tgz#25217e05efd3358a7e5ddec00f0a380c9b71b501" + integrity sha512-i/XJVOfPw7npbMv67+bOeXr3gPqOAw6uh5wFyNs3QvJ47tUx3M3V9rIE0//WytY42MKz4l/MXKyGkQ2LQTfLUQ== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.8.0" + debug "~3.1.0" + delegates "^1.0.0" + depd "^1.1.2" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^1.2.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + koalas@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/koalas/-/koalas-1.0.2.tgz#318433f074235db78fae5661a02a8ca53ee295cd" @@ -5965,6 +6124,11 @@ mime-db@1.43.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.26" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" @@ -5972,6 +6136,13 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.43.0" +mime-types@^2.1.18: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6104,6 +6275,11 @@ natural-orderby@^2.0.1: resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -6340,6 +6516,13 @@ object.values@^1.1.0, object.values@^1.1.1: function-bind "^1.1.1" has "^1.0.3" +on-finished@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -6359,6 +6542,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= + open@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" @@ -6570,6 +6758,11 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parseurl@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -7339,16 +7532,16 @@ rxjs@^6.5.3: dependencies: tslib "^1.9.0" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -7437,6 +7630,11 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + sha.js@^2.4.11: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -7682,7 +7880,7 @@ stats-lite@vtex/node-stats-lite#dist: dependencies: isnumber "~1.0.0" -"statuses@>= 1.5.0 < 2": +"statuses@>= 1.5.0 < 2", statuses@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -8212,6 +8410,11 @@ tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -8468,6 +8671,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -8779,6 +8987,11 @@ yarn@^1.22.4: resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.4.tgz#01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e" integrity sha512-oYM7hi/lIWm9bCoDMEWgffW8aiNZXCWeZ1/tGy0DWrN6vmzjCXIKu2Y21o8DYVBUtiktwKcNoxyGl/2iKLUNGA== +ylru@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" + integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 136fdf9de7fa13264f053ca3d569ad28ec9be340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Martins=20N=C3=A1poli?= Date: Mon, 10 Aug 2020 17:35:36 -0300 Subject: [PATCH 2/7] [deps] Replace get-port with detect-port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tiago Martins Nápoli --- package.json | 3 ++- yarn.lock | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 3cf278b8f..455c8a989 100644 --- a/package.json +++ b/package.json @@ -78,12 +78,12 @@ "configstore": "^5.0.1", "csvtojson": "~2.0.10", "debounce": "~1.2.0", + "detect-port": "^1.3.0", "diff": "~3.5.0", "enquirer": "~2.3.2", "eventsource": "~1.0.7", "extendable-error": "~0.1.5", "fs-extra": "~7.0.0", - "get-port": "^5.1.1", "get-stream": "~4.0.0", "globby": "~8.0.1", "graphql": "^14.2.1", @@ -128,6 +128,7 @@ "@types/co-body": "^5.1.0", "@types/configstore": "^4.0.0", "@types/debounce": "^1.2.0", + "@types/detect-port": "^1.3.0", "@types/eventsource": "^1.1.2", "@types/fs-extra": "5.0.4", "@types/jest": "24.0.23", diff --git a/yarn.lock b/yarn.lock index ed3e4039e..e63adc355 100644 --- a/yarn.lock +++ b/yarn.lock @@ -751,6 +751,11 @@ resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.2.0.tgz#9ee99259f41018c640b3929e1bb32c3dcecdb192" integrity sha512-bWG5wapaWgbss9E238T0R6bfo5Fh3OkeoSt245CM7JJwVwpw6MEBCbIxLq5z8KzsE3uJhzcIuQkyiZmzV3M/Dw== +"@types/detect-port@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/detect-port/-/detect-port-1.3.0.tgz#3e9cbd049ec29e84a2ff7852dbc629c81245774c" + integrity sha512-NnDUDk1Ry5cHljTFetg0BNT79FaJSddTh9RsGOS2v/97DwOUJ+hBkgxtQHF6T8IarZD4i+bFEL787Nz+xpodfA== + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1279,6 +1284,11 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + agentkeepalive@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.0.tgz#a48e040ed16745dd29ce923675f60c9c90f39ee0" @@ -2987,7 +2997,7 @@ debug@=3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -3121,6 +3131,14 @@ detect-newline@^2.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + diagnostics@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" @@ -4060,11 +4078,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" From ceccf860188d46cb0602929ff8603af31ed0627c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Martins=20N=C3=A1poli?= Date: Mon, 10 Aug 2020 17:36:30 -0300 Subject: [PATCH 3/7] Fix free port retrieval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tiago Martins Nápoli --- src/api/error/ErrorKinds.ts | 1 + .../OAuthAuthenticator/LoginServer.ts | 31 +++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/api/error/ErrorKinds.ts b/src/api/error/ErrorKinds.ts index 8d9859ff8..d074c09a9 100644 --- a/src/api/error/ErrorKinds.ts +++ b/src/api/error/ErrorKinds.ts @@ -16,4 +16,5 @@ export const ErrorKinds = { APP_LOGS_PARSE_ERROR: 'LogsParseError', STICKY_HOST_ERROR: 'StickyHostError', FLOW_ISSUE_ERROR: 'FlowIssue', + LOGIN_SERVER_START_ERROR: 'LoginServerStartError', } diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts index a87da237b..8f7542771 100644 --- a/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts @@ -1,9 +1,9 @@ import asyncRetry from 'async-retry' import coBody from 'co-body' -import getPort from 'get-port' +import detectPort from 'detect-port' import { Server } from 'http' import Koa from 'koa' -import { logger } from '../../../../api' +import { ErrorKinds, logger } from '../../../../api' import { VTEXID } from '../../../../api/clients/IOClients/external/VTEXID' import { ErrorReport } from '../../../../api/error/ErrorReport' @@ -23,6 +23,7 @@ const SUCCESS_PAGE = ` export class LoginServer { private static readonly LOGIN_CALLBACK_PATH = '/login_callback' + private static readonly SERVER_START_RETRIES = 1 public static async create(loginConfig: LoginConfig) { const loginServer = new LoginServer(loginConfig) @@ -68,23 +69,31 @@ export class LoginServer { public start() { return asyncRetry( - async bail => { + async (bail, attemptNumber) => { try { - const port = await getPort({ - port: getPort.makeRange(3000, 3050), - }) + // detectPort will get the specified port or, if it's in use, another ramdom unnused port + this.port = await detectPort(3000) + this.server = await this.initServer(this.port) - this.server = await this.initServer(port) - this.port = port logger.debug(`LoginServer started on http://localhost:${this.port}`) } catch (err) { logger.debug(`LoginServer failed to start on port:${this.port}. Reason: ${err.message}.`) if (err.code !== 'EADDRINUSE') { return bail(err) } + + if (attemptNumber < LoginServer.SERVER_START_RETRIES + 1) { + logger.debug(`Retrying to start LoginServer...`) + } + + throw ErrorReport.createAndMaybeRegisterOnTelemetry({ + originalError: err, + kind: ErrorKinds.LOGIN_SERVER_START_ERROR, + details: { attemptNumber }, + }) } }, - { retries: 2, maxTimeout: 50, minTimeout: 50 } + { retries: LoginServer.SERVER_START_RETRIES, maxTimeout: 100, minTimeout: 100 } ) } @@ -95,14 +104,16 @@ export class LoginServer { private initServer(port: number): Promise { return new Promise((resolve, reject) => { - this.app.on('error', reject) const server = this.app.listen(port, () => { server.on('connection', socket => { socket.unref() }) + server.removeListener('error', reject) resolve(server) }) + + server.on('error', reject) }) } From 9cc184131c6754319dd0875fc34d22c466b661b7 Mon Sep 17 00:00:00 2001 From: tiagonapoli Date: Mon, 10 Aug 2020 21:19:32 -0300 Subject: [PATCH 4/7] Fix WSL2 login Signed-off-by: tiagonapoli --- .../auth/AuthProviders/OAuthAuthenticator/LoginServer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts index 8f7542771..8dd5f49d4 100644 --- a/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/LoginServer.ts @@ -24,6 +24,7 @@ const SUCCESS_PAGE = ` export class LoginServer { private static readonly LOGIN_CALLBACK_PATH = '/login_callback' private static readonly SERVER_START_RETRIES = 1 + private static readonly HOSTNAME = '127.0.0.1' public static async create(loginConfig: LoginConfig) { const loginServer = new LoginServer(loginConfig) @@ -60,7 +61,7 @@ export class LoginServer { throw new Error('LoginServer not initialized') } - return `http://127.0.0.1:${this.port}${LoginServer.LOGIN_CALLBACK_PATH}` + return `http://${LoginServer.HOSTNAME}:${this.port}${LoginServer.LOGIN_CALLBACK_PATH}` } public setLoginState(val: string) { @@ -74,8 +75,7 @@ export class LoginServer { // detectPort will get the specified port or, if it's in use, another ramdom unnused port this.port = await detectPort(3000) this.server = await this.initServer(this.port) - - logger.debug(`LoginServer started on http://localhost:${this.port}`) + logger.debug(`LoginServer started on http://${LoginServer.HOSTNAME}:${this.port}`) } catch (err) { logger.debug(`LoginServer failed to start on port:${this.port}. Reason: ${err.message}.`) if (err.code !== 'EADDRINUSE') { @@ -104,7 +104,7 @@ export class LoginServer { private initServer(port: number): Promise { return new Promise((resolve, reject) => { - const server = this.app.listen(port, () => { + const server = this.app.listen(port, LoginServer.HOSTNAME, () => { server.on('connection', socket => { socket.unref() }) From 71add9bb5ecf51c9e151d7285514567aefbd0050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Martins=20N=C3=A1poli?= Date: Mon, 10 Aug 2020 18:49:27 -0300 Subject: [PATCH 5/7] changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tiago Martins Nápoli --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfe31922..515d427c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- [vtex login] Use new VTEX ID login flow: + - Fix free TCP port retrieval: use `detect-port` package instead of `get-port`. + - Fix issue on WSL2 by specifying host binding to `127.0.0.1` when starting the server. + ## [2.108.0] - 2020-08-11 ### Added - [vtex workspace promote] Conflict handling. @@ -19,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [vtex workspace abtest] Update command plugin version - Remove `CommandError` class and move to `FLOW_ISSUE_ERROR` kind inside `ErrorReport` class. +### Fixed + - [vtex login] Revert to use old login flow due to issues on local server port. + ## [2.106.4] - 2020-07-30 ### Changed - [vtex login] Use new VTEX ID login flow. From 67f94502cea227958a07c9ece3ca835f41278116 Mon Sep 17 00:00:00 2001 From: tiagonapoli Date: Tue, 11 Aug 2020 20:21:10 -0300 Subject: [PATCH 6/7] Add WSL issue warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: tiagonapoli Signed-off-by: Tiago Martins Nápoli --- .../AuthProviders/OAuthAuthenticator/index.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts index f67832f08..69abf4188 100644 --- a/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts @@ -1,10 +1,13 @@ import axios from 'axios' import { createHash } from 'crypto' +import isWsl from 'is-wsl' import jwt from 'jsonwebtoken' import opn from 'opn' import { join } from 'path' +import { logger } from '../../../../api' import { VTEXID } from '../../../../api/clients/IOClients/external/VTEXID' import { storeUrl } from '../../../../api/storeUrl' +import { ColorifyConstants } from '../../../constants/Colors' import { randomCryptoString } from '../../../utils/randomCryptoString' import { spawnUnblockingChildProcess } from '../../../utils/spawnUnblockingChildProcess' import { AuthProviderBase } from '../AuthProviderBase' @@ -39,6 +42,24 @@ export class OAuthAuthenticator extends AuthProviderBase { const url = await this.loginUrl(account, loginState) opn(url, { wait: false }) + if (isWsl) { + logger.warn( + "We noticed you're using WSL, in which case you may face login issues depending on your WSL version." + ) + + logger.warn( + `If you do, make sure your Windows is up to date and try again (${ColorifyConstants.URL_INTERACTIVE( + 'https://support.microsoft.com/en-us/help/4027667/windows-10-update' + )}).` + ) + + logger.warn( + `In case login errors persist after updating please create an issue on ${ColorifyConstants.URL_INTERACTIVE( + 'https://github.com/vtex/toolbelt/issues' + )}. We'll promptly help you finding a solution.` + ) + } + const token = await loginServer.token const decodedToken = jwt.decode(token) const login: string = decodedToken.sub From fefd07f57233a82ee22252b9ddb7280390ec8d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20N=C3=A1poli?= Date: Tue, 11 Aug 2020 21:19:00 -0300 Subject: [PATCH 7/7] Update src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Larícia Mota --- src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts index 69abf4188..56e6b7bd1 100644 --- a/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts +++ b/src/lib/auth/AuthProviders/OAuthAuthenticator/index.ts @@ -56,7 +56,7 @@ export class OAuthAuthenticator extends AuthProviderBase { logger.warn( `In case login errors persist after updating please create an issue on ${ColorifyConstants.URL_INTERACTIVE( 'https://github.com/vtex/toolbelt/issues' - )}. We'll promptly help you finding a solution.` + )}. We'll promptly help you find a solution.` ) }