From bf764842f579e51ee3a978a09505b049b3da764c Mon Sep 17 00:00:00 2001 From: chriswang521 Date: Fri, 21 Jun 2024 10:59:09 +0800 Subject: [PATCH] =?UTF-8?q?fixed:=20=E7=A7=BB=E9=99=A4github,github-authen?= =?UTF-8?q?tication,microsoft-authentication,tunnel-forwarding=E6=8F=92?= =?UTF-8?q?=E4=BB=B6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode-test.js | 5 - build/gulpfile.extensions.js | 4 - build/lib/mangle/index.ts | 2 - build/npm/dirs.js | 4 - extensions/github-authentication/.gitignore | 1 - .../github-authentication/.vscodeignore | 9 - extensions/github-authentication/README.md | 7 - .../extension-browser.webpack.config.js | 28 - .../extension.webpack.config.js | 17 - .../github-authentication/images/icon.png | Bin 3658 -> 0 bytes .../github-authentication/media/auth.css | 100 -- .../github-authentication/media/favicon.ico | Bin 34494 -> 0 bytes .../github-authentication/media/icon.png | Bin 3818 -> 0 bytes .../github-authentication/media/index.html | 37 - extensions/github-authentication/package.json | 75 -- .../github-authentication/package.nls.json | 4 - .../src/browser/authServer.ts | 12 - .../src/browser/buffer.ts | 8 - .../src/browser/crypto.ts | 6 - .../src/browser/fetch.ts | 6 - .../github-authentication/src/common/env.ts | 39 - .../src/common/errors.ts | 10 - .../src/common/experimentationService.ts | 97 -- .../src/common/keychain.ts | 48 - .../src/common/logger.ts | 32 - .../github-authentication/src/common/utils.ts | 118 --- .../github-authentication/src/config.ts | 15 - .../github-authentication/src/extension.ts | 46 - extensions/github-authentication/src/flows.ts | 502 --------- .../github-authentication/src/github.ts | 383 ------- .../github-authentication/src/githubServer.ts | 357 ------- .../src/node/authServer.ts | 198 ---- .../github-authentication/src/node/buffer.ts | 8 - .../github-authentication/src/node/crypto.ts | 8 - .../github-authentication/src/node/fetch.ts | 7 - .../src/test/flows.test.ts | 196 ---- .../src/test/node/authServer.test.ts | 65 -- .../github-authentication/tsconfig.json | 17 - extensions/github-authentication/yarn.lock | 233 ----- extensions/github/.vscodeignore | 7 - extensions/github/README.md | 12 - extensions/github/extension.webpack.config.js | 17 - extensions/github/images/icon.png | Bin 3657 -> 0 bytes extensions/github/markdown.css | 9 - extensions/github/package.json | 195 ---- extensions/github/package.nls.json | 27 - extensions/github/src/auth.ts | 89 -- extensions/github/src/branchProtection.ts | 244 ----- extensions/github/src/canonicalUriProvider.ts | 49 - extensions/github/src/commands.ts | 65 -- extensions/github/src/credentialProvider.ts | 64 -- extensions/github/src/extension.ts | 132 --- extensions/github/src/links.ts | 256 ----- extensions/github/src/publish.ts | 224 ---- extensions/github/src/pushErrorHandler.ts | 324 ------ extensions/github/src/remoteSourceProvider.ts | 139 --- .../github/src/remoteSourcePublisher.ts | 18 - extensions/github/src/shareProviders.ts | 113 --- extensions/github/src/test/github.test.ts | 65 -- extensions/github/src/test/index.ts | 30 - extensions/github/src/typings/git-base.d.ts | 85 -- extensions/github/src/typings/git.d.ts | 376 ------- extensions/github/src/typings/ref.d.ts | 6 - .../vscode.proposed.canonicalUriProvider.d.ts | 47 - .../vscode.proposed.shareProvider.d.ts | 29 - extensions/github/src/util.ts | 39 - .../.github/PULL_REQUEST_TEMPLATE.md | 0 .../.github/PULL_REQUEST_TEMPLATE/a.md | 0 .../.github/PULL_REQUEST_TEMPLATE/b.md | 0 .../.github/PULL_REQUEST_TEMPLATE/x.txt | 0 .../testWorkspace/PULL_REQUEST_TEMPLATE.md | 0 .../testWorkspace/PULL_REQUEST_TEMPLATE/a.md | 0 .../testWorkspace/PULL_REQUEST_TEMPLATE/b.md | 0 .../testWorkspace/PULL_REQUEST_TEMPLATE/x.txt | 0 .../docs/PULL_REQUEST_TEMPLATE.md | 0 .../docs/PULL_REQUEST_TEMPLATE/a.md | 0 .../docs/PULL_REQUEST_TEMPLATE/b.md | 0 .../docs/PULL_REQUEST_TEMPLATE/x.txt | 0 .../github/testWorkspace/some-markdown.md | 0 extensions/github/testWorkspace/x.txt | 0 extensions/github/tsconfig.json | 14 - extensions/github/yarn.lock | 319 ------ .../microsoft-authentication/.vscodeignore | 14 - extensions/microsoft-authentication/README.md | 9 - .../extension-browser.webpack.config.js | 31 - .../extension.webpack.config.js | 17 - .../microsoft-authentication/media/auth.css | 100 -- .../media/favicon.ico | Bin 34494 -> 0 bytes .../microsoft-authentication/media/icon.png | Bin 3818 -> 0 bytes .../microsoft-authentication/media/index.html | 37 - .../microsoft-authentication/package.json | 127 --- .../microsoft-authentication/package.nls.json | 29 - .../microsoft-authentication/src/AADHelper.ts | 953 ------------------ .../src/UriEventHandler.ts | 12 - .../src/betterSecretStorage.ts | 248 ----- .../src/browser/authServer.ts | 12 - .../src/browser/buffer.ts | 17 - .../src/browser/crypto.ts | 6 - .../src/browser/fetch.ts | 6 - .../src/common/async.ts | 49 - .../src/common/uri.ts | 37 - .../src/cryptoUtils.ts | 45 - .../microsoft-authentication/src/extension.ts | 181 ---- .../microsoft-authentication/src/logger.ts | 9 - .../src/node/authServer.ts | 198 ---- .../src/node/buffer.ts | 12 - .../src/node/crypto.ts | 7 - .../src/node/fetch.ts | 7 - .../microsoft-authentication/tsconfig.json | 27 - extensions/microsoft-authentication/yarn.lock | 210 ---- .../tunnel-forwarding/.vscode/launch.json | 15 - extensions/tunnel-forwarding/.vscodeignore | 5 - .../extension.webpack.config.js | 20 - extensions/tunnel-forwarding/media/icon.png | Bin 7191 -> 0 bytes extensions/tunnel-forwarding/package.json | 59 -- extensions/tunnel-forwarding/package.nls.json | 7 - .../tunnel-forwarding/src/deferredPromise.ts | 62 -- extensions/tunnel-forwarding/src/extension.ts | 330 ------ extensions/tunnel-forwarding/src/split.ts | 51 - extensions/tunnel-forwarding/tsconfig.json | 16 - extensions/tunnel-forwarding/yarn.lock | 8 - 121 files changed, 8671 deletions(-) delete mode 100644 extensions/github-authentication/.gitignore delete mode 100644 extensions/github-authentication/.vscodeignore delete mode 100644 extensions/github-authentication/README.md delete mode 100644 extensions/github-authentication/extension-browser.webpack.config.js delete mode 100644 extensions/github-authentication/extension.webpack.config.js delete mode 100644 extensions/github-authentication/images/icon.png delete mode 100644 extensions/github-authentication/media/auth.css delete mode 100644 extensions/github-authentication/media/favicon.ico delete mode 100644 extensions/github-authentication/media/icon.png delete mode 100644 extensions/github-authentication/media/index.html delete mode 100644 extensions/github-authentication/package.json delete mode 100644 extensions/github-authentication/package.nls.json delete mode 100644 extensions/github-authentication/src/browser/authServer.ts delete mode 100644 extensions/github-authentication/src/browser/buffer.ts delete mode 100644 extensions/github-authentication/src/browser/crypto.ts delete mode 100644 extensions/github-authentication/src/browser/fetch.ts delete mode 100644 extensions/github-authentication/src/common/env.ts delete mode 100644 extensions/github-authentication/src/common/errors.ts delete mode 100644 extensions/github-authentication/src/common/experimentationService.ts delete mode 100644 extensions/github-authentication/src/common/keychain.ts delete mode 100644 extensions/github-authentication/src/common/logger.ts delete mode 100644 extensions/github-authentication/src/common/utils.ts delete mode 100644 extensions/github-authentication/src/config.ts delete mode 100644 extensions/github-authentication/src/extension.ts delete mode 100644 extensions/github-authentication/src/flows.ts delete mode 100644 extensions/github-authentication/src/github.ts delete mode 100644 extensions/github-authentication/src/githubServer.ts delete mode 100644 extensions/github-authentication/src/node/authServer.ts delete mode 100644 extensions/github-authentication/src/node/buffer.ts delete mode 100644 extensions/github-authentication/src/node/crypto.ts delete mode 100644 extensions/github-authentication/src/node/fetch.ts delete mode 100644 extensions/github-authentication/src/test/flows.test.ts delete mode 100644 extensions/github-authentication/src/test/node/authServer.test.ts delete mode 100644 extensions/github-authentication/tsconfig.json delete mode 100644 extensions/github-authentication/yarn.lock delete mode 100644 extensions/github/.vscodeignore delete mode 100644 extensions/github/README.md delete mode 100644 extensions/github/extension.webpack.config.js delete mode 100644 extensions/github/images/icon.png delete mode 100644 extensions/github/markdown.css delete mode 100644 extensions/github/package.json delete mode 100644 extensions/github/package.nls.json delete mode 100644 extensions/github/src/auth.ts delete mode 100644 extensions/github/src/branchProtection.ts delete mode 100644 extensions/github/src/canonicalUriProvider.ts delete mode 100644 extensions/github/src/commands.ts delete mode 100644 extensions/github/src/credentialProvider.ts delete mode 100644 extensions/github/src/extension.ts delete mode 100644 extensions/github/src/links.ts delete mode 100644 extensions/github/src/publish.ts delete mode 100644 extensions/github/src/pushErrorHandler.ts delete mode 100644 extensions/github/src/remoteSourceProvider.ts delete mode 100644 extensions/github/src/remoteSourcePublisher.ts delete mode 100644 extensions/github/src/shareProviders.ts delete mode 100644 extensions/github/src/test/github.test.ts delete mode 100644 extensions/github/src/test/index.ts delete mode 100644 extensions/github/src/typings/git-base.d.ts delete mode 100644 extensions/github/src/typings/git.d.ts delete mode 100644 extensions/github/src/typings/ref.d.ts delete mode 100644 extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts delete mode 100644 extensions/github/src/typings/vscode.proposed.shareProvider.d.ts delete mode 100644 extensions/github/src/util.ts delete mode 100644 extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE.md delete mode 100644 extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/a.md delete mode 100644 extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/b.md delete mode 100644 extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/x.txt delete mode 100644 extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE.md delete mode 100644 extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/a.md delete mode 100644 extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/b.md delete mode 100644 extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/x.txt delete mode 100644 extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE.md delete mode 100644 extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/a.md delete mode 100644 extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/b.md delete mode 100644 extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/x.txt delete mode 100644 extensions/github/testWorkspace/some-markdown.md delete mode 100644 extensions/github/testWorkspace/x.txt delete mode 100644 extensions/github/tsconfig.json delete mode 100644 extensions/github/yarn.lock delete mode 100644 extensions/microsoft-authentication/.vscodeignore delete mode 100644 extensions/microsoft-authentication/README.md delete mode 100644 extensions/microsoft-authentication/extension-browser.webpack.config.js delete mode 100644 extensions/microsoft-authentication/extension.webpack.config.js delete mode 100644 extensions/microsoft-authentication/media/auth.css delete mode 100644 extensions/microsoft-authentication/media/favicon.ico delete mode 100644 extensions/microsoft-authentication/media/icon.png delete mode 100644 extensions/microsoft-authentication/media/index.html delete mode 100644 extensions/microsoft-authentication/package.json delete mode 100644 extensions/microsoft-authentication/package.nls.json delete mode 100644 extensions/microsoft-authentication/src/AADHelper.ts delete mode 100644 extensions/microsoft-authentication/src/UriEventHandler.ts delete mode 100644 extensions/microsoft-authentication/src/betterSecretStorage.ts delete mode 100644 extensions/microsoft-authentication/src/browser/authServer.ts delete mode 100644 extensions/microsoft-authentication/src/browser/buffer.ts delete mode 100644 extensions/microsoft-authentication/src/browser/crypto.ts delete mode 100644 extensions/microsoft-authentication/src/browser/fetch.ts delete mode 100644 extensions/microsoft-authentication/src/common/async.ts delete mode 100644 extensions/microsoft-authentication/src/common/uri.ts delete mode 100644 extensions/microsoft-authentication/src/cryptoUtils.ts delete mode 100644 extensions/microsoft-authentication/src/extension.ts delete mode 100644 extensions/microsoft-authentication/src/logger.ts delete mode 100644 extensions/microsoft-authentication/src/node/authServer.ts delete mode 100644 extensions/microsoft-authentication/src/node/buffer.ts delete mode 100644 extensions/microsoft-authentication/src/node/crypto.ts delete mode 100644 extensions/microsoft-authentication/src/node/fetch.ts delete mode 100644 extensions/microsoft-authentication/tsconfig.json delete mode 100644 extensions/microsoft-authentication/yarn.lock delete mode 100644 extensions/tunnel-forwarding/.vscode/launch.json delete mode 100644 extensions/tunnel-forwarding/.vscodeignore delete mode 100644 extensions/tunnel-forwarding/extension.webpack.config.js delete mode 100644 extensions/tunnel-forwarding/media/icon.png delete mode 100644 extensions/tunnel-forwarding/package.json delete mode 100644 extensions/tunnel-forwarding/package.nls.json delete mode 100644 extensions/tunnel-forwarding/src/deferredPromise.ts delete mode 100644 extensions/tunnel-forwarding/src/extension.ts delete mode 100644 extensions/tunnel-forwarding/src/split.ts delete mode 100644 extensions/tunnel-forwarding/tsconfig.json delete mode 100644 extensions/tunnel-forwarding/yarn.lock diff --git a/.vscode-test.js b/.vscode-test.js index f2db8d69..069149c6 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -40,11 +40,6 @@ const extensions = [ label: 'configuration-editing', workspaceFolder: path.join(os.tmpdir(), `confeditout-${Math.floor(Math.random() * 100000)}`), mocha: { timeout: 60_000 } - }, - { - label: 'github-authentication', - workspaceFolder: path.join(os.tmpdir(), `msft-auth-${Math.floor(Math.random() * 100000)}`), - mocha: { timeout: 60_000 } } ]; diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index bcdb2066..1bda9778 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -41,8 +41,6 @@ const compilations = [ 'extension-editing/tsconfig.json', 'git/tsconfig.json', 'git-base/tsconfig.json', - 'github-authentication/tsconfig.json', - 'github/tsconfig.json', 'grunt/tsconfig.json', 'gulp/tsconfig.json', 'html-language-features/client/tsconfig.json', @@ -57,14 +55,12 @@ const compilations = [ 'markdown-math/tsconfig.json', 'media-preview/tsconfig.json', 'merge-conflict/tsconfig.json', - 'microsoft-authentication/tsconfig.json', 'notebook-renderers/tsconfig.json', 'npm/tsconfig.json', 'php-language-features/tsconfig.json', 'search-result/tsconfig.json', 'references-view/tsconfig.json', 'simple-browser/tsconfig.json', - 'tunnel-forwarding/tsconfig.json', 'typescript-language-features/test-workspace/tsconfig.json', 'typescript-language-features/web/tsconfig.json', 'typescript-language-features/tsconfig.json', diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index 035653c6..83d953ca 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -320,8 +320,6 @@ const skippedExportMangledProjects = [ // These projects use webpack to dynamically rewrite imports, which messes up our mangling 'configuration-editing', - 'microsoft-authentication', - 'github-authentication', 'html-language-features/server', ]; diff --git a/build/npm/dirs.js b/build/npm/dirs.js index 72cd9820..5ece2f45 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -19,8 +19,6 @@ const dirs = [ 'extensions/extension-editing', 'extensions/git', 'extensions/git-base', - 'extensions/github', - 'extensions/github-authentication', 'extensions/grunt', 'extensions/gulp', 'extensions/html-language-features', @@ -34,14 +32,12 @@ const dirs = [ 'extensions/markdown-math', 'extensions/media-preview', 'extensions/merge-conflict', - 'extensions/microsoft-authentication', 'extensions/notebook-renderers', 'extensions/npm', 'extensions/php-language-features', 'extensions/references-view', 'extensions/search-result', 'extensions/simple-browser', - 'extensions/tunnel-forwarding', 'extensions/typescript-language-features', 'extensions/vscode-api-tests', 'extensions/vscode-colorize-tests', diff --git a/extensions/github-authentication/.gitignore b/extensions/github-authentication/.gitignore deleted file mode 100644 index eab338cd..00000000 --- a/extensions/github-authentication/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src/common/config.json diff --git a/extensions/github-authentication/.vscodeignore b/extensions/github-authentication/.vscodeignore deleted file mode 100644 index f65bc15d..00000000 --- a/extensions/github-authentication/.vscodeignore +++ /dev/null @@ -1,9 +0,0 @@ -.gitignore -src/** -!src/common/config.json -out/** -build/** -extension.webpack.config.js -extension-browser.webpack.config.js -tsconfig.json -yarn.lock diff --git a/extensions/github-authentication/README.md b/extensions/github-authentication/README.md deleted file mode 100644 index 76830c1c..00000000 --- a/extensions/github-authentication/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# GitHub Authentication for Visual Studio Code - -**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. - -## Features - -This extension provides support for authenticating to GitHub. It registers the `github` Authentication Provider that can be leveraged by other extensions. This also provides the GitHub authentication used by Settings Sync. diff --git a/extensions/github-authentication/extension-browser.webpack.config.js b/extensions/github-authentication/extension-browser.webpack.config.js deleted file mode 100644 index f109e203..00000000 --- a/extensions/github-authentication/extension-browser.webpack.config.js +++ /dev/null @@ -1,28 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const path = require('path'); -const withBrowserDefaults = require('../shared.webpack.config').browser; - -module.exports = withBrowserDefaults({ - context: __dirname, - node: false, - entry: { - extension: './src/extension.ts', - }, - resolve: { - alias: { - 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js'), - './node/authServer': path.resolve(__dirname, 'src/browser/authServer'), - './node/crypto': path.resolve(__dirname, 'src/browser/crypto'), - './node/fetch': path.resolve(__dirname, 'src/browser/fetch'), - './node/buffer': path.resolve(__dirname, 'src/browser/buffer'), - } - } -}); diff --git a/extensions/github-authentication/extension.webpack.config.js b/extensions/github-authentication/extension.webpack.config.js deleted file mode 100644 index df3adb4e..00000000 --- a/extensions/github-authentication/extension.webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - extension: './src/extension.ts', - } -}); diff --git a/extensions/github-authentication/images/icon.png b/extensions/github-authentication/images/icon.png deleted file mode 100644 index d20a3fbf98902f270cc842b7afcd97cffa42199a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3658 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 diff --git a/extensions/github-authentication/media/auth.css b/extensions/github-authentication/media/auth.css deleted file mode 100644 index 45c42c75..00000000 --- a/extensions/github-authentication/media/auth.css +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -html { - height: 100%; -} - -body { - box-sizing: border-box; - min-height: 100%; - margin: 0; - padding: 15px 30px; - display: flex; - flex-direction: column; - color: white; - font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; - background-color: #2C2C32; -} - -.branding { - background-image: url('data:image/png;base64,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'); - background-size: 24px; - background-repeat: no-repeat; - background-position: left center; - padding-left: 36px; - font-size: 20px; - letter-spacing: -0.04rem; - font-weight: 400; - color: white; - text-decoration: none; -} - -.message-container { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - margin: 0 30px; -} - -.message { - font-weight: 300; - font-size: 1.4rem; -} - -body.error .message { - display: none; -} - -body.error .error-message { - display: block; -} - -.error-message { - display: none; - font-weight: 300; - font-size: 1.3rem; -} - -.error-text { - color: red; - font-size: 1rem; -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); - font-weight: 200 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); - font-weight: 300 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); - font-weight: 400 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); - font-weight: 600 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); - font-weight: 700 -} diff --git a/extensions/github-authentication/media/favicon.ico b/extensions/github-authentication/media/favicon.ico deleted file mode 100644 index 7d1a59f7bdac3916c4461727ee106d2f2e532663..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34494 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 b`vD(nzu?1mM<)H2_TzNYuX9O1qu>7zZLY>L diff --git a/extensions/github-authentication/media/icon.png b/extensions/github-authentication/media/icon.png deleted file mode 100644 index c179f87a7119eed57e5780296b957325e1a93bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3818 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 B3MBvl diff --git a/extensions/github-authentication/media/index.html b/extensions/github-authentication/media/index.html deleted file mode 100644 index efd092d6..00000000 --- a/extensions/github-authentication/media/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - GitHub Authentication - Sign In - - - - - - - Visual Studio Code - -
-
- You are signed in now and can close this page. -
-
- An error occurred while signing in: -
-
-
- - - - diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json deleted file mode 100644 index dc26d5c0..00000000 --- a/extensions/github-authentication/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "github-authentication", - "displayName": "%displayName%", - "description": "%description%", - "publisher": "vscode", - "license": "MIT", - "version": "0.0.2", - "engines": { - "vscode": "^1.41.0" - }, - "icon": "images/icon.png", - "categories": [ - "Other" - ], - "api": "none", - "extensionKind": [ - "ui", - "workspace" - ], - "activationEvents": [], - "capabilities": { - "virtualWorkspaces": true, - "untrustedWorkspaces": { - "supported": "limited", - "restrictedConfigurations": [ - "github-enterprise.uri" - ] - } - }, - "contributes": { - "authentication": [ - { - "label": "GitHub", - "id": "github" - }, - { - "label": "GitHub Enterprise Server", - "id": "github-enterprise" - } - ], - "configuration": { - "title": "GitHub Enterprise Server Authentication Provider", - "properties": { - "github-enterprise.uri": { - "type": "string", - "description": "GitHub Enterprise Server URI" - } - } - } - }, - "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", - "main": "./out/extension.js", - "browser": "./dist/browser/extension.js", - "scripts": { - "compile": "gulp compile-extension:github-authentication", - "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", - "watch": "gulp watch-extension:github-authentication", - "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose", - "vscode:prepublish": "npm run compile" - }, - "dependencies": { - "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "^0.9.0", - "vscode-tas-client": "^0.1.47" - }, - "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "18.x", - "@types/node-fetch": "^2.5.7" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } -} diff --git a/extensions/github-authentication/package.nls.json b/extensions/github-authentication/package.nls.json deleted file mode 100644 index 592a413b..00000000 --- a/extensions/github-authentication/package.nls.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "displayName": "GitHub Authentication", - "description": "GitHub Authentication Provider" -} diff --git a/extensions/github-authentication/src/browser/authServer.ts b/extensions/github-authentication/src/browser/authServer.ts deleted file mode 100644 index 60b53c71..00000000 --- a/extensions/github-authentication/src/browser/authServer.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function startServer(_: any): any { - throw new Error('Not implemented'); -} - -export function createServer(_: any): any { - throw new Error('Not implemented'); -} diff --git a/extensions/github-authentication/src/browser/buffer.ts b/extensions/github-authentication/src/browser/buffer.ts deleted file mode 100644 index 7192f5f1..00000000 --- a/extensions/github-authentication/src/browser/buffer.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function base64Encode(text: string): string { - return btoa(text); -} diff --git a/extensions/github-authentication/src/browser/crypto.ts b/extensions/github-authentication/src/browser/crypto.ts deleted file mode 100644 index 37a1b433..00000000 --- a/extensions/github-authentication/src/browser/crypto.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const crypto = globalThis.crypto; diff --git a/extensions/github-authentication/src/browser/fetch.ts b/extensions/github-authentication/src/browser/fetch.ts deleted file mode 100644 index f7f69f1a..00000000 --- a/extensions/github-authentication/src/browser/fetch.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const fetching = fetch; diff --git a/extensions/github-authentication/src/common/env.ts b/extensions/github-authentication/src/common/env.ts deleted file mode 100644 index 6af5f137..00000000 --- a/extensions/github-authentication/src/common/env.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { Uri } from 'vscode'; -import { AuthProviderType } from '../github'; - -const VALID_DESKTOP_CALLBACK_SCHEMES = [ - 'vscode', - 'kylin-code', - 'vscode-insiders', - // On Windows, some browsers don't seem to redirect back to OSS properly. - // As a result, you get stuck in the auth flow. We exclude this from the - // list until we can figure out a way to fix this behavior in browsers. - // 'code-oss', - 'vscode-wsl', - 'vscode-exploration' -]; - -export function isSupportedClient(uri: Uri): boolean { - return ( - VALID_DESKTOP_CALLBACK_SCHEMES.includes(uri.scheme) || - // vscode.dev & insiders.vscode.dev - /(?:^|\.)vscode\.dev$/.test(uri.authority) || - // github.dev & codespaces - /(?:^|\.)github\.dev$/.test(uri.authority) - ); -} - -export function isSupportedTarget(type: AuthProviderType, gheUri?: Uri): boolean { - return ( - type === AuthProviderType.github || - isHostedGitHubEnterprise(gheUri!) - ); -} - -export function isHostedGitHubEnterprise(uri: Uri): boolean { - return /\.ghe\.com$/.test(uri.authority); -} diff --git a/extensions/github-authentication/src/common/errors.ts b/extensions/github-authentication/src/common/errors.ts deleted file mode 100644 index 3ba3dfc0..00000000 --- a/extensions/github-authentication/src/common/errors.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const TIMED_OUT_ERROR = 'Timed out'; - -// These error messages are internal and should not be shown to the user in any way. -export const USER_CANCELLATION_ERROR = 'User Cancelled'; -export const NETWORK_ERROR = 'network error'; diff --git a/extensions/github-authentication/src/common/experimentationService.ts b/extensions/github-authentication/src/common/experimentationService.ts deleted file mode 100644 index 170742d5..00000000 --- a/extensions/github-authentication/src/common/experimentationService.ts +++ /dev/null @@ -1,97 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { getExperimentationService, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from 'vscode-tas-client'; - -export class ExperimentationTelemetry implements IExperimentationTelemetry { - private sharedProperties: Record = {}; - private experimentationServicePromise: Promise | undefined; - - constructor(private readonly context: vscode.ExtensionContext, private baseReporter: TelemetryReporter) { } - - private async createExperimentationService(): Promise { - let targetPopulation: TargetPopulation; - switch (vscode.env.uriScheme) { - case 'vscode': - case 'kylin-code': - targetPopulation = TargetPopulation.Public; - break; - case 'vscode-insiders': - targetPopulation = TargetPopulation.Insiders; - break; - case 'vscode-exploration': - targetPopulation = TargetPopulation.Internal; - break; - case 'code-oss': - targetPopulation = TargetPopulation.Team; - break; - default: - targetPopulation = TargetPopulation.Public; - break; - } - - const id = this.context.extension.id; - const version = this.context.extension.packageJSON.version; - const experimentationService = getExperimentationService(id, version, targetPopulation, this, this.context.globalState); - await experimentationService.initialFetch; - return experimentationService; - } - - /** - * @returns A promise that you shouldn't need to await because this is just telemetry. - */ - async sendTelemetryEvent(eventName: string, properties?: Record, measurements?: Record) { - if (!this.experimentationServicePromise) { - this.experimentationServicePromise = this.createExperimentationService(); - } - await this.experimentationServicePromise; - - this.baseReporter.sendTelemetryEvent( - eventName, - { - ...this.sharedProperties, - ...properties, - }, - measurements, - ); - } - - /** - * @returns A promise that you shouldn't need to await because this is just telemetry. - */ - async sendTelemetryErrorEvent( - eventName: string, - properties?: Record, - _measurements?: Record - ) { - if (!this.experimentationServicePromise) { - this.experimentationServicePromise = this.createExperimentationService(); - } - await this.experimentationServicePromise; - - this.baseReporter.sendTelemetryErrorEvent(eventName, { - ...this.sharedProperties, - ...properties, - }); - } - - setSharedProperty(name: string, value: string): void { - this.sharedProperties[name] = value; - } - - postEvent(eventName: string, props: Map): void { - const event: Record = {}; - for (const [key, value] of props) { - event[key] = value; - } - this.sendTelemetryEvent(eventName, event); - } - - dispose(): Promise { - return this.baseReporter.dispose(); - } -} diff --git a/extensions/github-authentication/src/common/keychain.ts b/extensions/github-authentication/src/common/keychain.ts deleted file mode 100644 index 977dade5..00000000 --- a/extensions/github-authentication/src/common/keychain.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Log } from './logger'; - -export class Keychain { - constructor( - private readonly context: vscode.ExtensionContext, - private readonly serviceId: string, - private readonly Logger: Log - ) { } - - async setToken(token: string): Promise { - try { - return await this.context.secrets.store(this.serviceId, token); - } catch (e) { - // Ignore - this.Logger.error(`Setting token failed: ${e}`); - } - } - - async getToken(): Promise { - try { - const secret = await this.context.secrets.get(this.serviceId); - if (secret && secret !== '[]') { - this.Logger.trace('Token acquired from secret storage.'); - } - return secret; - } catch (e) { - // Ignore - this.Logger.error(`Getting token failed: ${e}`); - return Promise.resolve(undefined); - } - } - - async deleteToken(): Promise { - try { - return await this.context.secrets.delete(this.serviceId); - } catch (e) { - // Ignore - this.Logger.error(`Deleting token failed: ${e}`); - return Promise.resolve(undefined); - } - } -} diff --git a/extensions/github-authentication/src/common/logger.ts b/extensions/github-authentication/src/common/logger.ts deleted file mode 100644 index cf90c417..00000000 --- a/extensions/github-authentication/src/common/logger.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { AuthProviderType } from '../github'; - -export class Log { - private output: vscode.LogOutputChannel; - - constructor(private readonly type: AuthProviderType) { - const friendlyName = this.type === AuthProviderType.github ? 'GitHub' : 'GitHub Enterprise'; - this.output = vscode.window.createOutputChannel(`${friendlyName} Authentication`, { log: true }); - } - - public trace(message: string): void { - this.output.trace(message); - } - - public info(message: string): void { - this.output.info(message); - } - - public error(message: string): void { - this.output.error(message); - } - - public warn(message: string): void { - this.output.warn(message); - } -} diff --git a/extensions/github-authentication/src/common/utils.ts b/extensions/github-authentication/src/common/utils.ts deleted file mode 100644 index b3da59cd..00000000 --- a/extensions/github-authentication/src/common/utils.ts +++ /dev/null @@ -1,118 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { EventEmitter, Event, Disposable } from 'vscode'; - -export function filterEvent(event: Event, filter: (e: T) => boolean): Event { - return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); -} - -export function onceEvent(event: Event): Event { - return (listener, thisArgs = null, disposables?) => { - const result = event(e => { - result.dispose(); - return listener.call(thisArgs, e); - }, null, disposables); - - return result; - }; -} - - -export interface PromiseAdapter { - ( - value: T, - resolve: - (value: U | PromiseLike) => void, - reject: - (reason: any) => void - ): any; -} - -const passthrough = (value: any, resolve: (value?: any) => void) => resolve(value); - -/** - * Return a promise that resolves with the next emitted event, or with some future - * event as decided by an adapter. - * - * If specified, the adapter is a function that will be called with - * `(event, resolve, reject)`. It will be called once per event until it resolves or - * rejects. - * - * The default adapter is the passthrough function `(value, resolve) => resolve(value)`. - * - * @param event the event - * @param adapter controls resolution of the returned promise - * @returns a promise that resolves or rejects as specified by the adapter - */ -export function promiseFromEvent( - event: Event, - adapter: PromiseAdapter = passthrough): { promise: Promise; cancel: EventEmitter } { - let subscription: Disposable; - const cancel = new EventEmitter(); - return { - promise: new Promise((resolve, reject) => { - cancel.event(_ => reject('Cancelled')); - subscription = event((value: T) => { - try { - Promise.resolve(adapter(value, resolve, reject)) - .catch(reject); - } catch (error) { - reject(error); - } - }); - }).then( - (result: U) => { - subscription.dispose(); - return result; - }, - error => { - subscription.dispose(); - throw error; - } - ), - cancel - }; -} - -export function arrayEquals(one: ReadonlyArray | undefined, other: ReadonlyArray | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { - if (one === other) { - return true; - } - - if (!one || !other) { - return false; - } - - if (one.length !== other.length) { - return false; - } - - for (let i = 0, len = one.length; i < len; i++) { - if (!itemEquals(one[i], other[i])) { - return false; - } - } - - return true; -} - - -export class StopWatch { - - private _startTime: number = Date.now(); - private _stopTime: number = -1; - - public stop(): void { - this._stopTime = Date.now(); - } - - public elapsed(): number { - if (this._stopTime !== -1) { - return this._stopTime - this._startTime; - } - return Date.now() - this._startTime; - } -} diff --git a/extensions/github-authentication/src/config.ts b/extensions/github-authentication/src/config.ts deleted file mode 100644 index 30b9dd66..00000000 --- a/extensions/github-authentication/src/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface IConfig { - // The client ID of the GitHub OAuth app - gitHubClientId: string; - gitHubClientSecret?: string; -} - -// For easy access to mixin client ID and secret -export const Config: IConfig = { - gitHubClientId: '01ab8ac9400c4e429b23' -}; diff --git a/extensions/github-authentication/src/extension.ts b/extensions/github-authentication/src/extension.ts deleted file mode 100644 index 62f69f85..00000000 --- a/extensions/github-authentication/src/extension.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { GitHubAuthenticationProvider, UriEventHandler } from './github'; - -function initGHES(context: vscode.ExtensionContext, uriHandler: UriEventHandler) { - const settingValue = vscode.workspace.getConfiguration().get('github-enterprise.uri'); - if (!settingValue) { - return undefined; - } - - // validate user value - let uri: vscode.Uri; - try { - uri = vscode.Uri.parse(settingValue, true); - } catch (e) { - vscode.window.showErrorMessage(vscode.l10n.t('GitHub Enterprise Server URI is not a valid URI: {0}', e.message ?? e)); - return; - } - - const githubEnterpriseAuthProvider = new GitHubAuthenticationProvider(context, uriHandler, uri); - context.subscriptions.push(githubEnterpriseAuthProvider); - return githubEnterpriseAuthProvider; -} - -export function activate(context: vscode.ExtensionContext) { - const uriHandler = new UriEventHandler(); - context.subscriptions.push(uriHandler); - context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); - - context.subscriptions.push(new GitHubAuthenticationProvider(context, uriHandler)); - - let githubEnterpriseAuthProvider: GitHubAuthenticationProvider | undefined = initGHES(context, uriHandler); - - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(async e => { - if (e.affectsConfiguration('github-enterprise.uri')) { - if (vscode.workspace.getConfiguration().get('github-enterprise.uri')) { - githubEnterpriseAuthProvider?.dispose(); - githubEnterpriseAuthProvider = initGHES(context, uriHandler); - } - } - })); -} diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts deleted file mode 100644 index 3641ffb3..00000000 --- a/extensions/github-authentication/src/flows.ts +++ /dev/null @@ -1,502 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import { ProgressLocation, Uri, commands, env, l10n, window } from 'vscode'; -import { Log } from './common/logger'; -import { Config } from './config'; -import { UriEventHandler } from './github'; -import { fetching } from './node/fetch'; -import { LoopbackAuthServer } from './node/authServer'; -import { promiseFromEvent } from './common/utils'; -import { isHostedGitHubEnterprise } from './common/env'; -import { NETWORK_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; - -interface IGitHubDeviceCodeResponse { - device_code: string; - user_code: string; - verification_uri: string; - interval: number; -} - -interface IFlowOptions { - // GitHub.com - readonly supportsGitHubDotCom: boolean; - // A GitHub Enterprise Server that is hosted by an organization - readonly supportsGitHubEnterpriseServer: boolean; - // A GitHub Enterprise Server that is hosted by GitHub for an organization - readonly supportsHostedGitHubEnterprise: boolean; - - // Runtimes - there are constraints on which runtimes support which flows - readonly supportsWebWorkerExtensionHost: boolean; - readonly supportsRemoteExtensionHost: boolean; - - // Clients - see `isSupportedClient` in `common/env.ts` for what constitutes a supported client - readonly supportsSupportedClients: boolean; - readonly supportsUnsupportedClients: boolean; - - // Configurations - some flows require a client secret - readonly supportsNoClientSecret: boolean; -} - -export const enum GitHubTarget { - DotCom, - Enterprise, - HostedEnterprise -} - -export const enum ExtensionHost { - WebWorker, - Remote, - Local -} - -export interface IFlowQuery { - target: GitHubTarget; - extensionHost: ExtensionHost; - isSupportedClient: boolean; -} - -interface IFlowTriggerOptions { - scopes: string; - baseUri: Uri; - logger: Log; - redirectUri: Uri; - nonce: string; - callbackUri: Uri; - uriHandler: UriEventHandler; - enterpriseUri?: Uri; -} - -interface IFlow { - label: string; - options: IFlowOptions; - trigger(options: IFlowTriggerOptions): Promise; -} - -async function exchangeCodeForToken( - logger: Log, - endpointUri: Uri, - redirectUri: Uri, - code: string, - enterpriseUri?: Uri -): Promise { - logger.info('Exchanging code for token...'); - - const clientSecret = Config.gitHubClientSecret; - if (!clientSecret) { - throw new Error('No client secret configured for GitHub authentication.'); - } - - const body = new URLSearchParams([ - ['code', code], - ['client_id', Config.gitHubClientId], - ['redirect_uri', redirectUri.toString(true)], - ['client_secret', clientSecret] - ]); - if (enterpriseUri) { - body.append('github_enterprise', enterpriseUri.toString(true)); - } - const result = await fetching(endpointUri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': body.toString() - - }, - body: body.toString() - }); - - if (result.ok) { - const json = await result.json(); - logger.info('Token exchange success!'); - return json.access_token; - } else { - const text = await result.text(); - const error = new Error(text); - error.name = 'GitHubTokenExchangeError'; - throw error; - } -} - -const allFlows: IFlow[] = [ - new class UrlHandlerFlow implements IFlow { - label = l10n.t('url handler'); - options: IFlowOptions = { - supportsGitHubDotCom: true, - // Supporting GHES would be challenging because different versions - // used a different client ID. We could try to detect the version - // and use the right one, but that's a lot of work when we have - // other flows that work well. - supportsGitHubEnterpriseServer: false, - supportsHostedGitHubEnterprise: true, - supportsRemoteExtensionHost: true, - supportsWebWorkerExtensionHost: true, - // exchanging a code for a token requires a client secret - supportsNoClientSecret: false, - supportsSupportedClients: true, - supportsUnsupportedClients: false - }; - - async trigger({ - scopes, - baseUri, - redirectUri, - logger, - nonce, - callbackUri, - uriHandler, - enterpriseUri - }: IFlowTriggerOptions): Promise { - logger.info(`Trying without local server... (${scopes})`); - return await window.withProgress({ - location: ProgressLocation.Notification, - title: l10n.t({ - message: 'Signing in to {0}...', - args: [baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const promise = uriHandler.waitForCode(logger, scopes, nonce, token); - - const searchParams = new URLSearchParams([ - ['client_id', Config.gitHubClientId], - ['redirect_uri', redirectUri.toString(true)], - ['scope', scopes], - ['state', encodeURIComponent(callbackUri.toString(true))] - ]); - - // The extra toString, parse is apparently needed for env.openExternal - // to open the correct URL. - const uri = Uri.parse(baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }).toString(true)); - await env.openExternal(uri); - - const code = await promise; - - const proxyEndpoints: { [providerId: string]: string } | undefined = await commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - const endpointUrl = proxyEndpoints?.github - ? Uri.parse(`${proxyEndpoints.github}login/oauth/access_token`) - : baseUri.with({ path: '/login/oauth/access_token' }); - - const accessToken = await exchangeCodeForToken(logger, endpointUrl, redirectUri, code, enterpriseUri); - return accessToken; - }); - } - }, - new class LocalServerFlow implements IFlow { - label = l10n.t('local server'); - options: IFlowOptions = { - supportsGitHubDotCom: true, - // Supporting GHES would be challenging because different versions - // used a different client ID. We could try to detect the version - // and use the right one, but that's a lot of work when we have - // other flows that work well. - supportsGitHubEnterpriseServer: false, - supportsHostedGitHubEnterprise: true, - // Opening a port on the remote side can't be open in the browser on - // the client side so this flow won't work in remote extension hosts - supportsRemoteExtensionHost: false, - // Web worker can't open a port to listen for the redirect - supportsWebWorkerExtensionHost: false, - // exchanging a code for a token requires a client secret - supportsNoClientSecret: false, - supportsSupportedClients: true, - supportsUnsupportedClients: true - }; - async trigger({ - scopes, - baseUri, - redirectUri, - logger, - enterpriseUri - }: IFlowTriggerOptions): Promise { - logger.info(`Trying with local server... (${scopes})`); - return await window.withProgress({ - location: ProgressLocation.Notification, - title: l10n.t({ - message: 'Signing in to {0}...', - args: [baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const searchParams = new URLSearchParams([ - ['client_id', Config.gitHubClientId], - ['redirect_uri', redirectUri.toString(true)], - ['scope', scopes], - ]); - - const loginUrl = baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }); - const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true)); - const port = await server.start(); - - let codeToExchange; - try { - env.openExternal(Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); - const { code } = await Promise.race([ - server.waitForOAuthResponse(), - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - codeToExchange = code; - } finally { - setTimeout(() => { - void server.stop(); - }, 5000); - } - - const accessToken = await exchangeCodeForToken( - logger, - baseUri.with({ path: '/login/oauth/access_token' }), - redirectUri, - codeToExchange, - enterpriseUri); - return accessToken; - }); - } - }, - new class DeviceCodeFlow implements IFlow { - label = l10n.t('device code'); - options: IFlowOptions = { - supportsGitHubDotCom: true, - supportsGitHubEnterpriseServer: true, - supportsHostedGitHubEnterprise: true, - supportsRemoteExtensionHost: true, - // CORS prevents this from working in web workers - supportsWebWorkerExtensionHost: false, - supportsNoClientSecret: true, - supportsSupportedClients: true, - supportsUnsupportedClients: true - }; - async trigger({ scopes, baseUri, logger }: IFlowTriggerOptions) { - logger.info(`Trying device code flow... (${scopes})`); - - // Get initial device code - const uri = baseUri.with({ - path: '/login/device/code', - query: `client_id=${Config.gitHubClientId}&scope=${scopes}` - }); - const result = await fetching(uri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - if (!result.ok) { - throw new Error(`Failed to get one-time code: ${await result.text()}`); - } - - const json = await result.json() as IGitHubDeviceCodeResponse; - - const button = l10n.t('Copy & Continue to GitHub'); - const modalResult = await window.showInformationMessage( - l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), - { - modal: true, - detail: l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - await env.clipboard.writeText(json.user_code); - - const uriToOpen = await env.asExternalUri(Uri.parse(json.verification_uri)); - await env.openExternal(uriToOpen); - - return await this.waitForDeviceCodeAccessToken(baseUri, json); - } - - private async waitForDeviceCodeAccessToken( - baseUri: Uri, - json: IGitHubDeviceCodeResponse, - ): Promise { - return await window.withProgress({ - location: ProgressLocation.Notification, - cancellable: true, - title: l10n.t({ - message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', - args: [json.verification_uri, json.user_code], - comment: [ - 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', - '{Locked="[{0}]({0})"}' - ] - }) - }, async (_, token) => { - const refreshTokenUri = baseUri.with({ - path: '/login/oauth/access_token', - query: `client_id=${Config.gitHubClientId}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code` - }); - - // Try for 2 minutes - const attempts = 120 / json.interval; - for (let i = 0; i < attempts; i++) { - await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); - if (token.isCancellationRequested) { - throw new Error(USER_CANCELLATION_ERROR); - } - let accessTokenResult; - try { - accessTokenResult = await fetching(refreshTokenUri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - } catch { - continue; - } - - if (!accessTokenResult.ok) { - continue; - } - - const accessTokenJson = await accessTokenResult.json(); - - if (accessTokenJson.error === 'authorization_pending') { - continue; - } - - if (accessTokenJson.error) { - throw new Error(accessTokenJson.error_description); - } - - return accessTokenJson.access_token; - } - - throw new Error(TIMED_OUT_ERROR); - }); - } - }, - new class PatFlow implements IFlow { - label = l10n.t('personal access token'); - options: IFlowOptions = { - supportsGitHubDotCom: true, - supportsGitHubEnterpriseServer: true, - supportsHostedGitHubEnterprise: true, - supportsRemoteExtensionHost: true, - supportsWebWorkerExtensionHost: true, - supportsNoClientSecret: true, - // PATs can't be used with Settings Sync so we don't enable this flow - // for supported clients - supportsSupportedClients: false, - supportsUnsupportedClients: true - }; - - async trigger({ scopes, baseUri, logger, enterpriseUri }: IFlowTriggerOptions) { - logger.info(`Trying to retrieve PAT... (${scopes})`); - - const button = l10n.t('Continue to GitHub'); - const modalResult = await window.showInformationMessage( - l10n.t('Continue to GitHub to create a Personal Access Token (PAT)'), - { - modal: true, - detail: l10n.t('To finish authenticating, navigate to GitHub to create a PAT then paste the PAT into the input box.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - const description = `${env.appName} (${scopes})`; - const uriToOpen = await env.asExternalUri(baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` })); - await env.openExternal(uriToOpen); - const token = await window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true }); - if (!token) { throw new Error(USER_CANCELLATION_ERROR); } - - const appUri = !enterpriseUri || isHostedGitHubEnterprise(enterpriseUri) - ? Uri.parse(`${baseUri.scheme}://api.${baseUri.authority}`) - : Uri.parse(`${baseUri.scheme}://${baseUri.authority}/api/v3`); - - const tokenScopes = await this.getScopes(token, appUri, logger); // Example: ['repo', 'user'] - const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' - if (!scopesList.every(scope => { - const included = tokenScopes.includes(scope); - if (included || !scope.includes(':')) { - return included; - } - - return scope.split(':').some(splitScopes => { - return tokenScopes.includes(splitScopes); - }); - })) { - throw new Error(`The provided token does not match the requested scopes: ${scopes}`); - } - - return token; - } - - private async getScopes(token: string, serverUri: Uri, logger: Log): Promise { - try { - logger.info('Getting token scopes...'); - const result = await fetching(serverUri.toString(), { - headers: { - Authorization: `token ${token}`, - 'User-Agent': `${env.appName} (${env.appHost})` - } - }); - - if (result.ok) { - const scopes = result.headers.get('X-OAuth-Scopes'); - return scopes ? scopes.split(',').map(scope => scope.trim()) : []; - } else { - logger.error(`Getting scopes failed: ${result.statusText}`); - throw new Error(result.statusText); - } - } catch (ex) { - logger.error(ex.message); - throw new Error(NETWORK_ERROR); - } - } - } -]; - -export function getFlows(query: IFlowQuery) { - return allFlows.filter(flow => { - let useFlow: boolean = true; - switch (query.target) { - case GitHubTarget.DotCom: - useFlow &&= flow.options.supportsGitHubDotCom; - break; - case GitHubTarget.Enterprise: - useFlow &&= flow.options.supportsGitHubEnterpriseServer; - break; - case GitHubTarget.HostedEnterprise: - useFlow &&= flow.options.supportsHostedGitHubEnterprise; - break; - } - - switch (query.extensionHost) { - case ExtensionHost.Remote: - useFlow &&= flow.options.supportsRemoteExtensionHost; - break; - case ExtensionHost.WebWorker: - useFlow &&= flow.options.supportsWebWorkerExtensionHost; - break; - } - - if (!Config.gitHubClientSecret) { - useFlow &&= flow.options.supportsNoClientSecret; - } - - if (query.isSupportedClient) { - // TODO: revisit how we support PAT in GHES but not DotCom... but this works for now since - // there isn't another flow that has supportsSupportedClients = false - useFlow &&= (flow.options.supportsSupportedClients || query.target !== GitHubTarget.DotCom); - } else { - useFlow &&= flow.options.supportsUnsupportedClients; - } - return useFlow; - }); -} diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts deleted file mode 100644 index 71aa17bd..00000000 --- a/extensions/github-authentication/src/github.ts +++ /dev/null @@ -1,383 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { Keychain } from './common/keychain'; -import { GitHubServer, IGitHubServer } from './githubServer'; -import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils'; -import { ExperimentationTelemetry } from './common/experimentationService'; -import { Log } from './common/logger'; -import { crypto } from './node/crypto'; -import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; - -interface SessionData { - id: string; - account?: { - label?: string; - displayName?: string; - id: string; - }; - scopes: string[]; - accessToken: string; -} - -export enum AuthProviderType { - github = 'github', - githubEnterprise = 'github-enterprise' -} - -export class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { - private readonly _pendingNonces = new Map(); - private readonly _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); - - public handleUri(uri: vscode.Uri) { - this.fire(uri); - } - - public async waitForCode(logger: Log, scopes: string, nonce: string, token: vscode.CancellationToken) { - const existingNonces = this._pendingNonces.get(scopes) || []; - this._pendingNonces.set(scopes, [...existingNonces, nonce]); - - let codeExchangePromise = this._codeExchangePromises.get(scopes); - if (!codeExchangePromise) { - codeExchangePromise = promiseFromEvent(this.event, this.handleEvent(logger, scopes)); - this._codeExchangePromises.set(scopes, codeExchangePromise); - } - - try { - return await Promise.race([ - codeExchangePromise.promise, - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - } finally { - this._pendingNonces.delete(scopes); - codeExchangePromise?.cancel.fire(); - this._codeExchangePromises.delete(scopes); - } - } - - private handleEvent: (logger: Log, scopes: string) => PromiseAdapter = - (logger: Log, scopes) => (uri, resolve, reject) => { - const query = new URLSearchParams(uri.query); - const code = query.get('code'); - const nonce = query.get('nonce'); - if (!code) { - reject(new Error('No code')); - return; - } - if (!nonce) { - reject(new Error('No nonce')); - return; - } - - const acceptedNonces = this._pendingNonces.get(scopes) || []; - if (!acceptedNonces.includes(nonce)) { - // A common scenario of this happening is if you: - // 1. Trigger a sign in with one set of scopes - // 2. Before finishing 1, you trigger a sign in with a different set of scopes - // In this scenario we should just return and wait for the next UriHandler event - // to run as we are probably still waiting on the user to hit 'Continue' - logger.info('Nonce not found in accepted nonces. Skipping this execution...'); - return; - } - - resolve(code); - }; -} - -export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable { - private readonly _sessionChangeEmitter = new vscode.EventEmitter(); - private readonly _logger: Log; - private readonly _githubServer: IGitHubServer; - private readonly _telemetryReporter: ExperimentationTelemetry; - private readonly _keychain: Keychain; - private readonly _accountsSeen = new Set(); - private readonly _disposable: vscode.Disposable | undefined; - - private _sessionsPromise: Promise; - - constructor( - private readonly context: vscode.ExtensionContext, - uriHandler: UriEventHandler, - ghesUri?: vscode.Uri - ) { - const { aiKey } = context.extension.packageJSON as { name: string; version: string; aiKey: string }; - this._telemetryReporter = new ExperimentationTelemetry(context, new TelemetryReporter(aiKey)); - - const type = ghesUri ? AuthProviderType.githubEnterprise : AuthProviderType.github; - - this._logger = new Log(type); - - this._keychain = new Keychain( - this.context, - type === AuthProviderType.github - ? `${type}.auth` - : `${ghesUri?.authority}${ghesUri?.path}.ghes.auth`, - this._logger); - - this._githubServer = new GitHubServer( - this._logger, - this._telemetryReporter, - uriHandler, - context.extension.extensionKind, - ghesUri); - - // Contains the current state of the sessions we have available. - this._sessionsPromise = this.readSessions().then((sessions) => { - // fire telemetry after a second to allow the workbench to focus on loading - setTimeout(() => sessions.forEach(s => this.afterSessionLoad(s)), 1000); - return sessions; - }); - - this._disposable = vscode.Disposable.from( - this._telemetryReporter, - vscode.authentication.registerAuthenticationProvider(type, this._githubServer.friendlyName, this, { supportsMultipleAccounts: false }), - this.context.secrets.onDidChange(() => this.checkForUpdates()) - ); - } - - dispose() { - this._disposable?.dispose(); - } - - get onDidChangeSessions() { - return this._sessionChangeEmitter.event; - } - - async getSessions(scopes?: string[]): Promise { - // For GitHub scope list, order doesn't matter so we immediately sort the scopes - const sortedScopes = scopes?.sort() || []; - this._logger.info(`Getting sessions for ${sortedScopes.length ? sortedScopes.join(',') : 'all scopes'}...`); - const sessions = await this._sessionsPromise; - const finalSessions = sortedScopes.length - ? sessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) - : sessions; - - this._logger.info(`Got ${finalSessions.length} sessions for ${sortedScopes?.join(',') ?? 'all scopes'}...`); - return finalSessions; - } - - private async afterSessionLoad(session: vscode.AuthenticationSession): Promise { - // We only want to fire a telemetry if we haven't seen this account yet in this session. - if (!this._accountsSeen.has(session.account.id)) { - this._accountsSeen.add(session.account.id); - this._githubServer.sendAdditionalTelemetryInfo(session); - } - } - - private async checkForUpdates() { - const previousSessions = await this._sessionsPromise; - this._sessionsPromise = this.readSessions(); - const storedSessions = await this._sessionsPromise; - - const added: vscode.AuthenticationSession[] = []; - const removed: vscode.AuthenticationSession[] = []; - - storedSessions.forEach(session => { - const matchesExisting = previousSessions.some(s => s.id === session.id); - // Another window added a session to the keychain, add it to our state as well - if (!matchesExisting) { - this._logger.info('Adding session found in keychain'); - added.push(session); - } - }); - - previousSessions.forEach(session => { - const matchesExisting = storedSessions.some(s => s.id === session.id); - // Another window has logged out, remove from our state - if (!matchesExisting) { - this._logger.info('Removing session no longer found in keychain'); - removed.push(session); - } - }); - - if (added.length || removed.length) { - this._sessionChangeEmitter.fire({ added, removed, changed: [] }); - } - } - - private async readSessions(): Promise { - let sessionData: SessionData[]; - try { - this._logger.info('Reading sessions from keychain...'); - const storedSessions = await this._keychain.getToken(); - if (!storedSessions) { - return []; - } - this._logger.info('Got stored sessions!'); - - try { - sessionData = JSON.parse(storedSessions); - } catch (e) { - await this._keychain.deleteToken(); - throw e; - } - } catch (e) { - this._logger.error(`Error reading token: ${e}`); - return []; - } - - // TODO: eventually remove this Set because we should only have one session per set of scopes. - const scopesSeen = new Set(); - const sessionPromises = sessionData.map(async (session: SessionData) => { - // For GitHub scope list, order doesn't matter so we immediately sort the scopes - const scopesStr = [...session.scopes].sort().join(' '); - if (scopesSeen.has(scopesStr)) { - return undefined; - } - let userInfo: { id: string; accountName: string } | undefined; - if (!session.account) { - try { - userInfo = await this._githubServer.getUserInfo(session.accessToken); - this._logger.info(`Verified session with the following scopes: ${scopesStr}`); - } catch (e) { - // Remove sessions that return unauthorized response - if (e.message === 'Unauthorized') { - return undefined; - } - } - } - - this._logger.trace(`Read the following session from the keychain with the following scopes: ${scopesStr}`); - scopesSeen.add(scopesStr); - return { - id: session.id, - account: { - label: session.account - ? session.account.label ?? session.account.displayName ?? '' - : userInfo?.accountName ?? '', - id: session.account?.id ?? userInfo?.id ?? '' - }, - // we set this to session.scopes to maintain the original order of the scopes requested - // by the extension that called getSession() - scopes: session.scopes, - accessToken: session.accessToken - }; - }); - - const verifiedSessions = (await Promise.allSettled(sessionPromises)) - .filter(p => p.status === 'fulfilled') - .map(p => (p as PromiseFulfilledResult).value) - .filter((p?: T): p is T => Boolean(p)); - - this._logger.info(`Got ${verifiedSessions.length} verified sessions.`); - if (verifiedSessions.length !== sessionData.length) { - await this.storeSessions(verifiedSessions); - } - - return verifiedSessions; - } - - private async storeSessions(sessions: vscode.AuthenticationSession[]): Promise { - this._logger.info(`Storing ${sessions.length} sessions...`); - this._sessionsPromise = Promise.resolve(sessions); - await this._keychain.setToken(JSON.stringify(sessions)); - this._logger.info(`Stored ${sessions.length} sessions!`); - } - - public async createSession(scopes: string[]): Promise { - try { - // For GitHub scope list, order doesn't matter so we use a sorted scope to determine - // if we've got a session already. - const sortedScopes = [...scopes].sort(); - - /* __GDPR__ - "login" : { - "owner": "TylerLeonhardt", - "comment": "Used to determine how much usage the GitHub Auth Provider gets.", - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." } - } - */ - this._telemetryReporter?.sendTelemetryEvent('login', { - scopes: JSON.stringify(scopes), - }); - - - const scopeString = sortedScopes.join(' '); - const token = await this._githubServer.login(scopeString); - const session = await this.tokenToSession(token, scopes); - this.afterSessionLoad(session); - - const sessions = await this._sessionsPromise; - const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes)); - if (sessionIndex > -1) { - sessions.splice(sessionIndex, 1, session); - } else { - sessions.push(session); - } - await this.storeSessions(sessions); - - this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); - - this._logger.info('Login success!'); - - return session; - } catch (e) { - // If login was cancelled, do not notify user. - if (e === 'Cancelled' || e.message === 'Cancelled') { - /* __GDPR__ - "loginCancelled" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users cancel the login flow." } - */ - this._telemetryReporter?.sendTelemetryEvent('loginCancelled'); - throw e; - } - - /* __GDPR__ - "loginFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users run into an error login flow." } - */ - this._telemetryReporter?.sendTelemetryEvent('loginFailed'); - - vscode.window.showErrorMessage(vscode.l10n.t('Sign in failed: {0}', `${e}`)); - this._logger.error(e); - throw e; - } - } - - private async tokenToSession(token: string, scopes: string[]): Promise { - const userInfo = await this._githubServer.getUserInfo(token); - return { - id: crypto.getRandomValues(new Uint32Array(2)).reduce((prev, curr) => prev += curr.toString(16), ''), - accessToken: token, - account: { label: userInfo.accountName, id: userInfo.id }, - scopes - }; - } - - public async removeSession(id: string) { - try { - /* __GDPR__ - "logout" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users log out of an account." } - */ - this._telemetryReporter?.sendTelemetryEvent('logout'); - - this._logger.info(`Logging out of ${id}`); - - const sessions = await this._sessionsPromise; - const sessionIndex = sessions.findIndex(session => session.id === id); - if (sessionIndex > -1) { - const session = sessions[sessionIndex]; - sessions.splice(sessionIndex, 1); - - await this.storeSessions(sessions); - await this._githubServer.logout(session); - - this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] }); - } else { - this._logger.error('Session not found'); - } - } catch (e) { - /* __GDPR__ - "logoutFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often logging out of an account fails." } - */ - this._telemetryReporter?.sendTelemetryEvent('logoutFailed'); - - vscode.window.showErrorMessage(vscode.l10n.t('Sign out failed: {0}', `${e}`)); - this._logger.error(e); - throw e; - } - } -} diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts deleted file mode 100644 index 0729c4c5..00000000 --- a/extensions/github-authentication/src/githubServer.ts +++ /dev/null @@ -1,357 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { ExperimentationTelemetry } from './common/experimentationService'; -import { AuthProviderType, UriEventHandler } from './github'; -import { Log } from './common/logger'; -import { isSupportedClient, isSupportedTarget } from './common/env'; -import { crypto } from './node/crypto'; -import { fetching } from './node/fetch'; -import { ExtensionHost, GitHubTarget, getFlows } from './flows'; -import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; -import { Config } from './config'; -import { base64Encode } from './node/buffer'; - -// This is the error message that we throw if the login was cancelled for any reason. Extensions -// calling `getSession` can handle this error to know that the user cancelled the login. -const CANCELLATION_ERROR = 'Cancelled'; - -const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect'; -const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect'; - -export interface IGitHubServer { - login(scopes: string): Promise; - logout(session: vscode.AuthenticationSession): Promise; - getUserInfo(token: string): Promise<{ id: string; accountName: string }>; - sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise; - friendlyName: string; -} - - -export class GitHubServer implements IGitHubServer { - readonly friendlyName: string; - - private readonly _type: AuthProviderType; - - private _redirectEndpoint: string | undefined; - - constructor( - private readonly _logger: Log, - private readonly _telemetryReporter: ExperimentationTelemetry, - private readonly _uriHandler: UriEventHandler, - private readonly _extensionKind: vscode.ExtensionKind, - private readonly _ghesUri?: vscode.Uri - ) { - this._type = _ghesUri ? AuthProviderType.githubEnterprise : AuthProviderType.github; - this.friendlyName = this._type === AuthProviderType.github ? 'GitHub' : _ghesUri?.authority!; - } - - get baseUri() { - if (this._type === AuthProviderType.github) { - return vscode.Uri.parse('https://github.com/'); - } - return this._ghesUri!; - } - - private async getRedirectEndpoint(): Promise { - if (this._redirectEndpoint) { - return this._redirectEndpoint; - } - if (this._type === AuthProviderType.github) { - const proxyEndpoints = await vscode.commands.executeCommand<{ [providerId: string]: string } | undefined>('workbench.getCodeExchangeProxyEndpoints'); - // If we are running in insiders vscode.dev, then ensure we use the redirect route on that. - this._redirectEndpoint = REDIRECT_URL_STABLE; - if (proxyEndpoints?.github && new URL(proxyEndpoints.github).hostname === 'insiders.vscode.dev') { - this._redirectEndpoint = REDIRECT_URL_INSIDERS; - } - } else { - // GHE only supports a single redirect endpoint, so we can't use - // insiders.vscode.dev/redirect when we're running in Insiders, unfortunately. - // Additionally, we make the assumption that this function will only be used - // in flows that target supported GHE targets, not on-prem GHES. Because of this - // assumption, we can assume that the GHE version used is at least 3.8 which is - // the version that changed the redirect endpoint to this URI from the old - // GitHub maintained server. - this._redirectEndpoint = 'https://vscode.dev/redirect'; - } - return this._redirectEndpoint; - } - - // TODO@joaomoreno TODO@TylerLeonhardt - private _isNoCorsEnvironment: boolean | undefined; - private async isNoCorsEnvironment(): Promise { - if (this._isNoCorsEnvironment !== undefined) { - return this._isNoCorsEnvironment; - } - const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/dummy`)); - this._isNoCorsEnvironment = (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority)); - return this._isNoCorsEnvironment; - } - - public async login(scopes: string): Promise { - this._logger.info(`Logging in for the following scopes: ${scopes}`); - - // Used for showing a friendlier message to the user when the explicitly cancel a flow. - let userCancelled: boolean | undefined; - const yes = vscode.l10n.t('Yes'); - const no = vscode.l10n.t('No'); - const promptToContinue = async (mode: string) => { - if (userCancelled === undefined) { - // We haven't had a failure yet so wait to prompt - return; - } - const message = userCancelled - ? vscode.l10n.t('Having trouble logging in? Would you like to try a different way? ({0})', mode) - : vscode.l10n.t('You have not yet finished authorizing this extension to use GitHub. Would you like to try a different way? ({0})', mode); - const result = await vscode.window.showWarningMessage(message, yes, no); - if (result !== yes) { - throw new Error(CANCELLATION_ERROR); - } - }; - - const nonce: string = crypto.getRandomValues(new Uint32Array(2)).reduce((prev, curr) => prev += curr.toString(16), ''); - const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate?nonce=${encodeURIComponent(nonce)}`)); - - const supportedClient = isSupportedClient(callbackUri); - const supportedTarget = isSupportedTarget(this._type, this._ghesUri); - - const flows = getFlows({ - target: this._type === AuthProviderType.github - ? GitHubTarget.DotCom - : supportedTarget ? GitHubTarget.HostedEnterprise : GitHubTarget.Enterprise, - extensionHost: typeof navigator === 'undefined' - ? this._extensionKind === vscode.ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote - : ExtensionHost.WebWorker, - isSupportedClient: supportedClient - }); - - - for (const flow of flows) { - try { - if (flow !== flows[0]) { - await promptToContinue(flow.label); - } - return await flow.trigger({ - scopes, - callbackUri, - nonce, - baseUri: this.baseUri, - logger: this._logger, - uriHandler: this._uriHandler, - enterpriseUri: this._ghesUri, - redirectUri: vscode.Uri.parse(await this.getRedirectEndpoint()), - }); - } catch (e) { - userCancelled = this.processLoginError(e); - } - } - - throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.'); - } - - public async logout(session: vscode.AuthenticationSession): Promise { - this._logger.trace(`Deleting session (${session.id}) from server...`); - - if (!Config.gitHubClientSecret) { - this._logger.warn('No client secret configured for GitHub authentication. The token has been deleted with best effort on this system, but we are unable to delete the token on server without the client secret.'); - return; - } - - // Only attempt to delete OAuth tokens. They are always prefixed with `gho_`. - // https://docs.github.com/en/rest/apps/oauth-applications#about-oauth-apps-and-oauth-authorizations-of-github-apps - if (!session.accessToken.startsWith('gho_')) { - this._logger.warn('The token being deleted is not an OAuth token. It has been deleted locally, but we cannot delete it on server.'); - return; - } - - if (!isSupportedTarget(this._type, this._ghesUri)) { - this._logger.trace('GitHub.com and GitHub hosted GitHub Enterprise are the only options that support deleting tokens on the server. Skipping.'); - return; - } - - const authHeader = 'Basic ' + base64Encode(`${Config.gitHubClientId}:${Config.gitHubClientSecret}`); - const uri = this.getServerUri(`/applications/${Config.gitHubClientId}/token`); - - try { - // Defined here: https://docs.github.com/en/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-token - const result = await fetching(uri.toString(true), { - method: 'DELETE', - headers: { - Accept: 'application/vnd.github+json', - Authorization: authHeader, - 'X-GitHub-Api-Version': '2022-11-28', - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - }, - body: JSON.stringify({ access_token: session.accessToken }), - }); - - if (result.status === 204) { - this._logger.trace(`Successfully deleted token from session (${session.id}) from server.`); - return; - } - - try { - const body = await result.text(); - throw new Error(body); - } catch (e) { - throw new Error(`${result.status} ${result.statusText}`); - } - } catch (e) { - this._logger.warn('Failed to delete token from server.' + e.message ?? e); - } - } - - private getServerUri(path: string = '') { - const apiUri = this.baseUri; - // github.com and Hosted GitHub Enterprise instances - if (isSupportedTarget(this._type, this._ghesUri)) { - return vscode.Uri.parse(`${apiUri.scheme}://api.${apiUri.authority}`).with({ path }); - } - // GitHub Enterprise Server (aka on-prem) - return vscode.Uri.parse(`${apiUri.scheme}://${apiUri.authority}/api/v3${path}`); - } - - public async getUserInfo(token: string): Promise<{ id: string; accountName: string }> { - let result; - try { - this._logger.info('Getting user info...'); - result = await fetching(this.getServerUri('/user').toString(), { - headers: { - Authorization: `token ${token}`, - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - } - }); - } catch (ex) { - this._logger.error(ex.message); - throw new Error(NETWORK_ERROR); - } - - if (result.ok) { - try { - const json = await result.json(); - this._logger.info('Got account info!'); - return { id: json.id, accountName: json.login }; - } catch (e) { - this._logger.error(`Unexpected error parsing response from GitHub: ${e.message ?? e}`); - throw e; - } - } else { - // either display the response message or the http status text - let errorMessage = result.statusText; - try { - const json = await result.json(); - if (json.message) { - errorMessage = json.message; - } - } catch (err) { - // noop - } - this._logger.error(`Getting account info failed: ${errorMessage}`); - throw new Error(errorMessage); - } - } - - public async sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise { - if (!vscode.env.isTelemetryEnabled) { - return; - } - const nocors = await this.isNoCorsEnvironment(); - - if (nocors) { - return; - } - - if (this._type === AuthProviderType.github) { - return await this.checkUserDetails(session); - } - - // GHES - await this.checkEnterpriseVersion(session.accessToken); - } - - private async checkUserDetails(session: vscode.AuthenticationSession): Promise { - let edu: string | undefined; - - try { - const result = await fetching('https://education.github.com/api/user', { - headers: { - Authorization: `token ${session.accessToken}`, - 'faculty-check-preview': 'true', - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - } - }); - - if (result.ok) { - const json: { student: boolean; faculty: boolean } = await result.json(); - edu = json.student - ? 'student' - : json.faculty - ? 'faculty' - : 'none'; - } else { - edu = 'unknown'; - } - } catch (e) { - edu = 'unknown'; - } - - /* __GDPR__ - "session" : { - "owner": "TylerLeonhardt", - "isEdu": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "isManaged": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this._telemetryReporter.sendTelemetryEvent('session', { - isEdu: edu, - // Apparently, this is how you tell if a user is an EMU... - isManaged: session.account.label.includes('_') ? 'true' : 'false' - }); - } - - private async checkEnterpriseVersion(token: string): Promise { - try { - let version: string; - if (!isSupportedTarget(this._type, this._ghesUri)) { - const result = await fetching(this.getServerUri('/meta').toString(), { - headers: { - Authorization: `token ${token}`, - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - } - }); - - if (!result.ok) { - return; - } - - const json: { verifiable_password_authentication: boolean; installed_version: string } = await result.json(); - version = json.installed_version; - } else { - version = 'hosted'; - } - - /* __GDPR__ - "ghe-session" : { - "owner": "TylerLeonhardt", - "version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this._telemetryReporter.sendTelemetryEvent('ghe-session', { - version - }); - } catch { - // No-op - } - } - - private processLoginError(error: Error): boolean { - if (error.message === CANCELLATION_ERROR) { - throw error; - } - this._logger.error(error.message ?? error); - return error.message === USER_CANCELLATION_ERROR; - } -} diff --git a/extensions/github-authentication/src/node/authServer.ts b/extensions/github-authentication/src/node/authServer.ts deleted file mode 100644 index de08c6fc..00000000 --- a/extensions/github-authentication/src/node/authServer.ts +++ /dev/null @@ -1,198 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as http from 'http'; -import { URL } from 'url'; -import * as fs from 'fs'; -import * as path from 'path'; -import { randomBytes } from 'crypto'; - -function sendFile(res: http.ServerResponse, filepath: string) { - fs.readFile(filepath, (err, body) => { - if (err) { - console.error(err); - res.writeHead(404); - res.end(); - } else { - res.writeHead(200, { - 'content-length': body.length, - }); - res.end(body); - } - }); -} - -interface IOAuthResult { - code: string; - state: string; -} - -interface ILoopbackServer { - /** - * If undefined, the server is not started yet. - */ - port: number | undefined; - - /** - * The nonce used - */ - nonce: string; - - /** - * The state parameter used in the OAuth flow. - */ - state: string | undefined; - - /** - * Starts the server. - * @returns The port to listen on. - * @throws If the server fails to start. - * @throws If the server is already started. - */ - start(): Promise; - /** - * Stops the server. - * @throws If the server is not started. - * @throws If the server fails to stop. - */ - stop(): Promise; - /** - * Returns a promise that resolves to the result of the OAuth flow. - */ - waitForOAuthResponse(): Promise; -} - -export class LoopbackAuthServer implements ILoopbackServer { - private readonly _server: http.Server; - private readonly _resultPromise: Promise; - private _startingRedirect: URL; - - public nonce = randomBytes(16).toString('base64'); - public port: number | undefined; - - public set state(state: string | undefined) { - if (state) { - this._startingRedirect.searchParams.set('state', state); - } else { - this._startingRedirect.searchParams.delete('state'); - } - } - public get state(): string | undefined { - return this._startingRedirect.searchParams.get('state') ?? undefined; - } - - constructor(serveRoot: string, startingRedirect: string) { - if (!serveRoot) { - throw new Error('serveRoot must be defined'); - } - if (!startingRedirect) { - throw new Error('startingRedirect must be defined'); - } - this._startingRedirect = new URL(startingRedirect); - let deferred: { resolve: (result: IOAuthResult) => void; reject: (reason: any) => void }; - this._resultPromise = new Promise((resolve, reject) => deferred = { resolve, reject }); - - this._server = http.createServer((req, res) => { - const reqUrl = new URL(req.url!, `http://${req.headers.host}`); - switch (reqUrl.pathname) { - case '/signin': { - const receivedNonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); - if (receivedNonce !== this.nonce) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); - res.end(); - } - res.writeHead(302, { location: this._startingRedirect.toString() }); - res.end(); - break; - } - case '/callback': { - const code = reqUrl.searchParams.get('code') ?? undefined; - const state = reqUrl.searchParams.get('state') ?? undefined; - const nonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); - if (!code || !state || !nonce) { - res.writeHead(400); - res.end(); - return; - } - if (this.state !== state) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('State does not match.')}` }); - res.end(); - throw new Error('State does not match.'); - } - if (this.nonce !== nonce) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); - res.end(); - throw new Error('Nonce does not match.'); - } - deferred.resolve({ code, state }); - res.writeHead(302, { location: '/' }); - res.end(); - break; - } - // Serve the static files - case '/': - sendFile(res, path.join(serveRoot, 'index.html')); - break; - default: - // substring to get rid of leading '/' - sendFile(res, path.join(serveRoot, reqUrl.pathname.substring(1))); - break; - } - }); - } - - public start(): Promise { - return new Promise((resolve, reject) => { - if (this._server.listening) { - throw new Error('Server is already started'); - } - const portTimeout = setTimeout(() => { - reject(new Error('Timeout waiting for port')); - }, 5000); - this._server.on('listening', () => { - const address = this._server.address(); - if (typeof address === 'string') { - this.port = parseInt(address); - } else if (address instanceof Object) { - this.port = address.port; - } else { - throw new Error('Unable to determine port'); - } - - clearTimeout(portTimeout); - - // set state which will be used to redirect back to vscode - this.state = `http://127.0.0.1:${this.port}/callback?nonce=${encodeURIComponent(this.nonce)}`; - - resolve(this.port); - }); - this._server.on('error', err => { - reject(new Error(`Error listening to server: ${err}`)); - }); - this._server.on('close', () => { - reject(new Error('Closed')); - }); - this._server.listen(0, '127.0.0.1'); - }); - } - - public stop(): Promise { - return new Promise((resolve, reject) => { - if (!this._server.listening) { - throw new Error('Server is not started'); - } - this._server.close((err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } - - public waitForOAuthResponse(): Promise { - return this._resultPromise; - } -} diff --git a/extensions/github-authentication/src/node/buffer.ts b/extensions/github-authentication/src/node/buffer.ts deleted file mode 100644 index 8e6208aa..00000000 --- a/extensions/github-authentication/src/node/buffer.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function base64Encode(text: string): string { - return Buffer.from(text, 'binary').toString('base64'); -} diff --git a/extensions/github-authentication/src/node/crypto.ts b/extensions/github-authentication/src/node/crypto.ts deleted file mode 100644 index 367246fd..00000000 --- a/extensions/github-authentication/src/node/crypto.ts +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { webcrypto } from 'crypto'; - -export const crypto = webcrypto as any as Crypto; diff --git a/extensions/github-authentication/src/node/fetch.ts b/extensions/github-authentication/src/node/fetch.ts deleted file mode 100644 index 58718078..00000000 --- a/extensions/github-authentication/src/node/fetch.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import fetch from 'node-fetch'; - -export const fetching = fetch; diff --git a/extensions/github-authentication/src/test/flows.test.ts b/extensions/github-authentication/src/test/flows.test.ts deleted file mode 100644 index 7f4963f4..00000000 --- a/extensions/github-authentication/src/test/flows.test.ts +++ /dev/null @@ -1,196 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { ExtensionHost, GitHubTarget, IFlowQuery, getFlows } from '../flows'; -import { Config } from '../config'; - -const enum Flows { - UrlHandlerFlow = 'url handler', - LocalServerFlow = 'local server', - DeviceCodeFlow = 'device code', - PatFlow = 'personal access token' -} - -suite('getFlows', () => { - let lastClientSecret: string | undefined = undefined; - suiteSetup(() => { - lastClientSecret = Config.gitHubClientSecret; - Config.gitHubClientSecret = 'asdf'; - }); - - suiteTeardown(() => { - Config.gitHubClientSecret = lastClientSecret; - }); - - const testCases: Array<{ label: string; query: IFlowQuery; expectedFlows: Flows[] }> = [ - { - label: 'VS Code Desktop. Local filesystem. GitHub.com', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: true, - target: GitHubTarget.DotCom - }, - expectedFlows: [ - Flows.UrlHandlerFlow, - Flows.LocalServerFlow, - Flows.DeviceCodeFlow - ] - }, - { - label: 'VS Code Desktop. Local filesystem. GitHub Hosted Enterprise', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: true, - target: GitHubTarget.HostedEnterprise - }, - expectedFlows: [ - Flows.UrlHandlerFlow, - Flows.LocalServerFlow, - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'VS Code Desktop. Local filesystem. GitHub Enterprise Server', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: true, - target: GitHubTarget.Enterprise - }, - expectedFlows: [ - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'vscode.dev. serverful. GitHub.com', - query: { - extensionHost: ExtensionHost.Remote, - isSupportedClient: true, - target: GitHubTarget.DotCom - }, - expectedFlows: [ - Flows.UrlHandlerFlow, - Flows.DeviceCodeFlow - ] - }, - { - label: 'vscode.dev. serverful. GitHub Hosted Enterprise', - query: { - extensionHost: ExtensionHost.Remote, - isSupportedClient: true, - target: GitHubTarget.HostedEnterprise - }, - expectedFlows: [ - Flows.UrlHandlerFlow, - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'vscode.dev. serverful. GitHub Enterprise', - query: { - extensionHost: ExtensionHost.Remote, - isSupportedClient: true, - target: GitHubTarget.Enterprise - }, - expectedFlows: [ - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'vscode.dev. serverless. GitHub.com', - query: { - extensionHost: ExtensionHost.WebWorker, - isSupportedClient: true, - target: GitHubTarget.DotCom - }, - expectedFlows: [ - Flows.UrlHandlerFlow - ] - }, - { - label: 'vscode.dev. serverless. GitHub Hosted Enterprise', - query: { - extensionHost: ExtensionHost.WebWorker, - isSupportedClient: true, - target: GitHubTarget.HostedEnterprise - }, - expectedFlows: [ - Flows.UrlHandlerFlow, - Flows.PatFlow - ] - }, - { - label: 'vscode.dev. serverless. GitHub Enterprise Server', - query: { - extensionHost: ExtensionHost.WebWorker, - isSupportedClient: true, - target: GitHubTarget.Enterprise - }, - expectedFlows: [ - Flows.PatFlow - ] - }, - { - label: 'Code - OSS. Local filesystem. GitHub.com', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: false, - target: GitHubTarget.DotCom - }, - expectedFlows: [ - Flows.LocalServerFlow, - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'Code - OSS. Local filesystem. GitHub Hosted Enterprise', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: false, - target: GitHubTarget.HostedEnterprise - }, - expectedFlows: [ - Flows.LocalServerFlow, - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - { - label: 'Code - OSS. Local filesystem. GitHub Enterprise Server', - query: { - extensionHost: ExtensionHost.Local, - isSupportedClient: false, - target: GitHubTarget.Enterprise - }, - expectedFlows: [ - Flows.DeviceCodeFlow, - Flows.PatFlow - ] - }, - ]; - - for (const testCase of testCases) { - test(`gives the correct flows - ${testCase.label}`, () => { - const flows = getFlows(testCase.query); - - assert.strictEqual( - flows.length, - testCase.expectedFlows.length, - `Unexpected number of flows: ${flows.map(f => f.label).join(',')}` - ); - - for (let i = 0; i < flows.length; i++) { - const flow = flows[i]; - - assert.strictEqual(flow.label, testCase.expectedFlows[i]); - } - }); - } -}); diff --git a/extensions/github-authentication/src/test/node/authServer.test.ts b/extensions/github-authentication/src/test/node/authServer.test.ts deleted file mode 100644 index 6de8da61..00000000 --- a/extensions/github-authentication/src/test/node/authServer.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { LoopbackAuthServer } from '../../node/authServer'; - -suite('LoopbackAuthServer', () => { - let server: LoopbackAuthServer; - let port: number; - - setup(async () => { - server = new LoopbackAuthServer(__dirname, 'http://localhost:8080'); - port = await server.start(); - }); - - teardown(async () => { - await server.stop(); - }); - - test('should redirect to starting redirect on /signin', async () => { - const response = await fetch(`http://localhost:${port}/signin?nonce=${server.nonce}`, { - redirect: 'manual' - }); - // Redirect - assert.strictEqual(response.status, 302); - - // Check location - const location = response.headers.get('location'); - assert.ok(location); - const locationUrl = new URL(location); - assert.strictEqual(locationUrl.origin, 'http://localhost:8080'); - - // Check state - const state = locationUrl.searchParams.get('state'); - assert.ok(state); - const stateLocation = new URL(state); - assert.strictEqual(stateLocation.origin, `http://127.0.0.1:${port}`); - assert.strictEqual(stateLocation.pathname, '/callback'); - assert.strictEqual(stateLocation.searchParams.get('nonce'), server.nonce); - }); - - test('should return 400 on /callback with missing parameters', async () => { - const response = await fetch(`http://localhost:${port}/callback`); - assert.strictEqual(response.status, 400); - }); - - test('should resolve with code and state on /callback with valid parameters', async () => { - server.state = 'valid-state'; - const response = await fetch( - `http://localhost:${port}/callback?code=valid-code&state=${server.state}&nonce=${server.nonce}`, - { redirect: 'manual' } - ); - assert.strictEqual(response.status, 302); - assert.strictEqual(response.headers.get('location'), '/'); - await Promise.race([ - server.waitForOAuthResponse().then(result => { - assert.strictEqual(result.code, 'valid-code'); - assert.strictEqual(result.state, server.state); - }), - new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000)) - ]); - }); -}); diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json deleted file mode 100644 index 5e4713e9..00000000 --- a/extensions/github-authentication/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": "./out", - "experimentalDecorators": true, - "typeRoots": [ - "./node_modules/@types" - ], - "lib": [ - "WebWorker" - ] - }, - "include": [ - "src/**/*", - "../../src/vscode-dts/vscode.d.ts" - ] -} diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock deleted file mode 100644 index 6ca3b99e..00000000 --- a/extensions/github-authentication/yarn.lock +++ /dev/null @@ -1,233 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@microsoft/1ds-core-js@4.0.3", "@microsoft/1ds-core-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz#c8a92c623745a9595e06558a866658480c33bdf9" - integrity sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/1ds-post-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz#cfcb20bb23fb6215d3f0732f60f5b7df3e624f86" - integrity sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ== - dependencies: - "@microsoft/1ds-core-js" "4.0.3" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-channel-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz#247b6fe2158fad9826cbcdf7304f885766b36624" - integrity sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A== - dependencies: - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-common@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz#c4aa53ba343f5b3c7fbf54cddd3c86a5bdcd95dc" - integrity sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-core-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz#008308b786930d94a1de8a1fbb4af0351b74653e" - integrity sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g== - dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-shims@3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/applicationinsights-web-basic@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz#9a23323276b4a5a0dc6a352e2de5d75e3c16b534" - integrity sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ== - dependencies: - "@microsoft/applicationinsights-channel-js" "3.0.4" - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@nevware21/ts-async@>= 0.3.0 < 2.x": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" - integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== - dependencies: - "@nevware21/ts-utils" ">= 0.10.0 < 2.x" - -"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.10.1 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" - integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== - -"@types/mocha@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== - -"@types/node-fetch@^2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" - integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*": - version "14.0.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.5.tgz#3d03acd3b3414cf67faf999aed11682ed121f22b" - integrity sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA== - -"@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== - -"@vscode/extension-telemetry@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz#8c6c61e253ff304f46045f04edd60059b144417a" - integrity sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ== - dependencies: - "@microsoft/1ds-core-js" "^4.0.3" - "@microsoft/1ds-post-js" "^4.0.3" - "@microsoft/applicationinsights-web-basic" "^3.0.4" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -axios@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== - -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -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: - 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" - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -tas-client@0.1.73: - version "0.1.73" - resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.73.tgz#2dacf68547a37989ef1554c6510dc108a1ea7a71" - integrity sha512-UDdUF9kV2hYdlv+7AgqP2kXarVSUhjK7tg1BUflIRGEgND0/QoNpN64rcEuhEcM8AIbW65yrCopJWqRhLZ3m8w== - dependencies: - axios "^1.6.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -vscode-tas-client@^0.1.47: - version "0.1.75" - resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.75.tgz#771780a9a178163028299f52d41973300060dd38" - integrity sha512-/+ALFWPI4U3obeRvLFSt39guT7P9bZQrkmcLoiS+2HtzJ/7iPKNt5Sj+XTiitGlPYVFGFc0plxX8AAp6Uxs0xQ== - dependencies: - tas-client "0.1.73" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" diff --git a/extensions/github/.vscodeignore b/extensions/github/.vscodeignore deleted file mode 100644 index ed8fa3a7..00000000 --- a/extensions/github/.vscodeignore +++ /dev/null @@ -1,7 +0,0 @@ -src/** -!src/common/config.json -out/** -build/** -extension.webpack.config.js -tsconfig.json -yarn.lock diff --git a/extensions/github/README.md b/extensions/github/README.md deleted file mode 100644 index 293a5cd6..00000000 --- a/extensions/github/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# GitHub for Visual Studio Code - -**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. - -## Features - -This extension provides the following GitHub-related features for VS Code: - -- `Publish to GitHub` command -- `Clone from GitHub` participant to the `Git: Clone` command -- GitHub authentication for built-in git commands, controlled via the `github.gitAuthentication` command -- Automatic fork creation when attempting to push to a repository without permissions diff --git a/extensions/github/extension.webpack.config.js b/extensions/github/extension.webpack.config.js deleted file mode 100644 index 45600607..00000000 --- a/extensions/github/extension.webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - extension: './src/extension.ts' - } -}); diff --git a/extensions/github/images/icon.png b/extensions/github/images/icon.png deleted file mode 100644 index d706dc83e7a7fce76157a8dfd5be1720e2f9ceb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3657 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 a6QCh=sf&*Aiq0&d+j)Goe(3H=XYWEMpL diff --git a/extensions/github/markdown.css b/extensions/github/markdown.css deleted file mode 100644 index a8cb5840..00000000 --- a/extensions/github/markdown.css +++ /dev/null @@ -1,9 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.vscode-dark img[src$=\#gh-light-mode-only], -.vscode-light img[src$=\#gh-dark-mode-only] { - display: none; -} diff --git a/extensions/github/package.json b/extensions/github/package.json deleted file mode 100644 index 1e5e886c..00000000 --- a/extensions/github/package.json +++ /dev/null @@ -1,195 +0,0 @@ -{ - "name": "github", - "displayName": "%displayName%", - "description": "%description%", - "publisher": "vscode", - "license": "MIT", - "version": "0.0.1", - "engines": { - "vscode": "^1.41.0" - }, - "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", - "icon": "images/icon.png", - "categories": [ - "Other" - ], - "activationEvents": [ - "*" - ], - "extensionDependencies": [ - "vscode.git-base" - ], - "main": "./out/extension.js", - "capabilities": { - "virtualWorkspaces": false, - "untrustedWorkspaces": { - "supported": true - } - }, - "enabledApiProposals": [ - "contribShareMenu", - "contribEditSessions", - "canonicalUriProvider", - "shareProvider" - ], - "contributes": { - "commands": [ - { - "command": "github.publish", - "title": "Publish to GitHub" - }, - { - "command": "github.copyVscodeDevLink", - "title": "Copy vscode.dev Link" - }, - { - "command": "github.copyVscodeDevLinkFile", - "title": "Copy vscode.dev Link" - }, - { - "command": "github.copyVscodeDevLinkWithoutRange", - "title": "Copy vscode.dev Link" - }, - { - "command": "github.openOnVscodeDev", - "title": "Open in vscode.dev", - "icon": "$(globe)" - } - ], - "continueEditSession": [ - { - "command": "github.openOnVscodeDev", - "when": "github.hasGitHubRepo", - "qualifiedName": "Continue Working in vscode.dev", - "category": "Remote Repositories", - "remoteGroup": "virtualfs_44_vscode-vfs_2_web@2" - } - ], - "menus": { - "commandPalette": [ - { - "command": "github.publish", - "when": "git-base.gitEnabled && remoteName != 'codespaces'" - }, - { - "command": "github.copyVscodeDevLink", - "when": "false" - }, - { - "command": "github.copyVscodeDevLinkFile", - "when": "false" - }, - { - "command": "github.copyVscodeDevLinkWithoutRange", - "when": "false" - }, - { - "command": "github.openOnVscodeDev", - "when": "false" - } - ], - "file/share": [ - { - "command": "github.copyVscodeDevLinkFile", - "when": "github.hasGitHubRepo && remoteName != 'codespaces'", - "group": "0_vscode@0" - } - ], - "editor/context/share": [ - { - "command": "github.copyVscodeDevLink", - "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", - "group": "0_vscode@0" - } - ], - "explorer/context/share": [ - { - "command": "github.copyVscodeDevLinkWithoutRange", - "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", - "group": "0_vscode@0" - } - ], - "editor/lineNumber/context": [ - { - "command": "github.copyVscodeDevLink", - "when": "github.hasGitHubRepo && resourceScheme != untitled && activeEditor == workbench.editors.files.textFileEditor && config.editor.lineNumbers == on && remoteName != 'codespaces'", - "group": "1_cutcopypaste@2" - }, - { - "command": "github.copyVscodeDevLink", - "when": "github.hasGitHubRepo && resourceScheme != untitled && activeEditor == workbench.editor.notebook && remoteName != 'codespaces'", - "group": "1_cutcopypaste@2" - } - ], - "editor/title/context/share": [ - { - "command": "github.copyVscodeDevLinkWithoutRange", - "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", - "group": "0_vscode@0" - } - ] - }, - "configuration": [ - { - "title": "GitHub", - "properties": { - "github.branchProtection": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%config.branchProtection%" - }, - "github.gitAuthentication": { - "type": "boolean", - "scope": "resource", - "default": true, - "description": "%config.gitAuthentication%" - }, - "github.gitProtocol": { - "type": "string", - "enum": [ - "https", - "ssh" - ], - "default": "https", - "description": "%config.gitProtocol%" - } - } - } - ], - "viewsWelcome": [ - { - "view": "scm", - "contents": "%welcome.publishFolder%", - "when": "config.git.enabled && git.state == initialized && workbenchState == folder && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0" - }, - { - "view": "scm", - "contents": "%welcome.publishWorkspaceFolder%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0" - } - ], - "markdown.previewStyles": [ - "./markdown.css" - ] - }, - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "gulp compile-extension:github", - "watch": "gulp watch-extension:github" - }, - "dependencies": { - "@octokit/graphql": "5.0.5", - "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", - "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.0" - }, - "devDependencies": { - "@types/node": "18.x" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } -} diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json deleted file mode 100644 index 909ec761..00000000 --- a/extensions/github/package.nls.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "displayName": "GitHub", - "description": "GitHub features for Kylin-Code", - "config.branchProtection": "Controls whether to query repository rules for GitHub repositories", - "config.gitAuthentication": "Controls whether to enable automatic GitHub authentication for git commands within Kylin-Code.", - "config.gitProtocol": "Controls which protocol is used to clone a GitHub repository", - "welcome.publishFolder": { - "message": "You can directly publish this folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", - "comment": [ - "{Locked='$(github)'}", - "Do not translate '$(github)'. It will be rendered as an icon", - "{Locked='](command:github.publish'}", - "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Kylin-Code", - "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" - ] - }, - "welcome.publishWorkspaceFolder": { - "message": "You can directly publish a workspace folder to a GitHub repository. Once published, you'll have access to source control features powered by git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", - "comment": [ - "{Locked='$(github)'}", - "Do not translate '$(github)'. It will be rendered as an icon", - "{Locked='](command:github.publish'}", - "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Kylin-Code", - "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" - ] - } -} diff --git a/extensions/github/src/auth.ts b/extensions/github/src/auth.ts deleted file mode 100644 index e7be2637..00000000 --- a/extensions/github/src/auth.ts +++ /dev/null @@ -1,89 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { AuthenticationSession, authentication, window } from 'vscode'; -import { Agent, globalAgent } from 'https'; -import { graphql } from '@octokit/graphql/dist-types/types'; -import { Octokit } from '@octokit/rest'; -import { httpsOverHttp } from 'tunnel'; -import { URL } from 'url'; - -export class AuthenticationError extends Error { } - -function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent { - if (!url) { - return globalAgent; - } - - try { - const { hostname, port, username, password } = new URL(url); - const auth = username && password && `${username}:${password}`; - return httpsOverHttp({ proxy: { host: hostname, port, proxyAuth: auth } }); - } catch (e) { - window.showErrorMessage(`HTTPS_PROXY environment variable ignored: ${e.message}`); - return globalAgent; - } -} - -const scopes = ['repo', 'workflow', 'user:email', 'read:user']; - -export async function getSession(): Promise { - return await authentication.getSession('github', scopes, { createIfNone: true }); -} - -let _octokit: Promise | undefined; - -export function getOctokit(): Promise { - if (!_octokit) { - _octokit = getSession().then(async session => { - const token = session.accessToken; - const agent = getAgent(); - - const { Octokit } = await import('@octokit/rest'); - - return new Octokit({ - request: { agent }, - userAgent: 'GitHub VSCode', - auth: `token ${token}` - }); - }).then(null, async err => { - _octokit = undefined; - throw err; - }); - } - - return _octokit; -} - -let _octokitGraphql: Promise | undefined; - -export async function getOctokitGraphql(): Promise { - if (!_octokitGraphql) { - try { - const session = await authentication.getSession('github', scopes, { silent: true }); - - if (!session) { - throw new AuthenticationError('No GitHub authentication session available.'); - } - - const token = session.accessToken; - const { graphql } = await import('@octokit/graphql'); - - return graphql.defaults({ - headers: { - authorization: `token ${token}` - }, - request: { - agent: getAgent() - } - }); - } catch (err) { - _octokitGraphql = undefined; - throw err; - } - } - - return _octokitGraphql; -} diff --git a/extensions/github/src/branchProtection.ts b/extensions/github/src/branchProtection.ts deleted file mode 100644 index a7d18c41..00000000 --- a/extensions/github/src/branchProtection.ts +++ /dev/null @@ -1,244 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { authentication, EventEmitter, LogOutputChannel, Memento, Uri, workspace } from 'vscode'; -import { Repository as GitHubRepository, RepositoryRuleset } from '@octokit/graphql-schema'; -import { AuthenticationError, getOctokitGraphql } from './auth'; -import { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git'; -import { DisposableStore, getRepositoryFromUrl } from './util'; -import TelemetryReporter from '@vscode/extension-telemetry'; - -const REPOSITORY_QUERY = ` - query repositoryPermissions($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - defaultBranchRef { - name - }, - viewerPermission - } - } -`; - -const REPOSITORY_RULESETS_QUERY = ` - query repositoryRulesets($owner: String!, $repo: String!, $cursor: String, $limit: Int = 100) { - repository(owner: $owner, name: $repo) { - rulesets(includeParents: true, first: $limit, after: $cursor) { - nodes { - name - enforcement - rules(type: PULL_REQUEST) { - totalCount - } - conditions { - refName { - include - exclude - } - } - target - }, - pageInfo { - endCursor, - hasNextPage - } - } - } - } -`; - -export class GithubBranchProtectionProviderManager { - - private readonly disposables = new DisposableStore(); - private readonly providerDisposables = new DisposableStore(); - - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - if (enabled) { - for (const repository of this.gitAPI.repositories) { - this.providerDisposables.add(this.gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); - } - } else { - this.providerDisposables.dispose(); - } - - this._enabled = enabled; - } - - constructor( - private readonly gitAPI: API, - private readonly globalState: Memento, - private readonly logger: LogOutputChannel, - private readonly telemetryReporter: TelemetryReporter) { - this.disposables.add(this.gitAPI.onDidOpenRepository(repository => { - if (this._enabled) { - this.providerDisposables.add(gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); - } - })); - - this.disposables.add(workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('github.branchProtection')) { - this.updateEnablement(); - } - })); - - this.updateEnablement(); - } - - private updateEnablement(): void { - const config = workspace.getConfiguration('github', null); - this.enabled = config.get('branchProtection', true) === true; - } - - dispose(): void { - this.enabled = false; - this.disposables.dispose(); - } - -} - -export class GithubBranchProtectionProvider implements BranchProtectionProvider { - private readonly _onDidChangeBranchProtection = new EventEmitter(); - onDidChangeBranchProtection = this._onDidChangeBranchProtection.event; - - private branchProtection: BranchProtection[]; - private readonly globalStateKey = `branchProtection:${this.repository.rootUri.toString()}`; - - constructor( - private readonly repository: Repository, - private readonly globalState: Memento, - private readonly logger: LogOutputChannel, - private readonly telemetryReporter: TelemetryReporter) { - // Restore branch protection from global state - this.branchProtection = this.globalState.get(this.globalStateKey, []); - - repository.status().then(() => { - authentication.onDidChangeSessions(e => { - if (e.provider.id === 'github') { - this.updateRepositoryBranchProtection(); - } - }); - this.updateRepositoryBranchProtection(); - }); - } - - provideBranchProtection(): BranchProtection[] { - return this.branchProtection; - } - - private async getRepositoryDetails(owner: string, repo: string): Promise { - const graphql = await getOctokitGraphql(); - const { repository } = await graphql<{ repository: GitHubRepository }>(REPOSITORY_QUERY, { owner, repo }); - - return repository; - } - - private async getRepositoryRulesets(owner: string, repo: string): Promise { - const rulesets: RepositoryRuleset[] = []; - - let cursor: string | undefined = undefined; - const graphql = await getOctokitGraphql(); - - while (true) { - const { repository } = await graphql<{ repository: GitHubRepository }>(REPOSITORY_RULESETS_QUERY, { owner, repo, cursor }); - - rulesets.push(...(repository.rulesets?.nodes ?? []) - // Active branch ruleset that contains the pull request required rule - .filter(node => node && node.target === 'BRANCH' && node.enforcement === 'ACTIVE' && (node.rules?.totalCount ?? 0) > 0) as RepositoryRuleset[]); - - if (repository.rulesets?.pageInfo.hasNextPage) { - cursor = repository.rulesets.pageInfo.endCursor as string | undefined; - } else { - break; - } - } - - return rulesets; - } - - private async updateRepositoryBranchProtection(): Promise { - const branchProtection: BranchProtection[] = []; - - try { - for (const remote of this.repository.state.remotes) { - const repository = getRepositoryFromUrl(remote.pushUrl ?? remote.fetchUrl ?? ''); - - if (!repository) { - continue; - } - - // Repository details - this.logger.trace(`Fetching repository details for "${repository.owner}/${repository.repo}".`); - const repositoryDetails = await this.getRepositoryDetails(repository.owner, repository.repo); - - // Check repository write permission - if (repositoryDetails.viewerPermission !== 'ADMIN' && repositoryDetails.viewerPermission !== 'MAINTAIN' && repositoryDetails.viewerPermission !== 'WRITE') { - this.logger.trace(`Skipping branch protection for "${repository.owner}/${repository.repo}" due to missing repository write permission.`); - continue; - } - - // Get repository rulesets - const branchProtectionRules: BranchProtectionRule[] = []; - const repositoryRulesets = await this.getRepositoryRulesets(repository.owner, repository.repo); - - for (const ruleset of repositoryRulesets) { - branchProtectionRules.push({ - include: (ruleset.conditions.refName?.include ?? []).map(r => this.parseRulesetRefName(repositoryDetails, r)), - exclude: (ruleset.conditions.refName?.exclude ?? []).map(r => this.parseRulesetRefName(repositoryDetails, r)) - }); - } - - branchProtection.push({ remote: remote.name, rules: branchProtectionRules }); - } - - this.branchProtection = branchProtection; - this._onDidChangeBranchProtection.fire(this.repository.rootUri); - - // Save branch protection to global state - await this.globalState.update(this.globalStateKey, branchProtection); - this.logger.trace(`Branch protection for "${this.repository.rootUri.toString()}": ${JSON.stringify(branchProtection)}.`); - - /* __GDPR__ - "branchProtection" : { - "owner": "lszomoru", - "rulesetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of repository rulesets" } - } - */ - this.telemetryReporter.sendTelemetryEvent('branchProtection', undefined, { rulesetCount: this.branchProtection.length }); - } catch (err) { - this.logger.warn(`Failed to update repository branch protection: ${err.message}`); - - if (err instanceof AuthenticationError) { - // A GitHub authentication session could be missing if the user has not yet - // signed in with their GitHub account or they have signed out. If there is - // branch protection information we have to clear it. - if (this.branchProtection.length !== 0) { - this.branchProtection = branchProtection; - this._onDidChangeBranchProtection.fire(this.repository.rootUri); - - await this.globalState.update(this.globalStateKey, undefined); - } - } - } - } - - private parseRulesetRefName(repository: GitHubRepository, refName: string): string { - if (refName.startsWith('refs/heads/')) { - return refName.substring(11); - } - - switch (refName) { - case '~ALL': - return '**/*'; - case '~DEFAULT_BRANCH': - return repository.defaultBranchRef!.name; - default: - return refName; - } - } -} diff --git a/extensions/github/src/canonicalUriProvider.ts b/extensions/github/src/canonicalUriProvider.ts deleted file mode 100644 index 09f5e243..00000000 --- a/extensions/github/src/canonicalUriProvider.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationToken, CanonicalUriProvider, CanonicalUriRequestOptions, Disposable, ProviderResult, Uri, workspace } from 'vscode'; -import { API } from './typings/git'; - -const SUPPORTED_SCHEMES = ['ssh', 'https', 'file']; - -export class GitHubCanonicalUriProvider implements CanonicalUriProvider { - - private disposables: Disposable[] = []; - constructor(private gitApi: API) { - this.disposables.push(...SUPPORTED_SCHEMES.map((scheme) => workspace.registerCanonicalUriProvider(scheme, this))); - } - - dispose() { this.disposables.forEach((disposable) => disposable.dispose()); } - - provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, _token: CancellationToken): ProviderResult { - if (options.targetScheme !== 'https') { - return; - } - - switch (uri.scheme) { - case 'file': { - const repository = this.gitApi.getRepository(uri); - const remote = repository?.state.remotes.find((remote) => remote.name === repository.state.HEAD?.remote)?.pushUrl?.replace(/^(git@[^\/:]+)(:)/i, 'ssh://$1/'); - if (remote) { - return toHttpsGitHubRemote(uri); - } - } - default: - return toHttpsGitHubRemote(uri); - } - } -} - -function toHttpsGitHubRemote(uri: Uri) { - if (uri.scheme === 'ssh' && uri.authority === 'git@github.com') { - // if this is a git@github.com URI, return the HTTPS equivalent - const [owner, repo] = (uri.path.endsWith('.git') ? uri.path.slice(0, -4) : uri.path).split('/').filter((segment) => segment.length > 0); - return Uri.parse(`https://github.com/${owner}/${repo}`); - } - if (uri.scheme === 'https' && uri.authority === 'github.com') { - return uri; - } - return undefined; -} diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts deleted file mode 100644 index 1f150452..00000000 --- a/extensions/github/src/commands.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { API as GitAPI } from './typings/git'; -import { publishRepository } from './publish'; -import { DisposableStore } from './util'; -import { LinkContext, getLink, getVscodeDevHost } from './links'; - -async function copyVscodeDevLink(gitAPI: GitAPI, useSelection: boolean, context: LinkContext, includeRange = true) { - try { - const permalink = await getLink(gitAPI, useSelection, true, getVscodeDevHost(), 'headlink', context, includeRange); - if (permalink) { - return vscode.env.clipboard.writeText(permalink); - } - } catch (err) { - if (!(err instanceof vscode.CancellationError)) { - vscode.window.showErrorMessage(err.message); - } - } -} - -async function openVscodeDevLink(gitAPI: GitAPI): Promise { - try { - const headlink = await getLink(gitAPI, true, false, getVscodeDevHost(), 'headlink'); - return headlink ? vscode.Uri.parse(headlink) : undefined; - } catch (err) { - if (!(err instanceof vscode.CancellationError)) { - vscode.window.showErrorMessage(err.message); - } - return undefined; - } -} - -export function registerCommands(gitAPI: GitAPI): vscode.Disposable { - const disposables = new DisposableStore(); - - disposables.add(vscode.commands.registerCommand('github.publish', async () => { - try { - publishRepository(gitAPI); - } catch (err) { - vscode.window.showErrorMessage(err.message); - } - })); - - disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLink', async (context: LinkContext) => { - return copyVscodeDevLink(gitAPI, true, context); - })); - - disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLinkFile', async (context: LinkContext) => { - return copyVscodeDevLink(gitAPI, false, context); - })); - - disposables.add(vscode.commands.registerCommand('github.copyVscodeDevLinkWithoutRange', async (context: LinkContext) => { - return copyVscodeDevLink(gitAPI, true, context, false); - })); - - disposables.add(vscode.commands.registerCommand('github.openOnVscodeDev', async () => { - return openVscodeDevLink(gitAPI); - })); - - return disposables; -} diff --git a/extensions/github/src/credentialProvider.ts b/extensions/github/src/credentialProvider.ts deleted file mode 100644 index 14c7e6a2..00000000 --- a/extensions/github/src/credentialProvider.ts +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CredentialsProvider, Credentials, API as GitAPI } from './typings/git'; -import { workspace, Uri, Disposable } from 'vscode'; -import { getSession } from './auth'; - -const EmptyDisposable: Disposable = { dispose() { } }; - -class GitHubCredentialProvider implements CredentialsProvider { - - async getCredentials(host: Uri): Promise { - if (!/github\.com/i.test(host.authority)) { - return; - } - - const session = await getSession(); - return { username: session.account.id, password: session.accessToken }; - } -} - -export class GithubCredentialProviderManager { - - private providerDisposable: Disposable = EmptyDisposable; - private readonly disposable: Disposable; - - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - this._enabled = enabled; - - if (enabled) { - this.providerDisposable = this.gitAPI.registerCredentialsProvider(new GitHubCredentialProvider()); - } else { - this.providerDisposable.dispose(); - } - } - - constructor(private gitAPI: GitAPI) { - this.disposable = workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('github')) { - this.refresh(); - } - }); - - this.refresh(); - } - - private refresh(): void { - const config = workspace.getConfiguration('github', null); - const enabled = config.get('gitAuthentication', true); - this.enabled = !!enabled; - } - - dispose(): void { - this.enabled = false; - this.disposable.dispose(); - } -} diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts deleted file mode 100644 index 18277683..00000000 --- a/extensions/github/src/extension.ts +++ /dev/null @@ -1,132 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { commands, Disposable, ExtensionContext, extensions, l10n, LogLevel, LogOutputChannel, window } from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { GithubRemoteSourceProvider } from './remoteSourceProvider'; -import { API, GitExtension } from './typings/git'; -import { registerCommands } from './commands'; -import { GithubCredentialProviderManager } from './credentialProvider'; -import { DisposableStore, repositoryHasGitHubRemote } from './util'; -import { GithubPushErrorHandler } from './pushErrorHandler'; -import { GitBaseExtension } from './typings/git-base'; -import { GithubRemoteSourcePublisher } from './remoteSourcePublisher'; -import { GithubBranchProtectionProviderManager } from './branchProtection'; -import { GitHubCanonicalUriProvider } from './canonicalUriProvider'; -import { VscodeDevShareProvider } from './shareProviders'; - -export function activate(context: ExtensionContext): void { - const disposables: Disposable[] = []; - context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); - - const logger = window.createOutputChannel('GitHub', { log: true }); - disposables.push(logger); - - const onDidChangeLogLevel = (logLevel: LogLevel) => { - logger.appendLine(l10n.t('Log level: {0}', LogLevel[logLevel])); - }; - disposables.push(logger.onDidChangeLogLevel(onDidChangeLogLevel)); - onDidChangeLogLevel(logger.logLevel); - - const { aiKey } = require('../package.json') as { aiKey: string }; - const telemetryReporter = new TelemetryReporter(aiKey); - disposables.push(telemetryReporter); - - disposables.push(initializeGitBaseExtension()); - disposables.push(initializeGitExtension(context, telemetryReporter, logger)); -} - -function initializeGitBaseExtension(): Disposable { - const disposables = new DisposableStore(); - - const initialize = () => { - try { - const gitBaseAPI = gitBaseExtension.getAPI(1); - - disposables.add(gitBaseAPI.registerRemoteSourceProvider(new GithubRemoteSourceProvider())); - } - catch (err) { - console.error('Could not initialize GitHub extension'); - console.warn(err); - } - }; - - const onDidChangeGitBaseExtensionEnablement = (enabled: boolean) => { - if (!enabled) { - disposables.dispose(); - } else { - initialize(); - } - }; - - const gitBaseExtension = extensions.getExtension('vscode.git-base')!.exports; - disposables.add(gitBaseExtension.onDidChangeEnablement(onDidChangeGitBaseExtensionEnablement)); - onDidChangeGitBaseExtensionEnablement(gitBaseExtension.enabled); - - return disposables; -} - -function setGitHubContext(gitAPI: API, disposables: DisposableStore) { - if (gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo))) { - commands.executeCommand('setContext', 'github.hasGitHubRepo', true); - } else { - const openRepoDisposable = gitAPI.onDidOpenRepository(async e => { - await e.status(); - if (repositoryHasGitHubRemote(e)) { - commands.executeCommand('setContext', 'github.hasGitHubRepo', true); - openRepoDisposable.dispose(); - } - }); - disposables.add(openRepoDisposable); - } -} - -function initializeGitExtension(context: ExtensionContext, telemetryReporter: TelemetryReporter, logger: LogOutputChannel): Disposable { - const disposables = new DisposableStore(); - - let gitExtension = extensions.getExtension('vscode.git'); - - const initialize = () => { - gitExtension!.activate() - .then(extension => { - const onDidChangeGitExtensionEnablement = (enabled: boolean) => { - if (enabled) { - const gitAPI = extension.getAPI(1); - - disposables.add(registerCommands(gitAPI)); - disposables.add(new GithubCredentialProviderManager(gitAPI)); - disposables.add(new GithubBranchProtectionProviderManager(gitAPI, context.globalState, logger, telemetryReporter)); - disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler(telemetryReporter))); - disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI))); - disposables.add(new GitHubCanonicalUriProvider(gitAPI)); - disposables.add(new VscodeDevShareProvider(gitAPI)); - setGitHubContext(gitAPI, disposables); - - commands.executeCommand('setContext', 'git-base.gitEnabled', true); - } else { - disposables.dispose(); - } - }; - - disposables.add(extension.onDidChangeEnablement(onDidChangeGitExtensionEnablement)); - onDidChangeGitExtensionEnablement(extension.enabled); - }); - }; - - if (gitExtension) { - initialize(); - } else { - const listener = extensions.onDidChange(() => { - if (!gitExtension && extensions.getExtension('vscode.git')) { - gitExtension = extensions.getExtension('vscode.git'); - initialize(); - listener.dispose(); - } - }); - disposables.add(listener); - } - - return disposables; -} diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts deleted file mode 100644 index 911f0e53..00000000 --- a/extensions/github/src/links.ts +++ /dev/null @@ -1,256 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { API as GitAPI, RefType, Repository } from './typings/git'; -import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util'; - -export function isFileInRepo(repository: Repository, file: vscode.Uri): boolean { - return file.path.toLowerCase() === repository.rootUri.path.toLowerCase() || - (file.path.toLowerCase().startsWith(repository.rootUri.path.toLowerCase()) && - file.path.substring(repository.rootUri.path.length).startsWith('/')); -} - -export function getRepositoryForFile(gitAPI: GitAPI, file: vscode.Uri): Repository | undefined { - for (const repository of gitAPI.repositories) { - if (isFileInRepo(repository, file)) { - return repository; - } - } - return undefined; -} - -enum LinkType { - File = 1, - Notebook = 2 -} - -interface IFilePosition { - type: LinkType.File; - uri: vscode.Uri; - range: vscode.Range | undefined; -} - -interface INotebookPosition { - type: LinkType.Notebook; - uri: vscode.Uri; - cellIndex: number; - range: vscode.Range | undefined; -} - -interface EditorLineNumberContext { - uri: vscode.Uri; - lineNumber: number; -} -export type LinkContext = vscode.Uri | EditorLineNumberContext | undefined; - -function extractContext(context: LinkContext): { fileUri: vscode.Uri | undefined; lineNumber: number | undefined } { - if (context instanceof vscode.Uri) { - return { fileUri: context, lineNumber: undefined }; - } else if (context !== undefined && 'lineNumber' in context && 'uri' in context) { - return { fileUri: context.uri, lineNumber: context.lineNumber }; - } else { - return { fileUri: undefined, lineNumber: undefined }; - } -} - -function getFileAndPosition(context: LinkContext): IFilePosition | INotebookPosition | undefined { - let range: vscode.Range | undefined; - - const { fileUri, lineNumber } = extractContext(context); - const uri = fileUri ?? vscode.window.activeTextEditor?.document.uri; - - if (uri) { - if (uri.scheme === 'vscode-notebook-cell' && vscode.window.activeNotebookEditor?.notebook.uri.fsPath === uri.fsPath) { - // if the active editor is a notebook editor and the focus is inside any a cell text editor - // generate deep link for text selection for the notebook cell. - const cell = vscode.window.activeNotebookEditor.notebook.getCells().find(cell => cell.document.uri.fragment === uri?.fragment); - const cellIndex = cell?.index ?? vscode.window.activeNotebookEditor.selection.start; - - const range = getRangeOrSelection(lineNumber); - return { type: LinkType.Notebook, uri, cellIndex, range }; - } else { - // the active editor is a text editor - range = getRangeOrSelection(lineNumber); - return { type: LinkType.File, uri, range }; - } - } - - if (vscode.window.activeNotebookEditor) { - // if the active editor is a notebook editor but the focus is not inside any cell text editor, generate deep link for the cell selection in the notebook document. - return { type: LinkType.Notebook, uri: vscode.window.activeNotebookEditor.notebook.uri, cellIndex: vscode.window.activeNotebookEditor.selection.start, range: undefined }; - } - - return undefined; -} - -function getRangeOrSelection(lineNumber: number | undefined) { - return lineNumber !== undefined && (!vscode.window.activeTextEditor || vscode.window.activeTextEditor.selection.isEmpty || !vscode.window.activeTextEditor.selection.contains(new vscode.Position(lineNumber - 1, 0))) - ? new vscode.Range(lineNumber - 1, 0, lineNumber - 1, 1) - : vscode.window.activeTextEditor?.selection; -} - -export function rangeString(range: vscode.Range | undefined) { - if (!range) { - return ''; - } - let hash = `#L${range.start.line + 1}`; - if (range.start.line !== range.end.line) { - hash += `-L${range.end.line + 1}`; - } - return hash; -} - -export function notebookCellRangeString(index: number | undefined, range: vscode.Range | undefined) { - if (index === undefined) { - return ''; - } - - if (!range) { - return `#C${index + 1}`; - } - - let hash = `#C${index + 1}:L${range.start.line + 1}`; - if (range.start.line !== range.end.line) { - hash += `-L${range.end.line + 1}`; - } - return hash; -} - -export function encodeURIComponentExceptSlashes(path: string) { - // There may be special characters like # and whitespace in the path. - // These characters are not escaped by encodeURI(), so it is not sufficient to - // feed the full URI to encodeURI(). - // Additonally, if we feed the full path into encodeURIComponent(), - // this will also encode the path separators, leading to an invalid path. - // Therefore, split on the path separator and encode each segment individually. - return path.split('/').map((segment) => encodeURIComponent(segment)).join('/'); -} - -export async function getLink(gitAPI: GitAPI, useSelection: boolean, shouldEnsurePublished: boolean, hostPrefix?: string, linkType: 'permalink' | 'headlink' = 'permalink', context?: LinkContext, useRange?: boolean): Promise { - hostPrefix = hostPrefix ?? 'https://github.com'; - const fileAndPosition = getFileAndPosition(context); - const fileUri = fileAndPosition?.uri; - - // Use the first repo if we cannot determine a repo from the uri. - const githubRepository = gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo)); - const gitRepo = (fileUri ? getRepositoryForFile(gitAPI, fileUri) : githubRepository) ?? githubRepository; - if (!gitRepo) { - return; - } - - if (shouldEnsurePublished && fileUri) { - await ensurePublished(gitRepo, fileUri); - } - - let repo: { owner: string; repo: string } | undefined; - gitRepo.state.remotes.find(remote => { - if (remote.fetchUrl) { - const foundRepo = getRepositoryFromUrl(remote.fetchUrl); - if (foundRepo && (remote.name === gitRepo.state.HEAD?.upstream?.remote)) { - repo = foundRepo; - return; - } else if (foundRepo && !repo) { - repo = foundRepo; - } - } - return; - }); - if (!repo) { - return; - } - - const blobSegment = gitRepo.state.HEAD ? (`/blob/${linkType === 'headlink' && gitRepo.state.HEAD.name ? encodeURIComponentExceptSlashes(gitRepo.state.HEAD.name) : gitRepo.state.HEAD?.commit}`) : ''; - const uriWithoutFileSegments = `${hostPrefix}/${repo.owner}/${repo.repo}${blobSegment}`; - if (!fileUri) { - return uriWithoutFileSegments; - } - - const encodedFilePath = encodeURIComponentExceptSlashes(fileUri.path.substring(gitRepo.rootUri.path.length)); - const fileSegments = fileAndPosition.type === LinkType.File - ? (useSelection ? `${encodedFilePath}${useRange ? rangeString(fileAndPosition.range) : ''}` : '') - : (useSelection ? `${encodedFilePath}${useRange ? notebookCellRangeString(fileAndPosition.cellIndex, fileAndPosition.range) : ''}` : ''); - - return `${uriWithoutFileSegments}${fileSegments}`; -} - -export function getBranchLink(url: string, branch: string, hostPrefix: string = 'https://github.com') { - const repo = getRepositoryFromUrl(url); - if (!repo) { - throw new Error('Invalid repository URL provided'); - } - - branch = encodeURIComponentExceptSlashes(branch); - return `${hostPrefix}/${repo.owner}/${repo.repo}/tree/${branch}`; -} - -export function getVscodeDevHost(): string { - return `https://${vscode.env.appName.toLowerCase().includes('insiders') ? 'insiders.' : ''}vscode.dev/github`; -} - -export async function ensurePublished(repository: Repository, file: vscode.Uri) { - await repository.status(); - - if ((repository.state.HEAD?.type === RefType.Head || repository.state.HEAD?.type === RefType.Tag) - // If HEAD is not published, make sure it is - && !repository?.state.HEAD?.upstream - ) { - const publishBranch = vscode.l10n.t('Publish Branch & Copy Link'); - const selection = await vscode.window.showInformationMessage( - vscode.l10n.t('The current branch is not published to the remote. Would you like to publish your branch before copying a link?'), - { modal: true }, - publishBranch - ); - if (selection !== publishBranch) { - throw new vscode.CancellationError(); - } - - await vscode.commands.executeCommand('git.publish'); - } - - const uncommittedChanges = [...repository.state.workingTreeChanges, ...repository.state.indexChanges]; - if (uncommittedChanges.find((c) => c.uri.toString() === file.toString()) && !repository.state.HEAD?.ahead && !repository.state.HEAD?.behind) { - const commitChanges = vscode.l10n.t('Commit Changes'); - const copyAnyway = vscode.l10n.t('Copy Anyway'); - const selection = await vscode.window.showWarningMessage( - vscode.l10n.t('The current file has uncommitted changes. Please commit your changes before copying a link.'), - { modal: true }, - commitChanges, - copyAnyway - ); - - if (selection !== copyAnyway) { - // Focus the SCM view - vscode.commands.executeCommand('workbench.view.scm'); - throw new vscode.CancellationError(); - } - } else if (repository.state.HEAD?.ahead) { - const pushCommits = vscode.l10n.t('Push Commits & Copy Link'); - const selection = await vscode.window.showInformationMessage( - vscode.l10n.t('The current branch has unpublished commits. Would you like to push your commits before copying a link?'), - { modal: true }, - pushCommits - ); - if (selection !== pushCommits) { - throw new vscode.CancellationError(); - } - - await repository.push(); - } else if (repository.state.HEAD?.behind) { - const pull = vscode.l10n.t('Pull Changes & Copy Link'); - const selection = await vscode.window.showInformationMessage( - vscode.l10n.t('The current branch is not up to date. Would you like to pull before copying a link?'), - { modal: true }, - pull - ); - if (selection !== pull) { - throw new vscode.CancellationError(); - } - - await repository.pull(); - } - - await repository.status(); -} diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts deleted file mode 100644 index dee8898d..00000000 --- a/extensions/github/src/publish.ts +++ /dev/null @@ -1,224 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { API as GitAPI, Repository } from './typings/git'; -import { getOctokit } from './auth'; -import { TextEncoder } from 'util'; -import { basename } from 'path'; -import { Octokit } from '@octokit/rest'; -import { isInCodespaces } from './pushErrorHandler'; - -function sanitizeRepositoryName(value: string): string { - return value.trim().replace(/[^a-z0-9_.]/ig, '-'); -} - -function getPick(quickpick: vscode.QuickPick): Promise { - return Promise.race([ - new Promise(c => quickpick.onDidAccept(() => quickpick.selectedItems.length > 0 && c(quickpick.selectedItems[0]))), - new Promise(c => quickpick.onDidHide(() => c(undefined))) - ]); -} - -export async function publishRepository(gitAPI: GitAPI, repository?: Repository): Promise { - if (!vscode.workspace.workspaceFolders?.length) { - return; - } - - let folder: vscode.Uri; - - if (repository) { - folder = repository.rootUri; - } else if (gitAPI.repositories.length === 1) { - repository = gitAPI.repositories[0]; - folder = repository.rootUri; - } else if (vscode.workspace.workspaceFolders.length === 1) { - folder = vscode.workspace.workspaceFolders[0].uri; - } else { - const picks = vscode.workspace.workspaceFolders.map(folder => ({ label: folder.name, folder })); - const placeHolder = vscode.l10n.t('Pick a folder to publish to GitHub'); - const pick = await vscode.window.showQuickPick(picks, { placeHolder }); - - if (!pick) { - return; - } - - folder = pick.folder.uri; - } - - let quickpick = vscode.window.createQuickPick(); - quickpick.ignoreFocusOut = true; - - quickpick.placeholder = 'Repository Name'; - quickpick.value = basename(folder.fsPath); - quickpick.show(); - quickpick.busy = true; - - let owner: string; - let octokit: Octokit; - try { - octokit = await getOctokit(); - const user = await octokit.users.getAuthenticated({}); - owner = user.data.login; - } catch (e) { - // User has cancelled sign in - quickpick.dispose(); - return; - } - - quickpick.busy = false; - - let repo: string | undefined; - let isPrivate: boolean; - - const onDidChangeValue = async () => { - const sanitizedRepo = sanitizeRepositoryName(quickpick.value); - - if (!sanitizedRepo) { - quickpick.items = []; - } else { - quickpick.items = [ - { label: `$(repo) Publish to GitHub private repository`, description: `$(github) ${owner}/${sanitizedRepo}`, alwaysShow: true, repo: sanitizedRepo, isPrivate: true }, - { label: `$(repo) Publish to GitHub public repository`, description: `$(github) ${owner}/${sanitizedRepo}`, alwaysShow: true, repo: sanitizedRepo, isPrivate: false }, - ]; - } - }; - - onDidChangeValue(); - - while (true) { - const listener = quickpick.onDidChangeValue(onDidChangeValue); - const pick = await getPick(quickpick); - listener.dispose(); - - repo = pick?.repo; - isPrivate = pick?.isPrivate ?? true; - - if (repo) { - try { - quickpick.busy = true; - await octokit.repos.get({ owner, repo: repo }); - quickpick.items = [{ label: `$(error) GitHub repository already exists`, description: `$(github) ${owner}/${repo}`, alwaysShow: true }]; - } catch { - break; - } finally { - quickpick.busy = false; - } - } - } - - quickpick.dispose(); - - if (!repo) { - return; - } - - if (!repository) { - const gitignore = vscode.Uri.joinPath(folder, '.gitignore'); - let shouldGenerateGitignore = false; - - try { - await vscode.workspace.fs.stat(gitignore); - } catch (err) { - shouldGenerateGitignore = true; - } - - if (shouldGenerateGitignore) { - quickpick = vscode.window.createQuickPick(); - quickpick.placeholder = vscode.l10n.t('Select which files should be included in the repository.'); - quickpick.canSelectMany = true; - quickpick.show(); - - try { - quickpick.busy = true; - - const children = (await vscode.workspace.fs.readDirectory(folder)) - .map(([name]) => name) - .filter(name => name !== '.git'); - - quickpick.items = children.map(name => ({ label: name })); - quickpick.selectedItems = quickpick.items; - quickpick.busy = false; - - const result = await Promise.race([ - new Promise(c => quickpick.onDidAccept(() => c(quickpick.selectedItems))), - new Promise(c => quickpick.onDidHide(() => c(undefined))) - ]); - - if (!result || result.length === 0) { - return; - } - - const ignored = new Set(children); - result.forEach(c => ignored.delete(c.label)); - - if (ignored.size > 0) { - const raw = [...ignored].map(i => `/${i}`).join('\n'); - const encoder = new TextEncoder(); - await vscode.workspace.fs.writeFile(gitignore, encoder.encode(raw)); - } - } finally { - quickpick.dispose(); - } - } - } - - const githubRepository = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: false, title: 'Publish to GitHub' }, async progress => { - progress.report({ - message: isPrivate - ? vscode.l10n.t('Publishing to a private GitHub repository') - : vscode.l10n.t('Publishing to a public GitHub repository'), - increment: 25 - }); - - type CreateRepositoryResponseData = Awaited>['data']; - let createdGithubRepository: CreateRepositoryResponseData | undefined = undefined; - - if (isInCodespaces()) { - createdGithubRepository = await vscode.commands.executeCommand('github.codespaces.publish', { name: repo!, isPrivate }); - } else { - const res = await octokit.repos.createForAuthenticatedUser({ - name: repo!, - private: isPrivate - }); - createdGithubRepository = res.data; - } - - if (createdGithubRepository) { - progress.report({ message: vscode.l10n.t('Creating first commit'), increment: 25 }); - - if (!repository) { - repository = await gitAPI.init(folder, { defaultBranch: createdGithubRepository.default_branch }) || undefined; - - if (!repository) { - return; - } - - await repository.commit('first commit', { all: true, postCommitCommand: null }); - } - - progress.report({ message: vscode.l10n.t('Uploading files'), increment: 25 }); - - const branch = await repository.getBranch('HEAD'); - const protocol = vscode.workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); - const remoteUrl = protocol === 'https' ? createdGithubRepository.clone_url : createdGithubRepository.ssh_url; - await repository.addRemote('origin', remoteUrl); - await repository.push('origin', branch.name, true); - } - - return createdGithubRepository; - }); - - if (!githubRepository) { - return; - } - - const openOnGitHub = vscode.l10n.t('Open on GitHub'); - vscode.window.showInformationMessage(vscode.l10n.t('Successfully published the "{0}" repository to GitHub.', `${owner}/${repo}`), openOnGitHub).then(action => { - if (action === openOnGitHub) { - vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(githubRepository.html_url)); - } - }); -} diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts deleted file mode 100644 index f1702bf1..00000000 --- a/extensions/github/src/pushErrorHandler.ts +++ /dev/null @@ -1,324 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TextDecoder } from 'util'; -import { commands, env, ProgressLocation, Uri, window, workspace, QuickPickOptions, FileType, l10n, Disposable, TextDocumentContentProvider } from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { getOctokit } from './auth'; -import { GitErrorCodes, PushErrorHandler, Remote, Repository } from './typings/git'; -import * as path from 'path'; - -type Awaited = T extends PromiseLike ? Awaited : T; - -export function isInCodespaces(): boolean { - return env.remoteName === 'codespaces'; -} - -const PR_TEMPLATE_FILES = [ - { dir: '.', files: ['pull_request_template.md', 'PULL_REQUEST_TEMPLATE.md'] }, - { dir: 'docs', files: ['pull_request_template.md', 'PULL_REQUEST_TEMPLATE.md'] }, - { dir: '.github', files: ['PULL_REQUEST_TEMPLATE.md', 'PULL_REQUEST_TEMPLATE.md'] } -]; - -const PR_TEMPLATE_DIRECTORY_NAMES = [ - 'PULL_REQUEST_TEMPLATE', - 'docs/PULL_REQUEST_TEMPLATE', - '.github/PULL_REQUEST_TEMPLATE' -]; - -async function assertMarkdownFiles(dir: Uri, files: string[]): Promise { - const dirFiles = await workspace.fs.readDirectory(dir); - return dirFiles - .filter(([name, type]) => Boolean(type & FileType.File) && files.indexOf(name) !== -1) - .map(([name]) => Uri.joinPath(dir, name)); -} - -async function findMarkdownFilesInDir(uri: Uri): Promise { - const files = await workspace.fs.readDirectory(uri); - return files - .filter(([name, type]) => Boolean(type & FileType.File) && path.extname(name) === '.md') - .map(([name]) => Uri.joinPath(uri, name)); -} - -/** - * PR templates can be: - * - In the root, `docs`, or `.github` folders, called `pull_request_template.md` or `PULL_REQUEST_TEMPLATE.md` - * - Or, in a `PULL_REQUEST_TEMPLATE` directory directly below the root, `docs`, or `.github` folders, called `*.md` - * - * NOTE This method is a modified copy of a method with same name at microsoft/vscode-pull-request-github repository: - * https://github.com/microsoft/vscode-pull-request-github/blob/0a0c3c6c21c0b9c2f4d5ffbc3f8c6a825472e9e6/src/github/folderRepositoryManager.ts#L1061 - * - */ -export async function findPullRequestTemplates(repositoryRootUri: Uri): Promise { - const results = await Promise.allSettled([ - ...PR_TEMPLATE_FILES.map(x => assertMarkdownFiles(Uri.joinPath(repositoryRootUri, x.dir), x.files)), - ...PR_TEMPLATE_DIRECTORY_NAMES.map(x => findMarkdownFilesInDir(Uri.joinPath(repositoryRootUri, x))) - ]); - - return results.flatMap(x => x.status === 'fulfilled' && x.value || []); -} - -export async function pickPullRequestTemplate(repositoryRootUri: Uri, templates: Uri[]): Promise { - const quickPickItemFromUri = (x: Uri) => ({ label: path.relative(repositoryRootUri.path, x.path), template: x }); - const quickPickItems = [ - { - label: l10n.t('No template'), - picked: true, - template: undefined, - }, - ...templates.map(quickPickItemFromUri) - ]; - const quickPickOptions: QuickPickOptions = { - placeHolder: l10n.t('Select the Pull Request template'), - ignoreFocusOut: true - }; - const pickedTemplate = await window.showQuickPick(quickPickItems, quickPickOptions); - return pickedTemplate?.template; -} - -class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider { - - private items = new Map(); - - set(uri: Uri, contents: string): void { - this.items.set(uri.path, contents); - } - - delete(uri: Uri): void { - this.items.delete(uri.path); - } - - provideTextDocumentContent(uri: Uri): string | undefined { - return this.items.get(uri.path); - } -} - -export class GithubPushErrorHandler implements PushErrorHandler { - - private disposables: Disposable[] = []; - private commandErrors = new CommandErrorOutputTextDocumentContentProvider(); - - constructor(private readonly telemetryReporter: TelemetryReporter) { - this.disposables.push(workspace.registerTextDocumentContentProvider('github-output', this.commandErrors)); - } - - async handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { stderr: string; gitErrorCode: GitErrorCodes }): Promise { - if (error.gitErrorCode !== GitErrorCodes.PermissionDenied && error.gitErrorCode !== GitErrorCodes.PushRejected) { - return false; - } - - const remoteUrl = remote.pushUrl || (isInCodespaces() ? remote.fetchUrl : undefined); - if (!remoteUrl) { - return false; - } - - const match = /^(?:https:\/\/github\.com\/|git@github\.com:)([^\/]+)\/([^\/.]+)/i.exec(remoteUrl); - if (!match) { - return false; - } - - if (/^:/.test(refspec)) { - return false; - } - - const [, owner, repo] = match; - - if (error.gitErrorCode === GitErrorCodes.PermissionDenied) { - await this.handlePermissionDeniedError(repository, remote, refspec, owner, repo); - - /* __GDPR__ - "pushErrorHandler" : { - "owner": "lszomoru", - "handler": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryReporter.sendTelemetryEvent('pushErrorHandler', { handler: 'PermissionDenied' }); - - return true; - } - - // Push protection - if (/GH009: Secrets detected!/i.test(error.stderr)) { - await this.handlePushProtectionError(owner, repo, error.stderr); - - /* __GDPR__ - "pushErrorHandler" : { - "owner": "lszomoru", - "handler": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryReporter.sendTelemetryEvent('pushErrorHandler', { handler: 'PushRejected.PushProtection' }); - - return true; - } - - /* __GDPR__ - "pushErrorHandler" : { - "owner": "lszomoru", - "handler": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryReporter.sendTelemetryEvent('pushErrorHandler', { handler: 'None' }); - - return false; - } - - private async handlePermissionDeniedError(repository: Repository, remote: Remote, refspec: string, owner: string, repo: string): Promise { - const yes = l10n.t('Create Fork'); - const no = l10n.t('No'); - const askFork = l10n.t('You don\'t have permissions to push to "{0}/{1}" on GitHub. Would you like to create a fork and push to it instead?', owner, repo); - - const answer = await window.showWarningMessage(askFork, { modal: true }, yes, no); - if (answer !== yes) { - return; - } - - const match = /^([^:]*):([^:]*)$/.exec(refspec); - const localName = match ? match[1] : refspec; - let remoteName = match ? match[2] : refspec; - - const [octokit, ghRepository] = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Create GitHub fork') }, async progress => { - progress.report({ message: l10n.t('Forking "{0}/{1}"...', owner, repo), increment: 33 }); - - const octokit = await getOctokit(); - - type CreateForkResponseData = Awaited>['data']; - - // Issue: what if the repo already exists? - let ghRepository: CreateForkResponseData; - try { - if (isInCodespaces()) { - // Call into the codespaces extension to fork the repository - const resp = await commands.executeCommand<{ repository: CreateForkResponseData; ref: string }>('github.codespaces.forkRepository'); - if (!resp) { - throw new Error('Unable to fork respository'); - } - - ghRepository = resp.repository; - - if (resp.ref) { - let ref = resp.ref; - if (ref.startsWith('refs/heads/')) { - ref = ref.substr(11); - } - - remoteName = ref; - } - } else { - const resp = await octokit.repos.createFork({ owner, repo }); - ghRepository = resp.data; - } - } catch (ex) { - console.error(ex); - throw ex; - } - - progress.report({ message: l10n.t('Pushing changes...'), increment: 33 }); - - // Issue: what if there's already an `upstream` repo? - await repository.renameRemote(remote.name, 'upstream'); - - // Issue: what if there's already another `origin` repo? - const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); - const remoteUrl = protocol === 'https' ? ghRepository.clone_url : ghRepository.ssh_url; - await repository.addRemote('origin', remoteUrl); - - try { - await repository.fetch('origin', remoteName); - await repository.setBranchUpstream(localName, `origin/${remoteName}`); - } catch { - // noop - } - - await repository.push('origin', localName, true); - - return [octokit, ghRepository] as const; - }); - - // yield - (async () => { - const openOnGitHub = l10n.t('Open on GitHub'); - const createPR = l10n.t('Create PR'); - const action = await window.showInformationMessage(l10n.t('The fork "{0}" was successfully created on GitHub.', ghRepository.full_name), openOnGitHub, createPR); - - if (action === openOnGitHub) { - await commands.executeCommand('vscode.open', Uri.parse(ghRepository.html_url)); - } else if (action === createPR) { - const pr = await window.withProgress({ location: ProgressLocation.Notification, cancellable: false, title: l10n.t('Creating GitHub Pull Request...') }, async _ => { - let title = `Update ${remoteName}`; - const head = repository.state.HEAD?.name; - - let body: string | undefined; - - if (head) { - const commit = await repository.getCommit(head); - title = commit.message.split('\n')[0]; - body = commit.message.slice(title.length + 1).trim(); - } - - const templates = await findPullRequestTemplates(repository.rootUri); - if (templates.length > 0) { - templates.sort((a, b) => a.path.localeCompare(b.path)); - - const template = await pickPullRequestTemplate(repository.rootUri, templates); - - if (template) { - body = new TextDecoder('utf-8').decode(await workspace.fs.readFile(template)); - } - } - - const { data: pr } = await octokit.pulls.create({ - owner, - repo, - title, - body, - head: `${ghRepository.owner.login}:${remoteName}`, - base: ghRepository.default_branch - }); - - await repository.setConfig(`branch.${localName}.remote`, 'upstream'); - await repository.setConfig(`branch.${localName}.merge`, `refs/heads/${remoteName}`); - await repository.setConfig(`branch.${localName}.github-pr-owner-number`, `${owner}#${repo}#${pr.number}`); - - return pr; - }); - - const openPR = l10n.t('Open PR'); - const action = await window.showInformationMessage(l10n.t('The PR "{0}/{1}#{2}" was successfully created on GitHub.', owner, repo, pr.number), openPR); - - if (action === openPR) { - await commands.executeCommand('vscode.open', Uri.parse(pr.html_url)); - } - } - })(); - } - - private async handlePushProtectionError(owner: string, repo: string, stderr: string): Promise { - // Open command output in an editor - const timestamp = new Date().getTime(); - const uri = Uri.parse(`github-output:/github-error-${timestamp}`); - this.commandErrors.set(uri, stderr); - - try { - const doc = await workspace.openTextDocument(uri); - await window.showTextDocument(doc); - } - finally { - this.commandErrors.set(uri, stderr); - } - - // Show dialog - const learnMore = l10n.t('Learn More'); - const message = l10n.t('Your push to "{0}/{1}" was rejected by GitHub because push protection is enabled and one or more secrets were detected.', owner, repo); - const answer = await window.showWarningMessage(message, { modal: true }, learnMore); - if (answer === learnMore) { - commands.executeCommand('vscode.open', 'https://aka.ms/vscode-github-push-protection'); - } - } - - dispose() { - this.disposables.forEach(d => d.dispose()); - } -} diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts deleted file mode 100644 index 0d8b9340..00000000 --- a/extensions/github/src/remoteSourceProvider.ts +++ /dev/null @@ -1,139 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Uri, env, l10n, workspace } from 'vscode'; -import { RemoteSourceProvider, RemoteSource, RemoteSourceAction } from './typings/git-base'; -import { getOctokit } from './auth'; -import { Octokit } from '@octokit/rest'; -import { getRepositoryFromQuery, getRepositoryFromUrl } from './util'; -import { getBranchLink, getVscodeDevHost } from './links'; - -function asRemoteSource(raw: any): RemoteSource { - const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); - return { - name: `$(github) ${raw.full_name}`, - description: `${raw.stargazers_count > 0 ? `$(star-full) ${raw.stargazers_count}` : '' - }`, - detail: raw.description || undefined, - url: protocol === 'https' ? raw.clone_url : raw.ssh_url - }; -} - -export class GithubRemoteSourceProvider implements RemoteSourceProvider { - - readonly name = 'GitHub'; - readonly icon = 'github'; - readonly supportsQuery = true; - - private userReposCache: RemoteSource[] = []; - - async getRemoteSources(query?: string): Promise { - const octokit = await getOctokit(); - - if (query) { - const repository = getRepositoryFromUrl(query); - - if (repository) { - const raw = await octokit.repos.get(repository); - return [asRemoteSource(raw.data)]; - } - } - - const all = await Promise.all([ - this.getQueryRemoteSources(octokit, query), - this.getUserRemoteSources(octokit, query), - ]); - - const map = new Map(); - - for (const group of all) { - for (const remoteSource of group) { - map.set(remoteSource.name, remoteSource); - } - } - - return [...map.values()]; - } - - private async getUserRemoteSources(octokit: Octokit, query?: string): Promise { - if (!query) { - const user = await octokit.users.getAuthenticated({}); - const username = user.data.login; - const res = await octokit.repos.listForAuthenticatedUser({ username, sort: 'updated', per_page: 100 }); - this.userReposCache = res.data.map(asRemoteSource); - } - - return this.userReposCache; - } - - private async getQueryRemoteSources(octokit: Octokit, query?: string): Promise { - if (!query) { - return []; - } - - const repository = getRepositoryFromQuery(query); - - if (repository) { - query = `user:${repository.owner}+${repository.repo}`; - } - - query += ` fork:true`; - - const raw = await octokit.search.repos({ q: query, sort: 'stars' }); - return raw.data.items.map(asRemoteSource); - } - - async getBranches(url: string): Promise { - const repository = getRepositoryFromUrl(url); - - if (!repository) { - return []; - } - - const octokit = await getOctokit(); - - const branches: string[] = []; - let page = 1; - - while (true) { - const res = await octokit.repos.listBranches({ ...repository, per_page: 100, page }); - - if (res.data.length === 0) { - break; - } - - branches.push(...res.data.map(b => b.name)); - page++; - } - - const repo = await octokit.repos.get(repository); - const defaultBranch = repo.data.default_branch; - - return branches.sort((a, b) => a === defaultBranch ? -1 : b === defaultBranch ? 1 : 0); - } - - async getRemoteSourceActions(url: string): Promise { - const repository = getRepositoryFromUrl(url); - if (!repository) { - return []; - } - - return [{ - label: l10n.t('Open on GitHub'), - icon: 'github', - run(branch: string) { - const link = getBranchLink(url, branch); - env.openExternal(Uri.parse(link)); - } - }, { - label: l10n.t('Checkout on vscode.dev'), - icon: 'globe', - run(branch: string) { - const link = getBranchLink(url, branch, getVscodeDevHost()); - env.openExternal(Uri.parse(link)); - } - }]; - } -} diff --git a/extensions/github/src/remoteSourcePublisher.ts b/extensions/github/src/remoteSourcePublisher.ts deleted file mode 100644 index 2e6a5d88..00000000 --- a/extensions/github/src/remoteSourcePublisher.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { publishRepository } from './publish'; -import { API as GitAPI, RemoteSourcePublisher, Repository } from './typings/git'; - -export class GithubRemoteSourcePublisher implements RemoteSourcePublisher { - readonly name = 'GitHub'; - readonly icon = 'github'; - - constructor(private gitAPI: GitAPI) { } - - publishRepository(repository: Repository): Promise { - return publishRepository(this.gitAPI, repository); - } -} diff --git a/extensions/github/src/shareProviders.ts b/extensions/github/src/shareProviders.ts deleted file mode 100644 index 7aea9c27..00000000 --- a/extensions/github/src/shareProviders.ts +++ /dev/null @@ -1,113 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { API } from './typings/git'; -import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util'; -import { encodeURIComponentExceptSlashes, ensurePublished, getRepositoryForFile, notebookCellRangeString, rangeString } from './links'; - -export class VscodeDevShareProvider implements vscode.ShareProvider, vscode.Disposable { - readonly id: string = 'copyVscodeDevLink'; - readonly label: string = vscode.l10n.t('Copy vscode.dev Link'); - readonly priority: number = 10; - - - private _hasGitHubRepositories: boolean = false; - private set hasGitHubRepositories(value: boolean) { - vscode.commands.executeCommand('setContext', 'github.hasGitHubRepo', value); - this._hasGitHubRepositories = value; - this.ensureShareProviderRegistration(); - } - - private shareProviderRegistration: vscode.Disposable | undefined; - private disposables: vscode.Disposable[] = []; - - constructor(private readonly gitAPI: API) { - this.initializeGitHubRepoContext(); - } - - dispose() { - this.disposables.forEach(d => d.dispose()); - } - - private initializeGitHubRepoContext() { - if (this.gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo))) { - this.hasGitHubRepositories = true; - vscode.commands.executeCommand('setContext', 'github.hasGitHubRepo', true); - } else { - this.disposables.push(this.gitAPI.onDidOpenRepository(async e => { - await e.status(); - if (repositoryHasGitHubRemote(e)) { - vscode.commands.executeCommand('setContext', 'github.hasGitHubRepo', true); - this.hasGitHubRepositories = true; - } - })); - } - this.disposables.push(this.gitAPI.onDidCloseRepository(() => { - if (!this.gitAPI.repositories.find(repo => repositoryHasGitHubRemote(repo))) { - this.hasGitHubRepositories = false; - } - })); - } - - private ensureShareProviderRegistration() { - if (vscode.env.appHost !== 'codespaces' && !this.shareProviderRegistration && this._hasGitHubRepositories) { - const shareProviderRegistration = vscode.window.registerShareProvider({ scheme: 'file' }, this); - this.shareProviderRegistration = shareProviderRegistration; - this.disposables.push(shareProviderRegistration); - } else if (this.shareProviderRegistration && !this._hasGitHubRepositories) { - this.shareProviderRegistration.dispose(); - this.shareProviderRegistration = undefined; - } - } - - async provideShare(item: vscode.ShareableItem, _token: vscode.CancellationToken): Promise { - const repository = getRepositoryForFile(this.gitAPI, item.resourceUri); - if (!repository) { - return; - } - - await ensurePublished(repository, item.resourceUri); - - let repo: { owner: string; repo: string } | undefined; - repository.state.remotes.find(remote => { - if (remote.fetchUrl) { - const foundRepo = getRepositoryFromUrl(remote.fetchUrl); - if (foundRepo && (remote.name === repository.state.HEAD?.upstream?.remote)) { - repo = foundRepo; - return; - } else if (foundRepo && !repo) { - repo = foundRepo; - } - } - return; - }); - - if (!repo) { - return; - } - - const blobSegment = repository?.state.HEAD?.name ? encodeURIComponentExceptSlashes(repository.state.HEAD?.name) : repository?.state.HEAD?.commit; - const filepathSegment = encodeURIComponentExceptSlashes(item.resourceUri.path.substring(repository?.rootUri.path.length)); - const rangeSegment = getRangeSegment(item); - return vscode.Uri.parse(`${this.getVscodeDevHost()}/${repo.owner}/${repo.repo}/blob/${blobSegment}${filepathSegment}${rangeSegment}`); - - } - - private getVscodeDevHost(): string { - return `https://${vscode.env.appName.toLowerCase().includes('insiders') ? 'insiders.' : ''}vscode.dev/github`; - } -} - -function getRangeSegment(item: vscode.ShareableItem) { - if (item.resourceUri.scheme === 'vscode-notebook-cell') { - const notebookEditor = vscode.window.visibleNotebookEditors.find(editor => editor.notebook.uri.fsPath === item.resourceUri.fsPath); - const cell = notebookEditor?.notebook.getCells().find(cell => cell.document.uri.fragment === item.resourceUri?.fragment); - const cellIndex = cell?.index ?? notebookEditor?.selection.start; - return notebookCellRangeString(cellIndex, item.selection); - } - - return rangeString(item.selection); -} diff --git a/extensions/github/src/test/github.test.ts b/extensions/github/src/test/github.test.ts deleted file mode 100644 index 2fc5fbd2..00000000 --- a/extensions/github/src/test/github.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'mocha'; -import * as assert from 'assert'; -import { workspace, extensions, Uri, commands } from 'vscode'; -import { findPullRequestTemplates, pickPullRequestTemplate } from '../pushErrorHandler'; - -suite('github smoke test', function () { - const cwd = workspace.workspaceFolders![0].uri; - - suiteSetup(async function () { - const ext = extensions.getExtension('vscode.github'); - await ext?.activate(); - }); - - test('should find all templates', async function () { - const expectedValuesSorted = [ - 'PULL_REQUEST_TEMPLATE/a.md', - 'PULL_REQUEST_TEMPLATE/b.md', - 'docs/PULL_REQUEST_TEMPLATE.md', - 'docs/PULL_REQUEST_TEMPLATE/a.md', - 'docs/PULL_REQUEST_TEMPLATE/b.md', - '.github/PULL_REQUEST_TEMPLATE.md', - '.github/PULL_REQUEST_TEMPLATE/a.md', - '.github/PULL_REQUEST_TEMPLATE/b.md', - 'PULL_REQUEST_TEMPLATE.md' - ]; - expectedValuesSorted.sort(); - - const uris = await findPullRequestTemplates(cwd); - - const urisSorted = uris.map(x => x.path.slice(cwd.path.length)); - urisSorted.sort(); - - assert.deepStrictEqual(urisSorted, expectedValuesSorted); - }); - - test('selecting non-default quick-pick item should correspond to a template', async () => { - const template0 = Uri.file("some-imaginary-template-0"); - const template1 = Uri.file("some-imaginary-template-1"); - const templates = [template0, template1]; - - const pick = pickPullRequestTemplate(Uri.file("/"), templates); - - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - - assert.ok(await pick === template0); - }); - - test('selecting first quick-pick item should return undefined', async () => { - const templates = [Uri.file("some-imaginary-file")]; - - const pick = pickPullRequestTemplate(Uri.file("/"), templates); - - await commands.executeCommand('workbench.action.quickOpenSelectNext'); - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - - assert.ok(await pick === undefined); - }); -}); diff --git a/extensions/github/src/test/index.ts b/extensions/github/src/test/index.ts deleted file mode 100644 index 52c5acf8..00000000 --- a/extensions/github/src/test/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as testRunner from '../../../../test/integration/electron/testrunner'; - -const suite = 'Github Tests'; - -const options: import('mocha').MochaOptions = { - ui: 'tdd', - color: true, - timeout: 60000 -}; - -if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { - options.reporter = 'mocha-multi-reporters'; - options.reporterOptions = { - reporterEnabled: 'spec, mocha-junit-reporter', - mochaJunitReporterReporterOptions: { - testsuitesTitle: `${suite} ${process.platform}`, - mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) - } - }; -} - -testRunner.configure(options); - -export = testRunner; diff --git a/extensions/github/src/typings/git-base.d.ts b/extensions/github/src/typings/git-base.d.ts deleted file mode 100644 index 53cac4d5..00000000 --- a/extensions/github/src/typings/git-base.d.ts +++ /dev/null @@ -1,85 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable, Event, ProviderResult, Uri } from 'vscode'; -export { ProviderResult } from 'vscode'; - -export interface API { - registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; - pickRemoteSource(options: PickRemoteSourceOptions): Promise; -} - -export interface GitBaseExtension { - - readonly enabled: boolean; - readonly onDidChangeEnablement: Event; - - /** - * Returns a specific API version. - * - * Throws error if git-base extension is disabled. You can listed to the - * [GitBaseExtension.onDidChangeEnablement](#GitBaseExtension.onDidChangeEnablement) - * event to know when the extension becomes enabled/disabled. - * - * @param version Version number. - * @returns API instance - */ - getAPI(version: 1): API; -} - -export interface PickRemoteSourceOptions { - readonly providerLabel?: (provider: RemoteSourceProvider) => string; - readonly urlLabel?: string | ((url: string) => string); - readonly providerName?: string; - readonly title?: string; - readonly placeholder?: string; - readonly branch?: boolean; // then result is PickRemoteSourceResult - readonly showRecentSources?: boolean; -} - -export interface PickRemoteSourceResult { - readonly url: string; - readonly branch?: string; -} - -export interface RemoteSourceAction { - readonly label: string; - /** - * Codicon name - */ - readonly icon: string; - run(branch: string): void; -} - -export interface RemoteSource { - readonly name: string; - readonly description?: string; - readonly detail?: string; - /** - * Codicon name - */ - readonly icon?: string; - readonly url: string | string[]; -} - -export interface RecentRemoteSource extends RemoteSource { - readonly timestamp: number; -} - -export interface RemoteSourceProvider { - readonly name: string; - /** - * Codicon name - */ - readonly icon?: string; - readonly label?: string; - readonly placeholder?: string; - readonly supportsQuery?: boolean; - - getBranches?(url: string): ProviderResult; - getRemoteSourceActions?(url: string): ProviderResult; - getRecentRemoteSources?(query?: string): ProviderResult; - getRemoteSources(query?: string): ProviderResult; -} diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts deleted file mode 100644 index 7ac67937..00000000 --- a/extensions/github/src/typings/git.d.ts +++ /dev/null @@ -1,376 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Uri, Event, Disposable, ProviderResult, Command } from 'vscode'; -export { ProviderResult } from 'vscode'; - -export interface Git { - readonly path: string; -} - -export interface InputBox { - value: string; -} - -export const enum ForcePushMode { - Force, - ForceWithLease -} - -export const enum RefType { - Head, - RemoteHead, - Tag -} - -export interface Ref { - readonly type: RefType; - readonly name?: string; - readonly commit?: string; - readonly remote?: string; -} - -export interface UpstreamRef { - readonly remote: string; - readonly name: string; -} - -export interface Branch extends Ref { - readonly upstream?: UpstreamRef; - readonly ahead?: number; - readonly behind?: number; -} - -export interface Commit { - readonly hash: string; - readonly message: string; - readonly parents: string[]; - readonly authorDate?: Date; - readonly authorName?: string; - readonly authorEmail?: string; - readonly commitDate?: Date; -} - -export interface Submodule { - readonly name: string; - readonly path: string; - readonly url: string; -} - -export interface Remote { - readonly name: string; - readonly fetchUrl?: string; - readonly pushUrl?: string; - readonly isReadOnly: boolean; -} - -export const enum Status { - INDEX_MODIFIED, - INDEX_ADDED, - INDEX_DELETED, - INDEX_RENAMED, - INDEX_COPIED, - - MODIFIED, - DELETED, - UNTRACKED, - IGNORED, - INTENT_TO_ADD, - INTENT_TO_RENAME, - TYPE_CHANGED, - - ADDED_BY_US, - ADDED_BY_THEM, - DELETED_BY_US, - DELETED_BY_THEM, - BOTH_ADDED, - BOTH_DELETED, - BOTH_MODIFIED -} - -export interface Change { - - /** - * Returns either `originalUri` or `renameUri`, depending - * on whether this change is a rename change. When - * in doubt always use `uri` over the other two alternatives. - */ - readonly uri: Uri; - readonly originalUri: Uri; - readonly renameUri: Uri | undefined; - readonly status: Status; -} - -export interface RepositoryState { - readonly HEAD: Branch | undefined; - readonly refs: Ref[]; - readonly remotes: Remote[]; - readonly submodules: Submodule[]; - readonly rebaseCommit: Commit | undefined; - - readonly mergeChanges: Change[]; - readonly indexChanges: Change[]; - readonly workingTreeChanges: Change[]; - - readonly onDidChange: Event; -} - -export interface RepositoryUIState { - readonly selected: boolean; - readonly onDidChange: Event; -} - -/** - * Log options. - */ -export interface LogOptions { - /** Max number of log entries to retrieve. If not specified, the default is 32. */ - readonly maxEntries?: number; - readonly path?: string; -} - -export interface CommitOptions { - all?: boolean | 'tracked'; - amend?: boolean; - signoff?: boolean; - signCommit?: boolean; - empty?: boolean; - noVerify?: boolean; - requireUserConfig?: boolean; - useEditor?: boolean; - verbose?: boolean; - /** - * string - execute the specified command after the commit operation - * undefined - execute the command specified in git.postCommitCommand - * after the commit operation - * null - do not execute any command after the commit operation - */ - postCommitCommand?: string | null; -} - -export interface FetchOptions { - remote?: string; - ref?: string; - all?: boolean; - prune?: boolean; - depth?: number; -} - -export interface InitOptions { - defaultBranch?: string; -} - -export interface BranchQuery { - readonly remote?: boolean; - readonly pattern?: string; - readonly count?: number; - readonly contains?: string; -} - -export interface Repository { - - readonly rootUri: Uri; - readonly inputBox: InputBox; - readonly state: RepositoryState; - readonly ui: RepositoryUIState; - - getConfigs(): Promise<{ key: string; value: string; }[]>; - getConfig(key: string): Promise; - setConfig(key: string, value: string): Promise; - getGlobalConfig(key: string): Promise; - - getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; - detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>; - buffer(ref: string, path: string): Promise; - show(ref: string, path: string): Promise; - getCommit(ref: string): Promise; - - add(paths: string[]): Promise; - revert(paths: string[]): Promise; - clean(paths: string[]): Promise; - - apply(patch: string, reverse?: boolean): Promise; - diff(cached?: boolean): Promise; - diffWithHEAD(): Promise; - diffWithHEAD(path: string): Promise; - diffWith(ref: string): Promise; - diffWith(ref: string, path: string): Promise; - diffIndexWithHEAD(): Promise; - diffIndexWithHEAD(path: string): Promise; - diffIndexWith(ref: string): Promise; - diffIndexWith(ref: string, path: string): Promise; - diffBlobs(object1: string, object2: string): Promise; - diffBetween(ref1: string, ref2: string): Promise; - diffBetween(ref1: string, ref2: string, path: string): Promise; - - hashObject(data: string): Promise; - - createBranch(name: string, checkout: boolean, ref?: string): Promise; - deleteBranch(name: string, force?: boolean): Promise; - getBranch(name: string): Promise; - getBranches(query: BranchQuery): Promise; - setBranchUpstream(name: string, upstream: string): Promise; - - getMergeBase(ref1: string, ref2: string): Promise; - - tag(name: string, upstream: string): Promise; - deleteTag(name: string): Promise; - - status(): Promise; - checkout(treeish: string): Promise; - - addRemote(name: string, url: string): Promise; - removeRemote(name: string): Promise; - renameRemote(name: string, newName: string): Promise; - - fetch(options?: FetchOptions): Promise; - fetch(remote?: string, ref?: string, depth?: number): Promise; - pull(unshallow?: boolean): Promise; - push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise; - - blame(path: string): Promise; - log(options?: LogOptions): Promise; - - commit(message: string, opts?: CommitOptions): Promise; -} - -export interface RemoteSource { - readonly name: string; - readonly description?: string; - readonly url: string | string[]; -} - -export interface RemoteSourceProvider { - readonly name: string; - readonly icon?: string; // codicon name - readonly supportsQuery?: boolean; - getRemoteSources(query?: string): ProviderResult; - getBranches?(url: string): ProviderResult; - publishRepository?(repository: Repository): Promise; -} - -export interface RemoteSourcePublisher { - readonly name: string; - readonly icon?: string; // codicon name - publishRepository(repository: Repository): Promise; -} - -export interface Credentials { - readonly username: string; - readonly password: string; -} - -export interface CredentialsProvider { - getCredentials(host: Uri): ProviderResult; -} - -export interface PostCommitCommandsProvider { - getCommands(repository: Repository): Command[]; -} - -export interface PushErrorHandler { - handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise; -} - -export interface BranchProtection { - readonly remote: string; - readonly rules: BranchProtectionRule[]; -} - -export interface BranchProtectionRule { - readonly include?: string[]; - readonly exclude?: string[]; -} - -export interface BranchProtectionProvider { - onDidChangeBranchProtection: Event; - provideBranchProtection(): BranchProtection[]; -} - -export type APIState = 'uninitialized' | 'initialized'; - -export interface PublishEvent { - repository: Repository; - branch?: string; -} - -export interface API { - readonly state: APIState; - readonly onDidChangeState: Event; - readonly onDidPublish: Event; - readonly git: Git; - readonly repositories: Repository[]; - readonly onDidOpenRepository: Event; - readonly onDidCloseRepository: Event; - - toGitUri(uri: Uri, ref: string): Uri; - getRepository(uri: Uri): Repository | null; - init(root: Uri, options?: InitOptions): Promise; - openRepository(root: Uri): Promise - - registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable; - registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; - registerCredentialsProvider(provider: CredentialsProvider): Disposable; - registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; - registerPushErrorHandler(handler: PushErrorHandler): Disposable; - registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable; -} - -export interface GitExtension { - - readonly enabled: boolean; - readonly onDidChangeEnablement: Event; - - /** - * Returns a specific API version. - * - * Throws error if git extension is disabled. You can listen to the - * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event - * to know when the extension becomes enabled/disabled. - * - * @param version Version number. - * @returns API instance - */ - getAPI(version: 1): API; -} - -export const enum GitErrorCodes { - BadConfigFile = 'BadConfigFile', - AuthenticationFailed = 'AuthenticationFailed', - NoUserNameConfigured = 'NoUserNameConfigured', - NoUserEmailConfigured = 'NoUserEmailConfigured', - NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified', - NotAGitRepository = 'NotAGitRepository', - NotAtRepositoryRoot = 'NotAtRepositoryRoot', - Conflict = 'Conflict', - StashConflict = 'StashConflict', - UnmergedChanges = 'UnmergedChanges', - PushRejected = 'PushRejected', - RemoteConnectionError = 'RemoteConnectionError', - DirtyWorkTree = 'DirtyWorkTree', - CantOpenResource = 'CantOpenResource', - GitNotFound = 'GitNotFound', - CantCreatePipe = 'CantCreatePipe', - PermissionDenied = 'PermissionDenied', - CantAccessRemote = 'CantAccessRemote', - RepositoryNotFound = 'RepositoryNotFound', - RepositoryIsLocked = 'RepositoryIsLocked', - BranchNotFullyMerged = 'BranchNotFullyMerged', - NoRemoteReference = 'NoRemoteReference', - InvalidBranchName = 'InvalidBranchName', - BranchAlreadyExists = 'BranchAlreadyExists', - NoLocalChanges = 'NoLocalChanges', - NoStashFound = 'NoStashFound', - LocalChangesOverwritten = 'LocalChangesOverwritten', - NoUpstreamBranch = 'NoUpstreamBranch', - IsInSubmodule = 'IsInSubmodule', - WrongCase = 'WrongCase', - CantLockRef = 'CantLockRef', - CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', - PatchDoesNotApply = 'PatchDoesNotApply', - NoPathFound = 'NoPathFound', - UnknownPath = 'UnknownPath', - EmptyCommitMessage = 'EmptyCommitMessage' -} diff --git a/extensions/github/src/typings/ref.d.ts b/extensions/github/src/typings/ref.d.ts deleted file mode 100644 index 578bc1d0..00000000 --- a/extensions/github/src/typings/ref.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'tunnel'; diff --git a/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts b/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts deleted file mode 100644 index 84ee5997..00000000 --- a/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/180582 - - export namespace workspace { - /** - * - * @param scheme The URI scheme that this provider can provide canonical URIs for. - * A canonical URI represents the conversion of a resource's alias into a source of truth URI. - * Multiple aliases may convert to the same source of truth URI. - * @param provider A provider which can convert URIs of scheme @param scheme to - * a canonical URI which is stable across machines. - */ - export function registerCanonicalUriProvider(scheme: string, provider: CanonicalUriProvider): Disposable; - - /** - * - * @param uri The URI to provide a canonical URI for. - * @param token A cancellation token for the request. - */ - export function getCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriProvider { - /** - * - * @param uri The URI to provide a canonical URI for. - * @param options Options that the provider should honor in the URI it returns. - * @param token A cancellation token for the request. - * @returns The canonical URI for the requested URI or undefined if no canonical URI can be provided. - */ - provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriRequestOptions { - /** - * - * The desired scheme of the canonical URI. - */ - targetScheme: string; - } -} diff --git a/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts b/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts deleted file mode 100644 index 6470557c..00000000 --- a/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// https://github.com/microsoft/vscode/issues/176316 - -declare module 'vscode' { - export interface TreeItem { - shareableItem?: ShareableItem; - } - - export interface ShareableItem { - resourceUri: Uri; - selection?: Range; - } - - export interface ShareProvider { - readonly id: string; - readonly label: string; - readonly priority: number; - - provideShare(item: ShareableItem, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerShareProvider(selector: DocumentSelector, provider: ShareProvider): Disposable; - } -} diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts deleted file mode 100644 index 3d8bf4a4..00000000 --- a/extensions/github/src/util.ts +++ /dev/null @@ -1,39 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Repository } from './typings/git'; - -export class DisposableStore { - - private disposables = new Set(); - - add(disposable: vscode.Disposable): void { - this.disposables.add(disposable); - } - - dispose(): void { - for (const disposable of this.disposables) { - disposable.dispose(); - } - - this.disposables.clear(); - } -} - -export function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined { - const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/i.exec(url) - || /^git@github\.com:([^/]+)\/([^/]+?)(\.git)?$/i.exec(url); - return match ? { owner: match[1], repo: match[2] } : undefined; -} - -export function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined { - const match = /^([^/]+)\/([^/]+)$/i.exec(query); - return match ? { owner: match[1], repo: match[2] } : undefined; -} - -export function repositoryHasGitHubRemote(repository: Repository) { - return !!repository.state.remotes.find(remote => remote.fetchUrl ? getRepositoryFromUrl(remote.fetchUrl) : undefined); -} diff --git a/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE.md b/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/a.md b/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/a.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/b.md b/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/b.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/x.txt b/extensions/github/testWorkspace/.github/PULL_REQUEST_TEMPLATE/x.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE.md b/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/a.md b/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/a.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/b.md b/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/b.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/x.txt b/extensions/github/testWorkspace/PULL_REQUEST_TEMPLATE/x.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE.md b/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/a.md b/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/a.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/b.md b/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/b.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/x.txt b/extensions/github/testWorkspace/docs/PULL_REQUEST_TEMPLATE/x.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/some-markdown.md b/extensions/github/testWorkspace/some-markdown.md deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/testWorkspace/x.txt b/extensions/github/testWorkspace/x.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json deleted file mode 100644 index d7aed183..00000000 --- a/extensions/github/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": "./out", - "experimentalDecorators": true, - "typeRoots": [ - "./node_modules/@types" - ] - }, - "include": [ - "src/**/*", - "../../src/vscode-dts/vscode.d.ts" - ] -} diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock deleted file mode 100644 index caf35ace..00000000 --- a/extensions/github/yarn.lock +++ /dev/null @@ -1,319 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@microsoft/1ds-core-js@4.0.3", "@microsoft/1ds-core-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz#c8a92c623745a9595e06558a866658480c33bdf9" - integrity sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/1ds-post-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz#cfcb20bb23fb6215d3f0732f60f5b7df3e624f86" - integrity sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ== - dependencies: - "@microsoft/1ds-core-js" "4.0.3" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-channel-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz#247b6fe2158fad9826cbcdf7304f885766b36624" - integrity sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A== - dependencies: - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-common@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz#c4aa53ba343f5b3c7fbf54cddd3c86a5bdcd95dc" - integrity sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-core-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz#008308b786930d94a1de8a1fbb4af0351b74653e" - integrity sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g== - dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-shims@3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/applicationinsights-web-basic@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz#9a23323276b4a5a0dc6a352e2de5d75e3c16b534" - integrity sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ== - dependencies: - "@microsoft/applicationinsights-channel-js" "3.0.4" - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@nevware21/ts-async@>= 0.3.0 < 2.x": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" - integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== - dependencies: - "@nevware21/ts-utils" ">= 0.10.0 < 2.x" - -"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.10.1 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" - integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== - -"@octokit/auth-token@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.1.tgz#88bc2baf5d706cb258474e722a720a8365dff2ec" - integrity sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA== - dependencies: - "@octokit/types" "^7.0.0" - -"@octokit/core@^4.0.0": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.0.5.tgz#589e68c0a35d2afdcd41dafceab072c2fbc6ab5f" - integrity sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA== - dependencies: - "@octokit/auth-token" "^3.0.0" - "@octokit/graphql" "^5.0.0" - "@octokit/request" "^6.0.0" - "@octokit/request-error" "^3.0.0" - "@octokit/types" "^7.0.0" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^7.0.0": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.1.tgz#cb0d03e62e8762f3c80e52b025179de81899a823" - integrity sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg== - dependencies: - "@octokit/types" "^7.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql-schema@14.4.0": - version "14.4.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql-schema/-/graphql-schema-14.4.0.tgz#9336f64c3103a2e82ee3ce060c3ccf99d177d7f0" - integrity sha512-+O6/dsLlR6V9gv+t1lqsN+x73TLwyQWZpd3M8/eYnuny7VaznV9TAyUxf18tX8WBBS5IqtlLDk1nG+aSTPRZzQ== - dependencies: - graphql "^16.0.0" - graphql-tag "^2.10.3" - -"@octokit/graphql@5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.5.tgz#a4cb3ea73f83b861893a6370ee82abb36e81afd2" - integrity sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ== - dependencies: - "@octokit/request" "^6.0.0" - "@octokit/types" "^9.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.1.tgz#a06982514ad131fb6fbb9da968653b2233fade9b" - integrity sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA== - dependencies: - "@octokit/request" "^6.0.0" - "@octokit/types" "^7.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^13.6.0": - version "13.6.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-13.6.0.tgz#381884008e23fd82fd444553f6b4dcd24a5c4a4d" - integrity sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ== - -"@octokit/openapi-types@^17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-17.1.0.tgz#9a712b5bb9d644940d8a1f24115c798c317a64a5" - integrity sha512-rnI26BAITDZTo5vqFOmA7oX4xRd18rO+gcK4MiTpJmsRMxAw0JmevNjPsjpry1bb9SVNo56P/0kbiyXXa4QluA== - -"@octokit/plugin-paginate-rest@^4.0.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.2.0.tgz#41fc6ca312446a85a4275aca698b4d9c4c5e06ab" - integrity sha512-8otLCIK9esfmOCY14CBnG/xPqv0paf14rc+s9tHpbOpeFwrv5CnECKW1qdqMAT60ngAa9eB1bKQ+l2YCpi0HPQ== - dependencies: - "@octokit/types" "^7.2.0" - -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== - -"@octokit/plugin-rest-endpoint-methods@^6.0.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.4.0.tgz#09584dd4e85fc4fe04ade45620b105af582c20ba" - integrity sha512-YP4eUqZ6vORy/eZOTdil1ZSrMt0kv7i/CVw+HhC2C0yJN+IqTc/rot957JQ7JfyeJD6HZOjLg6Jp1o9cPhI9KA== - dependencies: - "@octokit/types" "^7.2.0" - deprecation "^2.3.1" - -"@octokit/request-error@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.1.tgz#3fd747913c06ab2195e52004a521889dadb4b295" - integrity sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ== - dependencies: - "@octokit/types" "^7.0.0" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^6.0.0": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.1.tgz#3ceeb22dab09a29595d96594b6720fc14495cf4e" - integrity sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ== - dependencies: - "@octokit/endpoint" "^7.0.0" - "@octokit/request-error" "^3.0.0" - "@octokit/types" "^7.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.7" - universal-user-agent "^6.0.0" - -"@octokit/rest@19.0.4": - version "19.0.4" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.4.tgz#fd8bed1cefffa486e9ae46a9dc608ce81bcfcbdd" - integrity sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA== - dependencies: - "@octokit/core" "^4.0.0" - "@octokit/plugin-paginate-rest" "^4.0.0" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^6.0.0" - -"@octokit/types@^7.0.0", "@octokit/types@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-7.2.0.tgz#7ee0fc27f9f463d7ccf12ca5956988d498b3c6c4" - integrity sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw== - dependencies: - "@octokit/openapi-types" "^13.6.0" - -"@octokit/types@^9.0.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.2.0.tgz#0358e3de070b1d43c5a8af63b9951c88a09fc9ed" - integrity sha512-xySzJG4noWrIBFyMu4lg4tu9vAgNg9S0aoLRONhAEz6ueyi1evBzb40HitIosaYS4XOexphG305IVcLrIX/30g== - dependencies: - "@octokit/openapi-types" "^17.1.0" - -"@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== - -"@vscode/extension-telemetry@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz#8c6c61e253ff304f46045f04edd60059b144417a" - integrity sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ== - dependencies: - "@microsoft/1ds-core-js" "^4.0.3" - "@microsoft/1ds-post-js" "^4.0.3" - "@microsoft/applicationinsights-web-basic" "^3.0.4" - -before-after-hook@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" - integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -graphql-tag@^2.10.3: - version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" - integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== - dependencies: - tslib "^2.1.0" - -graphql@^16.0.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" - integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tslib@^2.1.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/extensions/microsoft-authentication/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore deleted file mode 100644 index 46f23a20..00000000 --- a/extensions/microsoft-authentication/.vscodeignore +++ /dev/null @@ -1,14 +0,0 @@ -.vscode/** -.vscode-test/** -out/test/** -out/** -extension.webpack.config.js -extension-browser.webpack.config.js -yarn.lock -src/** -.gitignore -vsc-extension-quickstart.md -**/tsconfig.json -**/tslint.json -**/*.map -**/*.ts diff --git a/extensions/microsoft-authentication/README.md b/extensions/microsoft-authentication/README.md deleted file mode 100644 index 2462c2b3..00000000 --- a/extensions/microsoft-authentication/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Authentication for Visual Studio Code - -**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. - -## Features - -This extension provides support for authenticating to Microsoft. It registers the `microsoft` Authentication Provider that can be leveraged by other extensions. This also provides the Microsoft authentication used by Settings Sync. - -Additionally, it provides the `microsoft-sovereign-cloud` Authentication Provider that can be used to sign in to other Azure clouds like Azure for US Government or Azure China. Use the setting `microsoft-sovereign-cloud.endpoint` to select the authentication endpoint the provider should use. Please note that different scopes may also be required in different environments. diff --git a/extensions/microsoft-authentication/extension-browser.webpack.config.js b/extensions/microsoft-authentication/extension-browser.webpack.config.js deleted file mode 100644 index 2513c7d0..00000000 --- a/extensions/microsoft-authentication/extension-browser.webpack.config.js +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const path = require('path'); -const withBrowserDefaults = require('../shared.webpack.config').browser; - -module.exports = withBrowserDefaults({ - context: __dirname, - node: { - global: true, - __filename: false, - __dirname: false, - }, - entry: { - extension: './src/extension.ts', - }, - resolve: { - alias: { - './node/crypto': path.resolve(__dirname, 'src/browser/crypto'), - './node/authServer': path.resolve(__dirname, 'src/browser/authServer'), - './node/buffer': path.resolve(__dirname, 'src/browser/buffer'), - './node/fetch': path.resolve(__dirname, 'src/browser/fetch'), - } - } -}); diff --git a/extensions/microsoft-authentication/extension.webpack.config.js b/extensions/microsoft-authentication/extension.webpack.config.js deleted file mode 100644 index 45600607..00000000 --- a/extensions/microsoft-authentication/extension.webpack.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - extension: './src/extension.ts' - } -}); diff --git a/extensions/microsoft-authentication/media/auth.css b/extensions/microsoft-authentication/media/auth.css deleted file mode 100644 index 45c42c75..00000000 --- a/extensions/microsoft-authentication/media/auth.css +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -html { - height: 100%; -} - -body { - box-sizing: border-box; - min-height: 100%; - margin: 0; - padding: 15px 30px; - display: flex; - flex-direction: column; - color: white; - font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; - background-color: #2C2C32; -} - -.branding { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAlqADAAQAAAABAAAAlgAAAADkcSUjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAxaElEQVR4Ae19CbgdRbXu6j2cecoECTIkICCGzAg+7qeQ9544QFQgiXpVEJTEe59ALsbMwE5AMZCQELgKeSoqDlyiQogCSUAC6FNCQhIwQMALCbNMGc68p37/v7prnz47++yzzzl76OCu851d3dXV1VXVf69atWqtVZaUg397IPJwSCKT46zgx16wh+19XSYnO6PjE7H4MDuZ7BSxXhQJ/On5KTWbTSMm3bYlvHXmJNxj2SatFLFVioeWn5lDDwAgMvOU2NG/3DMo2h691rLtr4bqBtVX1NVJRUOVBIIiyYSI3RkXO9q5NZmU5S98tu7XpmQF2AwAzCoNwMrAMm/CT3HkrgqJTI/KwodOD4ZDa5MVdUPtfe+KxONKiSob6+SIE46XqsYGK5FIBoLVtQEJiSRaWp+xktaNuzpqfirTLcBOZNJtdnjrDCk6wMrA8hOgWJcZoFSrT4nJogfPESu4Dv8AVLTTEqsC9MkSDHB2NIYfkZGnjJOqpiZQrkTCAmWyKqtDgXBAEs0tLyZtWRl7953/u/uiUR0sVgH2xuKERCJJnhc6lIFV6B7OuXyAJrIpqDzVlQ99TUKVt0scAEomQaXskBAONlEFrgpvLdkZk6r6Wjn6IxMx2qVeY0IsSVrhqnCgMiiJA62v4a5Vkmz/4a7PDWtmVXSIfGMSAGYVFGCpGvGh5VCiHohEAiJXi77sKx+eLZU1N0hHCyuTAJCCDqCAKIUCYxv4AcY6YnL0qeOkZuhQSeoo6b5OG+AC4ZJQRThQFQbAWt7Bvf8ZSNg3P3deA8ZUF2AzADCrMAArA4u9XMoQsQMp6nHlH5dKdcMcad+fJGVCCKRAxfMU1XKB1dYpI8acKI0jj5FkDNSti3LpzSgjCQQmrFA4HKiuIMAO2EnrtkBb+8pdXxr2OjOd+bAd2vQ2cro8mXPjwH/LwBp4H/a/hLvuCsr06cpky6KHfiK1jRdJ235M80ClABMtmMMfKJQDNB4j1VCstg4ZcTKANWpkZmCxAJZiABYIhgO1VRwi21HGj63OxI27pje9xGz5BlgZWOzVUgSPjEqufOheqWmcIq37YwBCWKsDDGlQULnAUh6L4CLFAh/fCmCNOSE7sNxi3IilxiUQCAdrayR+oCWBcn4q0eSy56c2PMc80+6yg2t4MEAKVgYWO7HYwcz8Ig9XSTz5kNQ0nC7tB6KoBmZ+bnAZdaVUhIMHVDxWQtQCYI3thWKZ8rrHLICiCwCslmIKFGn/GoPm9S+cW7ddswJgZw4Ta9NkC/n6HsrA6nufDewOV/Ap8x8cggHvMUztTpLO5igkCV2g4hMIJI3dY56mqBdwgXOlWP0DlhbNkhVgtoQDELza7Z1iJ2J3J2P2DX8/r+Evmsm2A2dukkBfAYbZSDkUrQco+IQ0XeY9eKwEraekovYkzP4OBhUrlGLEXYB1q6QnzXPYLUtuJ5SLcei1k62tMYDKDtTWnRusrf5/J9zbcv9xa5snc9aooLJti3xYbsVy1lEOxekBI01f8NBECVnbJVx1hHS2YSrnGf4y1SQTwAyYTJzpvr6lAWA2AWYlW1vidrQjGait/VS4pvaPx9/bvOn43+4/GwCzDcAoC0P+rKNd1ot9q1s5d489YIa/BQ9+Ap/yAxKsgJSJ8gGXUc94I1BD4Og/j91zDodYGFTmvf88VsYnpiWSt7ICNXWcoQoo2uNJ217298/X/8bky7ZcVKZYppcKE0OaDg0FDn9XPfwFCYc3YEYGUEUpTQeFUNTgyZniDBUyfJdecu9hVJjAYS+YbGuJ8z9QXXNaqL5uzQlrm3d88O79X+Ujt860MIu17ExDZJliFealkMJYMn1NQNZATnXlQ9+ScPXNEm3H02zKrZQK9PpoxQ5+PBJ3pVwqbkBJpFhjPiSNx47sWY7V60Nyy4BaQDQhdqCyJgRWX5LNLbuA8xUfaKz7sTtEkkhhMuBoU5QpVm792rdclKbzkyWoFj64WCrrAKo2SNMxhukSjb4CvoZe/pGBebyfP88ZTNx14KQX6BdV4McQSna2JZItLXGrsurEUEPdra8daPnv43934CIy+Qoqth2hDKx8v4hpkKbrAi++3AUbf4AlmqukvTmuiytgWfr3OBdg/bs533cpwMDgJ6BFEQtUVB4dGlz/kxPuab5TH8S2A1z9bGi+6/o+KY/8FKkUw6KH/ktqm/4Ngk8y6UHltvVCH39IrQx1SsXmoI9l5Tc72xS2Y50AWGtnaEjdF46/p3mtPuJqqmOUQ356YMZtYai8xITrfzsGb4Dg8392W6IZ8FOygImXslwe8KOzF0Dti2D8vZbO0KC6z37wdwfm/d2yvu8dvbPfXr7acw8YcULkrw0Sa34EoBovHZCmq3Jez7dlvwKkGMCoqIHn+E8tQtse5n1UwZn37HXVq0nOeO14sj0Ws44tD4U59FjWLEaaPue+IyXWskMqa11QUfCZQgaK6OtxhqeaIkpInjLUyiRBjJKMhRrrqkOh5IXlodB0S3/ilDR9/cngyx+RcOVg6WjtXZqe67OUUqVnJroQsgu+nTzF/oXUlsIUTIk/WaZY/e18A6r5D34MXbkV0vTBEoVKZ1Zpen8flv2+FCHLnq0YV4N2Zwcwb48pU6z+dDfVXiKnRKGh8HksJt+tIp44penZlmj68yBzD6DjRQ8pmQbwWR4u2aSau0oQg2JRT9EaUqZYfep9vEaKFGhFs2DDN6Si8m594Yk4RQyF+0gVMRlgwyTzb9qRIZu51C02+Uzc7WLaSS55eIvJh1WHwnVGWt0O+VM1eFgMg4dIXBY8OF+qar8HlRd0JciHRRutEoXUy/Q836R5kjIemnwmzpjJTcwlD7O6+crAytaZ5ppK013B5/wNy6W6/goIPkmlMBD1V5puCu8h5gs66GV6E7zHPZRRsmSrgOS7ZI3K84O9Bg/zN/wcuulfVYMHZ+3Mw+Hk+bmmuHT8pPgrZki/aG4qfVymWNneAfmp6Y5TDpm34T6s+306v9L0bA/ntSzAMZdK45qht4qXKVaPPeTM/GISWVcjnZV/xPB3mho8WL1ofPZY4AAuGHmWAZMpKv3cpPsgLlOsTC/BLNHMXn+YRAOPglE/ERoK3a1oMt1XrDQdDsne4YEEl/kv1vOzPcetUxlY6Z3kLNFEZdGG42Gk/hh00w9Xg4fedNPTy8n7OdBjgKRl+5RcudUqy7G8ADDS9DkPnApQbYNjjsOlsz1/SzTeZ/V2bKiQiZk/HUvp572VWcTrfaNYVLddvTUkgyYl5Rk2c1NARtRbUkIHX3nrKyNNn/fgp6BCcL8E0DUxuHShNL3ULzBFqVCR1KwwlZi3LshnQbkDizMkS61i2dkmqDa2zMQpr7tuDc3FQyOmNH0TtD4hTZ97/1ckFLxDEniBCbpvUZMofzQjHdzp5/6oZaoWuQHLMLOrXmgQO34hvugzUMJgfMnvIH5I9rXfgRfTJpqv9P4vU63r7cAYPESmx2X+A7NgQLoCtn68q7BLNL3VK3U9DT1pp6lsPjzoBVj8mqFWSzOfW549B7ZwP4W67RB1fgm7ACh24T84DZTsKlm2/WKZOX499Z1l512OdYoPG5yqkho8qBAoIXMf+K5UNiyAch4axbdXwiWaVAU9BwZQqdHPJCAPD82/55aSHbp17BlY/JrXQLVmOoa/VTtnSqjqVuFI2LI3itkJmX6nCLrICVceIRVVD8iNTy2RK6yrtVGGypWshVke7Bg8kCqJzF2/Gk45LoGMynEf5HGPl6WEIl/yAIlPTjstcmWyP86tW+ZZoWvCo65sbvrblVLVcCuGCFtiUb6MCjQshH/oOquAtQJMbgKzp6TUD7lKVjy9Ua57bJAaaUZ2dnd0kb1KxblKXtAYPMxd/zss0VwibTB4sEGlaF7sl8AXZP61TjhxX1qqirYO2alTPx0c3JHetbGbdq6SmqZLpXUfAEUnAo7NWA8NYLNjkFBXQJj4NpxkTpMrJj6ixgXPTLNTXut6uLkoyTR4WD0zpg5kB7+3EbrpZ6g0veQyqh5abyTuRBSP1dsM8iYwnMBTn91ph4afdKw0jDrWtqForq+oh6KKmWyDoegOLO/MbuXOO6V20BekdS9ngRwyu+ftsaZ2DPIfmI8je7Rtvlwx9vuatdRDo/FJNe/3gzDZg8FD3RjH4KE/SzTsinTy0WOH9OFCWrl8hD4GPwZYSSj/wmzBqm2y7Ff23H7cZ856Lzxk6LfjLc30nFw69R1PKwmsrqGQL57iAg6DK3c+CCadoOIyhnoh8dzXy6EVhvvohMQ7bQyN18mNf1sHUURdSYdGCj6pnDd349H4RmDwUD0AULH5hQBVT+WaZwF0YEYkGA5KKGRZrfsWyS++fHGgtvofYEpArArjpLaXl93jZYd5N9SE4oTkM5vAd0yQln1k0vvLI/HLsZXa1TbCX7n1nNyw43z5zujHBZ7iVLhaYHfQqRYbafrcB8ZhDHkEk5BG6YD7oP63LVV04Q8AKhIxGuZjsAOVBZtx4C1JJKYmb/78Y/r8aKLKrsYR1A0NBAtfryxPcIkuPPbajjeUVTuOhBcUfM11E6Aa4iy4sqb9/4fPJVCvNrhADFd+QCqr/irLd8zSCYFjht3zjDRLvft0SakwdniYd/+ZuG+LhMKNKk03fj77VFiJMiuDjnGwuoG860a4ED1RAKojbttSwxrB7zYGHuc1MS55AF4IGUjLIUO46RksuNp/lYpqmC/Bw1x+mVln1mhB07Ju6AoMjf8iL738FTy3UzhrjIzm8/IfyKjTfdCc+6bC1eYa/Z7jMcfggS33BsPamDjTtfQ0c55eFtO95XiPzT0m5jUGU0Z6XhvyQ3g6ViXVtr2LZeWUiOZH2044YVL0dT3x5w8oFhys2rIeqiEwX2rPN6hMqx0n+G37YuDdpsqoY56V67eNV1DphCHSxeuZO/obU/7GMjn7m/PANyFNX6MC3XiCzvgpJjk4mDQTe3P0lMb0TNd4rzfde+wt1+TzXk8d66gWxUYC8P4f2yvtLWfJjQAV9e7JA7NtPg8haTzsPzD7GyUt72GbMqksYH35PWJoxDBbUTUK85ptsvypb8q3x96mz/TOSPtbCTV4wGvlJGTu+kWwSr7GFwYPfWoP51RoA4e+1n2PSqdMlZs/97aKSCKngOJGUvDrXiyTDQnsfqX4Z7bOCqc6DsHwNRcnYGjsgJQbHVE3+FZZ/vQdcC7u7CGDnar6XQUtI+L4aJpz/01gdK8BTwL5Gx9Egwd2vJ//teUY+kIBsCRBgGqpLPvMGXLzZxxQcVbrNCCti5wFBCfRL+2juMGW0QoseAzRd8D30O2fzUlP6+nc5DWxN583jUMSCm2Dw/y6QV+R007aKUu3nqRrkpxMcDjrS6BQ10jT59z/K6luvAyg4syPQ6wLqr4UWIq8Nih5NYe+Vuk48FmAap72Az8YB1Q9VwrdfNA3w9wm3RybOD2d5yakX2N6+nWTZtLNPeYc17lXC10yp93NcxM8uU1Sj7HJa2JvRm+aHjueelsh1qiqOREzx7/Jsh0X6mSC7gY5NOYSyKRz2xC24zv3rccQ8iXsRYOv2/ECnEsRpc2jQ19ch77Ols0ST5woy6as06GPn5f5YLJV0tu1Jp83zRybuKc83uvm2Bt7jzOVwTTmwT9dGu6ERS+TvDSV58UKFViH5EaNAfB6PwXftVofTD6J4oJsQaXpYGRnr6+V79y/GUs0ZykPR9/lbgN9G7NdNjeoDGLoqw2h3jfJDWefJiumvJbaszBX0VTf6Hu2Hs3bNQwT1m+kgiIRiB1KFSxYZFNLghoGdYMvkRuf3iHXbzlOxQUEV6ahcZorTZ/1wAjoiO3AyzkFwx+c8XORHKjiv5+DLRz6sA8h9nZuOzBdbvjMLK1uLkOfn9vl1g3A2r8CM8I9mEFV4qU4MiXvOzHH3th7zIK85+nH5rynfCYdW2ugnJBK/Ctrx0qw6hm5fvsXFFwcGimxN4HS9DUQfH573YfwPTwFg4fjpJM7PKjmhcnlV3Bx1ucOfe07xI6dJDees0Y4pNPFRy5DX1cLfXsUkCtOxxZjyU/Br9M+fEFUiekClwFFeszmpKeZc+8102xeM9dNbPJ5z538FbrkEghWQOZ1pyx7apUmczcqUi/VTQeoZq37H3g9T8J90FDpgMGDoVR8kLdMf1EuZ8ivqgthdWO1LPv0eFn22ZecoY+yKX+syjivYWC/kLxjFjYba3k37RwHbYQ/gU85qgDS977WMgyhZhL1SGJovFSWPf1ReIs7T2aOe1ULunTt2Vgp+L0EQcSiEF1YWDqiSoluDwKGg++HE0vDexhwmfO+1iYv+S0ubVHUksTQ9zVZfvYdWqzK71SU0L+nOFIv517vB9W/0gZ+l9vHkOSCtyIluHz0yxK1x0GV5GmdoeCVDfwpAyqBogJ82Zg1Vtd/RIKBF8B7nSUX/OJf4drz90qVYjHOBimecKgUY90ShOfuMaJU8B6nEgt+gFePpZlqLCBH25/DBzNaQUVAkXfMhwEK21Wath3ceW5d+PIkxSTPH7tXmk+eiOn6I1Dwo2ZDqcHF2nHxNY79jaskWLleRh33S2irQqVQfVIFHSDh3XHWrpQJLdPGeWMee/9ZbFECJkSY7VbVYcWh+eey7AmAaspz+iETUO4uDgOuSUkpcebad8mKuGCrZFlnh2fKsr/9FtP/86TNVfSDWkbmIoqQylljPEaib0lTExaGwra8/lpQosB9GE1QIOEqPxPW0tSUMa9pwEnqnIkmk3s5/1EUCo8V0E0TiBJmyvJzVusj2Mfs6/d5cCiWaSS/IkqxGWaffD70qWBoMIgyIcq4Uq9Irxfzx3myA5t43JKamoCMHClSXw+aSlkoMpihzwyFhKFJ1/vdPE5ZXdfy3Q5oTqFI6E5BRTvW8SK2lx+voNKPNgLWA338TxC6A4sNphRbjSkw/s8+eaa07fsurFhI2dhhfF2lDWTQoaggQVTpA0eJHHY4XiPOwetrDVNgwrnqiBtApRDl5DOtYP78BQ5vAlCFsSxzl7zcClHClB2iyoYAVARrmf8koWso9DZYFfHwdX0Y9oHTRy/CrOxt8AkrIZJALjpcpm51Xl+I9+m9H3MU05UQxEOHiVRXibwO7SRSrwoOjahbgJkQWE0e8oVztmji9Fkj0wcWMPRVVKiKTtuBy+XGsx0xiVKqyYXlVdlG/vshuN2YGVisoH5dnLWoOOImyJPeAc/wC3QcGGa4xnUMLErfFFaltk5k5CiRN94QaW4GuCBrVPGDCyRvLfkC2Hj+6zEO9Nh9MzzOOWhm3hhXteGOlldBTc/HssxmxzrpGbsYQ182omua21OT3FZrFzBPT+e85u0ak8+km3OVxOGkZ2DxDn7iEXQaxREzx/4SDD1M6m3sEAqO2ewQakrU/O6Pvije7p5785hrvGSu89jk8V7PlMdcN/eS0pih8UgMje++LfI2qoklOLXUZkt1wOcDkFfv1x8cI874Vkw+VgCB2RlMHXmsRejegwGIZ8JYjlon1rtfkhUXtOrQNx1C3CIFVstbtfTHZrtm8qbnST9nvkxp6enMw/+DeSzmTA86Y4Qa8eyT16NHT8UMrRVGCWTqM89uTA3MU7zlmWtMM9fT09Lzm3zmHhOn0vGWafJPkAw9TOQoAIygUb8eyJxi6HE9xXchnflT/ywUQctkunPaleY552HStZgJhi3M+uZBNgVVF4CKSzOR4oGKVTG457FfQm7AYm2pm04d9dknPwFqMB4znjexvkjtg6J9mVk7jUBiIPWqw2zxmFGC2SNqh+oRPBnB5QJIQcRjPXDKMYBzzry/zARVH7Q9EXsL1uEfx1rfUkdZ8dBQG/Y2plDHuQOLNTDgumL03/Gmxkln6y61fPYLuFhHMzSGgfkjj3aYezL1pGgMBjA8V+rlUjoDKr3uZE3l7zoFakHLqDbc0bIRVk0nyopzHtO1Pi4eF8ukras+vj3qG7DYDIKLPNd3xr0lweYJ4C0ehx2iX6T0TkcTXAZIFEccBYBxFqgyL2QheMyQmKJkSNc0FpGJenHoCwWx3hfAWl8EVOosWXnuPh36etPwZJH/ZKEX5r2H3lCeC7PFKyzunv1Ruf6pP0BK/xnIvEAaqFvFt1jqgCpw0OLQWN8gMhLKjG+8JtIK/1ecNbKGJGLpNeU9BCbTnWOagsZgMQOFRGiAWPHpECVsVIuZnR+Gh8PpmflM3F6UwDawnvz3S0Bd+k6xTOW5eG10pOaMPRuLxXCuTyl90hkuTL6Sx0AIlxXDIKpHjRQZMsShXPTa56VcqSHQTdcXBbJnQ2BWSWPR1kelDUPfcoBKVXci9vtFd6oQr6j/wGJtqCNFjUeGOWMuxBLQcixekwriO/KRLwHv0Hj4CPBeRzqgov0qAaT8Fqps+C4m2kkYY0BmEa4KghIvlZXnnCG3nveWZ+hT6LHp5XBwD/RvKPSWo0wrZkMMc6zZGBbfgT+t66DRyY6nkagDPM1Qwh+Ci4FDYwMWsisprcfQ2IGhkYw+KRazONloLIq1vvZWiFa+BJP2dchgybQ1MBYt8dCnjfD/z8AolmkfZ0NXA0JcvpgDt0Wdzd/Al45XFODSD4dG/wQCjOCiAcnRI0WaBrtMPb8D12KmAgvIHa2bpS3+IQUVhz6G94nasLalwD8Dp1imgo5ukSulH/NjuQGUywrdA78JQch7yGLmB8TmeQONCS6CbPgRWDSGu5Z/vAF+CkNfTS1mfXtXya1TL9dHqCXQIaLmwm/DBFJe7znT09PMuYl7upfXGdLLc1K7/7p58/+yOWNUccTYtVhS+QgsaPaBcPE5BJd/gndoHDTYlpHHBeB36oC89drZCirObA8VixkltmldmwkE6Wnm3MSmiEzn6Wkmb3rMfPjPP7D4oK3u0/78Z5FX9jhMsVq551o79/5iRDo0gomvAs818liR8ad1PXVa12H5qA89AKqVf2BxrYwCw5lrPieHD31C2jub5OUXk2CCAw7L5UNwcUxMQKHLCjTAKvsPct2Om5Bkq24aqW859LkH8ggsDB1k3uli59/v/jqMYO9Rvj2AmWFHR0BefglMMhza0LKGMzD/hQDUgRyj2fpBl8l12zdL5K9HpuwBVHTvv0r7tUb5AZb6bVoMYEFL8lv3zINu0o/AsBM9CTCMWAbBHCEOATXB1Y7pvU4WcZk5/PXfZTRLy6Cq6ufke09+VsHFehq1bb++TR/Va+CzQjK4EXe/5MvWLoerSeyXDPdBAVCwpOvFlxQqRHBhJrZnt7M4TOU8MzPzUYe4VaG4gS6FatGetfK97ddjaJyLa47RrB+NIQh8PwTOClGXgVEsgsrIdi5f+zOs+l8Bwahj7UuzJz5E//mDgDVcPX9lt8iB/Y7euj+HRdbWMZqNtiakfvAc8F2PSmTHYV1Do7aE+UobONf2C6jYE6gL33b/gUV+KgWqdX+AT6oLoOnARWhnhwcDKM669JhPRSCPxf/XXhHZS01PHPuqZ7SW5of9E3CNZj8mVbJLrtl+loIr4oojTM4Sxn7CFbuB9enfUKhakpNj8h93VUuyGvsl130UVilw323R94MTCCg9YYygp7hI1RQaOpB6vQEDCA6H1PrkOp03v97kgx/HnpL6V1CbqWgCv7hevrttsSy0Ilq7EgtQ2Wump33QW6kq9B1YpiMvvW8YhJ+PqdO0Ds9+yYoj/Ojam56kHqYHmgRwMaZFzVtvOuAaNtzBlR/B5bSAGqPYGAFbi9QPuVq+t+NfJJqcJpEJ+4QuLrlDWjmkeqBvQ6HZ4eGydcdLAO6DKqpPhI4SVZOp6OeARYHhAsekKZjwo8OiJ+Z16kbRAOLNV3nm5NElO5RhyvJLTHea3MiJ/iRq6v+3VFjPy7VPflxBxRmj2mNqK/7pf3IHltnh4fJ7P4IZ3zZoUg6Hdxp+pQ6oTFfqEOieKKBwzDh17AKLwyH/mR/meLJ/L7QN9jhDIvdB9CtTr0qMNneIiKEPhmH7lEfku9vnOoa+WIw3C9amP4oR+/AbzA1Y7Cxanlz++08CCI9DRbcWPgnIqGeWSnvBpR0L8JiYh/qPH+bTf6RREa8FNoFcAqKtoJ/B5bQFewZhO7141IaD3u9DJLFOZu+o1VUHP26np3Uu3k8vwDLSdCzRzFr7ZciiHgAQsPwBuyobfEW2LyVFokxjCCIee8BkQMV02v8RXNSPenk3zOYxwhpBKm/LFkw9TB6eDySY+03MsjIeu3I6qmTXYM+gwfYuWbLlNLULoHZtUYdGVtD8pzc+vfLefOnXvPem5zN5M6V3v9YzsChNp2Ibpemz7p0l4dpfqOeUJFSPnQ0wvTXo4ViR5FxLgYqnBlxpMTuGSnc02SK4OqBST3AZIwfTnvTYeUL3fk3P05dzlsf8DOa+no45a6QzXe4ZFKr6APjOv8o12y4v6p5BWlHvj6m0idMrb/Jma6S5Zu5Nvyc9vft5ZmDpEk3E0em+7N5rIX1eIdFWzGyVq6bgKfdAqmSCF1wpapUJXHgEh8NXXoILoBa/C1JN6+CyqBOUPI7t9AatxKzxLrn0vkp1bPdPODQeDCxdook4QqVZa2/D7GchZDjcL5mwQH7zFfQh5p0KKjc2YPOCy0vF+Koo5+KzXtkNfwz7HHBpAp/r2wALJdSNGyPUNE6DEuGzsuTJcY49JgTKzpYsvq18PivWHVjdpOlrfwvd9RmuM/787Jes4HIB6QUaW6Tgw4+Czb1IqTyZ+NdeFtn3btKR0pMZ85GhxsFvg5V39wyqHgWB6na5ZutMZSnoaIV9XKrQn2+yP/egfV2NNNJ0xjXDN0ql2S8Z0vR8BgLHK0pgxV0cOY/xnDAflU8tGIu+9aYzWRgCt0UJOF9znMH1bVjOZzt6L8vZM4h9zD2Drt3+LxIf97WUz9d8LWSbBYtcAZBrPm/7+noP8jsUS5XzoEc16+4mqR6+RSrqzkjtl8xCc/lnRUw+7zHTTEgde8Bz0LCIzLys/5oPFjPwkxCQDnn+2bPgYO1fwSTjOrh6G4Ya5pn+jDE0grpS5lXb9FUJP7VTIls+5Kw1gnL5wrDXvJz8xpj1oYFUzrt83dGSCMAZf81Y8FQHO+Pv7bkp0CCj95j3mZdujhl7yZSCCyDSYZCXcBzACwnAKLYGxqLxts2QPxwvd39zo8w/+ddQGDxHqR73Rwa3rMX59kc3RjBD44cw690JvusCHRqppVrKobGAfeaIEy5feyImfNg2pOooSNO7lmgK+GAt2lArnihxYkyAYeijAUYl9pjpaF4lt00/TX70lVfVWJSqwgvH/AE5T8eQ2AEzMw7n7jpdOqJxJWPIlC/XtPQCM92XMQ/ccbt7BtU1/QwiidWai+Kc96H6c0C+xh1W7fWQvzTBNVHxQGX6vhu4lGphjxnsLCrJKDZvmp4yw1KLGVBW9RsBd0oLxv4FIJyAOr+NZRXkx7ZsGviie/tnxvQ8uablcl9PecBv2VDj0D2DBl0i127bLpEnjtU26VIQBNLvkxCQ+v1XQJfqGHxNUEjHGthBHZ7eSQU4d7qTbChcLtLbMPaYSdgnyQ/Py7zHjHGntGDscxhaMHS3/re6asy0XYvBi/eFsQnpwdsscy0930DymHsdvha7fWEhu7J2HDZAfxYAm+bsSYih0fjDMHXoT2yeZervPc+WxmeZ6+bYe6+pS7Y0Nw+1PKfiq+dp1wzRFFC8OA5+Co72MfS1H1gtt5w7Xn54/otZ95gx7pTmjH5T4vvGw2nHFvVbZcDFupsO8B6bjjPXTOxtqzfNHDP2BpPONHOcLY+5tysvvdeg3dgzqKr+Llmy/SbNYvYMMvlzibvK7A4M3ptep2xpJq8pL/3ZmdLT09xzzgpH61INfSyYTMWMsYCDWV4Iin5JqDVfAFDN1PbopAJrlNmCDos2BI+TW2Th2FMByg3ujhrZ78tWZlGvweWTs2cQttNrugx81+MQS3xAh0blu3IbGv04ftIqJTOqC93BxtF+pTrafw6mYqNl1bl36CyJ03AytbkE406JM6xF4z8Jge6vAK6wTgDcvWNzKaaEefhxO0NjVd2pON4li7dOUXDx3ZC37CUwmwnmdZrYpDP2ppljE5t86efp96XnY34TzDFjSh//Bv/kvAYd4aIFGFzgj3vMdGCPmSHbR8stU59z/E4BUI4fiNwr43WntHDcl8G/rJJqgIu+rfwtpfe2EbNG1zKouv5eiWxbqv1Au4JeZo0GDObFegvNdC1bPnNvT/eZdJOPsTfNlM09oddgys7ruVEI5hxIsDj0VYKfAwFvb54Jby4XYg3NWeoYiMtFvgCqqZDaLRp/OcB1JZakKOci3YLDD1Ta7/9JWgbBRq6TlkGD5sjiJx+V2X/2n2VQDu8/IC17V2L42A1xA/z64KUXKujQR5eL2F4tFn0RBHI8+KnVOvSpNkWOQ1+2+tGdEgP5s0Xjr8WOW/8GgS9oozqOKCZFzlbL7NccXjfoWAY1fEzqq3fJVds+4YhZImARXF9k3lL89MGwXqhPQH56UQcsts6CuOE93ahR5UF5rimXXshhco+Z9pY1MrTzJLn5vB3gHypSi7PejhrIsUqzAVLKha4afytEF1NVpyug6hLFocoDqT/vdWR7mDU2O5ZBVdUb5KqtEYey4+Pxqj87n9JAn5i/+wkdBGdJZ9WUFyCQxA6rHS/hCyfDxSWd/AwdOuurgHYEFNw7sMfMLZ+frmrOpCrc17lQgcMqeZNF434LmdhkDDHc8zAEgB8iM0b2P3eOhWVQtD0J58FXy5VPbpBZ25oo83r++TfyqxyQ5/fgLOlwEfrm818Vq3U8KNc2LEI74BrYwwBNDH0sK9b5GmZ9p4FKrdJZTr6Gvt7qZ6T0V47ZBHP5SRCr7JMwpfQ+B5f71euXzaGRE522/di0oP4TUpvcJXM3f/z1mUdAhxvB5jDvv+BUiovQpCA3f+WADN1xCuRJD6oEnIx2/4KjdVCFPWY6WtZJdcuJcvO5m3XoUyZbFQn7V3Jf7zJS+oVjnsIEcTx2Z31FKqAtQUGqUmXi32f/rJjWCY1lHeFgCb/caTYGqnsY+OFHAnO3LGJXBMIWWBkeaU498MOPUyVTE4LLyI++dc+vAa4vYvji0EGpfPe85p70mGAM0CoCIR6dJ/953lI99patCUX+4bBICjbvsUFwB/kIpPRj8KL44fhzSCGgGBh3Axn41aQdsGqHWPZbr99+zLgT3gsNGvrtROuBBF5QrzIvLbPQP6jzwWDxOvr41t2rAK5LIWvijIp5s5FdKi9j6KvlUsVbYECnQpTwmA59o6fZUHArPZtJptfwXv8IboT68Bk6xPgRXClg4cALLvZiEjIVKDraiXBo2PB6qR9xmG1z59kMr7PQGMpYPup7MFCcocqRB91y7mUA1VVYLOVyDypuPCBrS1EmYwam47OiFL2zZSPoG4Y+BVWFOg7xA6hYTYKKlJOU66oJZ0rr/rtTS0CHgpReuxs/jmVQCPywI0JxztlC34SDKZapGgWN02H+RaB963ffxHreDzGzwteC3bNtBSTvZZOgkIehjzxkLBqRH5y7WIswWqmmPD/FNIfnFsUMS7bDYKRxBgSqFEVwKOm5T5i/WKELROxl/W7R06RW+g+KJXZnTIYdNVzqh9ONBprjVUEqVj0zPQd1660TIZB7GIaXkAv9+2+mADy3Y2gcgoVTp6E0dCCgOg5g1pe8WH4wbQNkLQHhHjMEpJ8DBY0RvjK8osg27HvduACCYjNcH0zJi90W1Ayd7DyVkbGtzAiswwAsfBeHELCchhnqM+OuRqkIXoD2noEL8LxvvY3GPCTh2B2yYnq7sx3ITH75bo84t/v210uVl2ybBf5wBfhDNAvrpqXeUUN70O1Gwt0w8OnAOhIUa8ShCiwio7dZXW/X/YsuUOVNDlVevA1uBCp+4Qz5HPd1NlyamucCrA53KDykgcXu5Rc+c3VI9g5KymhspE3m//UR2FptBumw+3mV5j0M+KlGHBHZ+ikM7/dj32sMP7EYKBcFqsUP2YCFncu681igWLQcTx8KWUZPzE62awNtLcru6bEDLfrQvJ+m8CpQ3X4q3tzD0PqogXYt5XjFB1cmYPXIvPtvKCw9k+onCBopfWT8ZkjksLzV+aYadqSk9KgsX3gx/k2/pD/LpPs8LgMr/QUZXfprJ2Bh3sK+123P6/JWIVWK0utgzg2ozPkhNL6UgWVemjemAJU8VwT7XjcMHQ8h8eNqqFEIlaKeyB9ngb0FD/ByyN1baXm9fgh9A3ltd26FRWysnVqcHWJW/OR9kNJ/GoLU4vBcBilGzGBil8/iAhkFpEMpIPXhrLBMsbJBzBhqKLAmcjN17HsNXXpHjdu8+mwl9P9apk9eKRR+0p9szvU6HlnK2G1xGVi9vXqvoUZk4oUA140w8KW2B2hGARfWDThYP++x1tckmNjNo9dK/ONWKdN3UeKa+fTxRtecC+pXbZkPcH0PqsPU5GdX5v8DZakMZgg0cZrkXYfC4Yf7bkkn/x3idMf775eAigBEXGFYcgo3U79EQtXQ7cSCKXX6CxIMunoo3KUOPVwtaXIZWH3qfi5Yu4YakQk/go3A51XafUi4U+pTQweWGSpIZWD1pwtVrwtS+iUT1ko8/nFVJaKtZKENNQyFMnF/6l7Ye2wrCIc62GukDKz+drSR0l8z6TG4s5yIPXbew9BIXXqKI7oYbgOCvsbeepl7vWneY/JfDMqH4bhEMRj2hAXjZxCsp8vAcl5J/34NuK4av1OsTpjPte+GQa5jqNG/ErvuUjDhVbmY6bqQ6cgFU6ZLRUyzEdRnbFIeKANroB1vloAiH31VAlEsAbVsd6T0/bZw8tQoJ1Qhvy8m99ysPZxs2d8mVujnZWB5XmO/D7kEREONyEcPyMkTTsEuFX+ERir2boRdZSGDUrVCPqAPZYMFCNbX84Yluy+qe7MMrD70XdasxlCDAtVrJv4vWP+sgadkDIsElzI9uN0gIdfYPNGlXOnFmMsljdFeLNAHmxorE/v33bv7oqalaK4/rWhL2k8DeThFEcaf1ZKJ0+HY4wegXOS5YAuI31zx5OJI8cib9L5UImpojgdS2QHfS0DFrFA4GKxrrEjs3/9fuy8e9DktdbFaQgz4AeUCvD2g5nOuO6VrJv0fDItL4E4JogjVsOUScm7BYMfEB93FC+b/oIuFTEiAcMYx+wsGahvDyVjnq4nmfReDUn1RH6pGKlayPBQW4hUYO0qamV0z8WpYMV0KhUH0tY4Q+ZfSG/AZnBUmTuDTiFsVNcFgbWPIjna8YLfun1H38uvHgVLdnvJN5rbdF9OJQrxbn5RJf1Yw1IDqzZVPfhEbiP5aN1BLUkG9F0MNBQt+UiBxj71rhdCaHno07QoLt1YIgGA7M9BbGC1zM5Bk+4GnwUIt3XNxw69cKixnPmyHNk121Yvcji9TrMIiEEtA6HAqDV4z8U5I6T+hzmyD3GW9Hx5vCDIGAzZzrIn5/eFwhxITVlVdKFBVF7Q7W59IdLSev/viprF7vt74S4JqEtsFlKWDijUpU6z8vo+eS0sZamyBlN7aBEONejXUsGCoYQDjvVvT8GNAZGaEXoqVUvTLH8VS/snCnmvVDQFa/tjR1sdsO3AdwHS/Wz0LgAptnTmJZkGZaq7ZysDyvsxCHxtwLdoxCiPMn2AgewQc2mb2eJMJWAQV0xHzlXZpkA4YWCiNQ5kdCtQ0WuCf4NmscwOmsd/f8/VBD2u3gDKduUmCmahTpm4rAytTrxQyLWW/uGUoKNejMNQ4CTr13cFF8GggenDgpVY89wLr6BHgsQ7DOjidTff5dbI0DnlhAEqSHdjNNh5fiw3Llu75+pC/IB0q2aqoAHcE3XkovZblp881yVJW+VKuPWDcKV36QqU0qJT+dPiN6AIXX7cGHPA4G7COGiF1AFaffDfQsw55KMsCoBok2bqfT7sTz7l+zzcGbdNHc+uVZzZh8jGZwOtzKAOrz12Wpxu8LgkWbr0XUvopEKimGWp4gEUJmAGZl2L1DVgsAYAKhAO19ZJs3o9j+Zkkg8v3XFL/rLaM9frwmbZulj6AppaBNYDOG/CtpApcAmJYuPUnMNS4SNr2gmO2HP/0TDdgMjEBRmDxkjLvOVEs3pWA92hQqDpJtuxrR7k/hl/AFS9f3PQirolAZCBvI9XURxP7/1MGVv/7Lj93upJqLWzh1qWQ0s/pcqdECT6umKGQxwAV05R5j9L8C8A6vMehUAFlBcNhq7oGgNp/wLKTt8bDVatevbDmNT6TIoOtg15MpvyFMTEPoQysPHTigIugTzG5Gowy9OoXPPEdMPTXwykwAUVqxi2KFUwKMgUWKRYYpWhchn/wGGCxAU5BkLWLeQegrATW8cJWVTUB9Q6YqlsqrI4f/P3iEW+zvpNus8Nb3wAVM6sEA25E9wLKwOreHyU8gycf405pwdavwZ3S7ZIAP5+EOyUbUvoUuFxQxeLY76FSjjj+WC+giK6kFa4IWxVVYMr3vQ5ErgzG965+ceZxyqErhXpjUsEAZTqwDCzTE36JzYxx/tZzYFS2DkpzdMHZiRcF7866L5DjFhJE7IhRx8CNfi135COg7EBFdQig4izvJSwdLU80t/7k1SuOamfTCk2h0ruvDKz0HvHDuRGkLtx2OpistXZlw1C7+T2w3xBWwV9yuKpKhh0xApuIYJ+gZDJgVdUGaMQAQD0HfN2w59Wmnxu5kwOoxaBQEfJbRQtlYBWtq/v4IJdyjbx9b1Pzu3uvxXB4QbCmqT4MnqmiBgYL4Mq4aZ7diQleLPokhKM37P5G453mKTrkzcCyi6OuY5KLFpeBVbSu7seDPLKuCffZw5rfjU6OJ2LjIQyFm2S7E2B7MRBI/OmlbwzdbEpXQPWyjmfyFjL+/4JPu45FLkyEAAAAAElFTkSuQmCC'); - background-size: 24px; - background-repeat: no-repeat; - background-position: left center; - padding-left: 36px; - font-size: 20px; - letter-spacing: -0.04rem; - font-weight: 400; - color: white; - text-decoration: none; -} - -.message-container { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - margin: 0 30px; -} - -.message { - font-weight: 300; - font-size: 1.4rem; -} - -body.error .message { - display: none; -} - -body.error .error-message { - display: block; -} - -.error-message { - display: none; - font-weight: 300; - font-size: 1.3rem; -} - -.error-text { - color: red; - font-size: 1rem; -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); - font-weight: 200 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); - font-weight: 300 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); - font-weight: 400 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); - font-weight: 600 -} - -@font-face { - font-family: 'Segoe UI'; - src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); - src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); - font-weight: 700 -} diff --git a/extensions/microsoft-authentication/media/favicon.ico b/extensions/microsoft-authentication/media/favicon.ico deleted file mode 100644 index 7d1a59f7bdac3916c4461727ee106d2f2e532663..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34494 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 b`vD(nzu?1mM<)H2_TzNYuX9O1qu>7zZLY>L diff --git a/extensions/microsoft-authentication/media/icon.png b/extensions/microsoft-authentication/media/icon.png deleted file mode 100644 index c179f87a7119eed57e5780296b957325e1a93bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3818 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 B3MBvl diff --git a/extensions/microsoft-authentication/media/index.html b/extensions/microsoft-authentication/media/index.html deleted file mode 100644 index 37de4764..00000000 --- a/extensions/microsoft-authentication/media/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - Microsoft Account - Sign In - - - - - - - Visual Studio Code - -
-
- You are signed in now and can close this page. -
-
- An error occurred while signing in: -
-
-
- - - - diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json deleted file mode 100644 index c82eea19..00000000 --- a/extensions/microsoft-authentication/package.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "name": "microsoft-authentication", - "publisher": "vscode", - "license": "MIT", - "displayName": "%displayName%", - "description": "%description%", - "version": "0.0.1", - "engines": { - "vscode": "^1.42.0" - }, - "icon": "media/icon.png", - "categories": [ - "Other" - ], - "activationEvents": [], - "enabledApiProposals": [ - "idToken" - ], - "capabilities": { - "virtualWorkspaces": true, - "untrustedWorkspaces": { - "supported": true - } - }, - "extensionKind": [ - "ui", - "workspace" - ], - "contributes": { - "authentication": [ - { - "label": "Microsoft", - "id": "microsoft" - }, - { - "label": "Microsoft Sovereign Cloud", - "id": "microsoft-sovereign-cloud" - } - ], - "configuration": [ - { - "title": "Microsoft Sovereign Cloud", - "properties": { - "microsoft-sovereign-cloud.environment": { - "type": "string", - "markdownDescription": "%microsoft-sovereign-cloud.environment.description%", - "enum": [ - "ChinaCloud", - "USGovernment", - "custom" - ], - "enumDescriptions": [ - "%microsoft-sovereign-cloud.environment.enumDescriptions.AzureChinaCloud%", - "%microsoft-sovereign-cloud.environment.enumDescriptions.AzureUSGovernment%", - "%microsoft-sovereign-cloud.environment.enumDescriptions.custom%" - ] - }, - "microsoft-sovereign-cloud.customEnvironment": { - "type": "object", - "additionalProperties": true, - "markdownDescription": "%microsoft-sovereign-cloud.customEnvironment.description%", - "properties": { - "name": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.name.description%" - }, - "portalUrl": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.portalUrl.description%" - }, - "managementEndpointUrl": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.managementEndpointUrl.description%" - }, - "resourceManagerEndpointUrl": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.resourceManagerEndpointUrl.description%" - }, - "activeDirectoryEndpointUrl": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.activeDirectoryEndpointUrl.description%" - }, - "activeDirectoryResourceId": { - "type": "string", - "description": "%microsoft-sovereign-cloud.customEnvironment.activeDirectoryResourceId.description%" - } - }, - "required": [ - "name", - "portalUrl", - "managementEndpointUrl", - "resourceManagerEndpointUrl", - "activeDirectoryEndpointUrl", - "activeDirectoryResourceId" - ] - } - } - } - ] - }, - "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", - "main": "./out/extension.js", - "browser": "./dist/browser/extension.js", - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "gulp compile-extension:microsoft-authentication", - "compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none", - "watch": "gulp watch-extension:microsoft-authentication", - "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" - }, - "devDependencies": { - "@types/node": "18.x", - "@types/node-fetch": "^2.5.7", - "@types/randombytes": "^2.0.0", - "@types/sha.js": "^2.4.0", - "@types/uuid": "8.0.0" - }, - "dependencies": { - "node-fetch": "2.6.7", - "@azure/ms-rest-azure-env": "^2.0.0", - "@vscode/extension-telemetry": "^0.9.0" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } -} diff --git a/extensions/microsoft-authentication/package.nls.json b/extensions/microsoft-authentication/package.nls.json deleted file mode 100644 index 14c625dc..00000000 --- a/extensions/microsoft-authentication/package.nls.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "displayName": "Microsoft Account", - "description": "Microsoft authentication provider", - "signIn": "Sign In", - "signOut": "Sign Out", - "microsoft-sovereign-cloud.environment.description": { - "message": "The Sovereign Cloud to use for authentication. If you select `custom`, you must also set the `#microsoft-sovereign-cloud.customEnvironment#` setting.", - "comment": [ - "{Locked='`#microsoft-sovereign-cloud.customEnvironment#`'}", - "The `#microsoft-sovereign-cloud.customEnvironment#` syntax will turn into a link. Do not translate it." - ] - }, - "microsoft-sovereign-cloud.environment.enumDescriptions.AzureChinaCloud": "Azure China", - "microsoft-sovereign-cloud.environment.enumDescriptions.AzureUSGovernment": "Azure US Government", - "microsoft-sovereign-cloud.environment.enumDescriptions.custom": "A custom Microsoft Sovereign Cloud", - "microsoft-sovereign-cloud.customEnvironment.description": { - "message": "The custom configuration for the Sovereign Cloud to use with the Microsoft Sovereign Cloud authentication provider. This along with setting `#microsoft-sovereign-cloud.environment#` to `custom` is required to use this feature.", - "comment": [ - "{Locked='`#microsoft-sovereign-cloud.environment#`'}", - "The `#microsoft-sovereign-cloud.environment#` syntax will turn into a link. Do not translate it." - ] - }, - "microsoft-sovereign-cloud.customEnvironment.name.description": "The name of the custom Sovereign Cloud.", - "microsoft-sovereign-cloud.customEnvironment.portalUrl.description": "The portal URL for the custom Sovereign Cloud.", - "microsoft-sovereign-cloud.customEnvironment.managementEndpointUrl.description": "The management endpoint for the custom Sovereign Cloud.", - "microsoft-sovereign-cloud.customEnvironment.resourceManagerEndpointUrl.description": "The resource manager endpoint for the custom Sovereign Cloud.", - "microsoft-sovereign-cloud.customEnvironment.activeDirectoryEndpointUrl.description": "The Active Directory endpoint for the custom Sovereign Cloud.", - "microsoft-sovereign-cloud.customEnvironment.activeDirectoryResourceId.description": "The Active Directory resource ID for the custom Sovereign Cloud." -} diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts deleted file mode 100644 index 0417defe..00000000 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ /dev/null @@ -1,953 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as path from 'path'; -import { isSupportedEnvironment } from './common/uri'; -import { IntervalTimer, SequencerByKey } from './common/async'; -import { generateCodeChallenge, generateCodeVerifier, randomUUID } from './cryptoUtils'; -import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecretStorage'; -import { LoopbackAuthServer } from './node/authServer'; -import { base64Decode } from './node/buffer'; -import { fetching } from './node/fetch'; -import { UriEventHandler } from './UriEventHandler'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { Environment } from '@azure/ms-rest-azure-env'; - -const redirectUrl = 'https://vscode.dev/redirect'; -const defaultActiveDirectoryEndpointUrl = Environment.AzureCloud.activeDirectoryEndpointUrl; -const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const DEFAULT_TENANT = 'organizations'; -const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad'; -const MSA_PASSTHRU_TID = 'f8cdef31-a31e-4b4a-93e4-5f571e91255a'; - -const enum MicrosoftAccountType { - AAD = 'aad', - MSA = 'msa', - Unknown = 'unknown' -} - -interface IToken { - accessToken?: string; // When unable to refresh due to network problems, the access token becomes undefined - idToken?: string; // depending on the scopes can be either supplied or empty - - expiresIn?: number; // How long access token is valid, in seconds - expiresAt?: number; // UNIX epoch time at which token will expire - refreshToken: string; - - account: { - label: string; - id: string; - type: MicrosoftAccountType; - }; - scope: string; - sessionId: string; // The account id + the scope -} - -export interface IStoredSession { - id: string; - refreshToken: string; - scope: string; // Scopes are alphabetized and joined with a space - account: { - label: string; - id: string; - }; - endpoint: string | undefined; -} - -export interface ITokenResponse { - access_token: string; - expires_in: number; - ext_expires_in: number; - refresh_token: string; - scope: string; - token_type: string; - id_token?: string; -} - -export interface IMicrosoftTokens { - accessToken: string; - idToken?: string; -} - -interface IScopeData { - originalScopes?: string[]; - scopes: string[]; - scopeStr: string; - scopesToSend: string; - clientId: string; - tenant: string; -} - -export const REFRESH_NETWORK_FAILURE = 'Network failure'; - -export class AzureActiveDirectoryService { - // For details on why this is set to 2/3... see https://github.com/microsoft/vscode/issues/133201#issuecomment-966668197 - private static REFRESH_TIMEOUT_MODIFIER = 1000 * 2 / 3; - private static POLLING_CONSTANT = 1000 * 60 * 30; - - private _tokens: IToken[] = []; - private _refreshTimeouts: Map = new Map(); - private _sessionChangeEmitter: vscode.EventEmitter = new vscode.EventEmitter(); - - // Used to keep track of current requests when not using the local server approach. - private _pendingNonces = new Map(); - private _codeExchangePromises = new Map>(); - private _codeVerfifiers = new Map(); - - // Used to keep track of tokens that we need to store but can't because we aren't the focused window. - private _pendingTokensToStore: Map = new Map(); - - // Used to sequence requests to the same scope. - private _sequencer = new SequencerByKey(); - - constructor( - private readonly _logger: vscode.LogOutputChannel, - _context: vscode.ExtensionContext, - private readonly _uriHandler: UriEventHandler, - private readonly _tokenStorage: BetterTokenStorage, - private readonly _telemetryReporter: TelemetryReporter, - private readonly _env: Environment - ) { - _context.subscriptions.push(this._tokenStorage.onDidChangeInOtherWindow((e) => this.checkForUpdates(e))); - _context.subscriptions.push(vscode.window.onDidChangeWindowState(async (e) => e.focused && await this.storePendingTokens())); - - // In the event that a window isn't focused for a long time, we should still try to store the tokens at some point. - const timer = new IntervalTimer(); - timer.cancelAndSet( - () => !vscode.window.state.focused && this.storePendingTokens(), - // 5 hours + random extra 0-30 seconds so that each window doesn't try to store at the same time - (18000000) + Math.floor(Math.random() * 30000)); - _context.subscriptions.push(timer); - } - - public async initialize(): Promise { - this._logger.trace('Reading sessions from secret storage...'); - const sessions = await this._tokenStorage.getAll(item => this.sessionMatchesEndpoint(item)); - this._logger.trace(`Got ${sessions.length} stored sessions`); - - const refreshes = sessions.map(async session => { - this._logger.trace(`[${session.scope}] '${session.id}' Read stored session`); - const scopes = session.scope.split(' '); - const scopeData: IScopeData = { - scopes, - scopeStr: session.scope, - // filter our special scopes - scopesToSend: scopes.filter(s => !s.startsWith('VSCODE_')).join(' '), - clientId: this.getClientId(scopes), - tenant: this.getTenantId(scopes), - }; - try { - await this.refreshToken(session.refreshToken, scopeData, session.id); - } catch (e) { - // If we aren't connected to the internet, then wait and try to refresh again later. - if (e.message === REFRESH_NETWORK_FAILURE) { - this._tokens.push({ - accessToken: undefined, - refreshToken: session.refreshToken, - account: { - ...session.account, - type: MicrosoftAccountType.Unknown - }, - scope: session.scope, - sessionId: session.id - }); - } else { - vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); - this._logger.error(e); - await this.removeSessionByIToken({ - accessToken: undefined, - refreshToken: session.refreshToken, - account: { - ...session.account, - type: MicrosoftAccountType.Unknown - }, - scope: session.scope, - sessionId: session.id - }); - } - } - }); - - const result = await Promise.allSettled(refreshes); - for (const res of result) { - if (res.status === 'rejected') { - this._logger.error(`Failed to initialize stored data: ${res.reason}`); - this.clearSessions(); - break; - } - } - - for (const token of this._tokens) { - /* __GDPR__ - "login" : { - "owner": "TylerLeonhardt", - "comment": "Used to determine the usage of the Microsoft Auth Provider.", - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." }, - "accountType": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what account types are being used." } - } - */ - this._telemetryReporter.sendTelemetryEvent('account', { - // Get rid of guids from telemetry. - scopes: JSON.stringify(token.scope.replace(/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i, '{guid}').split(' ')), - accountType: token.account.type - }); - } - } - - //#region session operations - - public get onDidChangeSessions(): vscode.Event { - return this._sessionChangeEmitter.event; - } - - public getSessions(scopes?: string[]): Promise { - if (!scopes) { - this._logger.info('Getting sessions for all scopes...'); - const sessions = this._tokens.map(token => this.convertToSessionSync(token)); - this._logger.info(`Got ${sessions.length} sessions for all scopes...`); - return Promise.resolve(sessions); - } - - let modifiedScopes = [...scopes]; - if (!modifiedScopes.includes('openid')) { - modifiedScopes.push('openid'); - } - if (!modifiedScopes.includes('email')) { - modifiedScopes.push('email'); - } - if (!modifiedScopes.includes('profile')) { - modifiedScopes.push('profile'); - } - if (!modifiedScopes.includes('offline_access')) { - modifiedScopes.push('offline_access'); - } - modifiedScopes = modifiedScopes.sort(); - - const modifiedScopesStr = modifiedScopes.join(' '); - const clientId = this.getClientId(scopes); - const scopeData: IScopeData = { - clientId, - originalScopes: scopes, - scopes: modifiedScopes, - scopeStr: modifiedScopesStr, - // filter our special scopes - scopesToSend: modifiedScopes.filter(s => !s.startsWith('VSCODE_')).join(' '), - tenant: this.getTenantId(scopes), - }; - - this._logger.trace(`[${scopeData.scopeStr}] Queued getting sessions`); - return this._sequencer.queue(modifiedScopesStr, () => this.doGetSessions(scopeData)); - } - - private async doGetSessions(scopeData: IScopeData): Promise { - this._logger.info(`[${scopeData.scopeStr}] Getting sessions`); - - const matchingTokens = this._tokens.filter(token => token.scope === scopeData.scopeStr); - // If we still don't have a matching token try to get a new token from an existing token by using - // the refreshToken. This is documented here: - // https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#refresh-the-access-token - // "Refresh tokens are valid for all permissions that your client has already received consent for." - if (!matchingTokens.length) { - // Get a token with the correct client id. - const token = scopeData.clientId === DEFAULT_CLIENT_ID - ? this._tokens.find(t => t.refreshToken && !t.scope.includes('VSCODE_CLIENT_ID')) - : this._tokens.find(t => t.refreshToken && t.scope.includes(`VSCODE_CLIENT_ID:${scopeData.clientId}`)); - - if (token) { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Found a matching token with a different scopes '${token.scope}'. Attempting to get a new session using the existing session.`); - try { - const itoken = await this.doRefreshToken(token.refreshToken, scopeData); - this._sessionChangeEmitter.fire({ added: [this.convertToSessionSync(itoken)], removed: [], changed: [] }); - matchingTokens.push(itoken); - } catch (err) { - this._logger.error(`[${scopeData.scopeStr}] Attempted to get a new session using the existing session with scopes '${token.scope}' but it failed due to: ${err.message ?? err}`); - } - } - } - - this._logger.info(`[${scopeData.scopeStr}] Got ${matchingTokens.length} sessions`); - const results = await Promise.allSettled(matchingTokens.map(token => this.convertToSession(token, scopeData))); - return results - .filter(result => result.status === 'fulfilled') - .map(result => (result as PromiseFulfilledResult).value); - } - - public createSession(scopes: string[]): Promise { - let modifiedScopes = [...scopes]; - if (!modifiedScopes.includes('openid')) { - modifiedScopes.push('openid'); - } - if (!modifiedScopes.includes('email')) { - modifiedScopes.push('email'); - } - if (!modifiedScopes.includes('profile')) { - modifiedScopes.push('profile'); - } - if (!modifiedScopes.includes('offline_access')) { - modifiedScopes.push('offline_access'); - } - modifiedScopes = modifiedScopes.sort(); - const scopeData: IScopeData = { - originalScopes: scopes, - scopes: modifiedScopes, - scopeStr: modifiedScopes.join(' '), - // filter our special scopes - scopesToSend: modifiedScopes.filter(s => !s.startsWith('VSCODE_')).join(' '), - clientId: this.getClientId(scopes), - tenant: this.getTenantId(scopes), - }; - - this._logger.trace(`[${scopeData.scopeStr}] Queued creating session`); - return this._sequencer.queue(scopeData.scopeStr, () => this.doCreateSession(scopeData)); - } - - private async doCreateSession(scopeData: IScopeData): Promise { - this._logger.info(`[${scopeData.scopeStr}] Creating session`); - - const runsRemote = vscode.env.remoteName !== undefined; - const runsServerless = vscode.env.remoteName === undefined && vscode.env.uiKind === vscode.UIKind.Web; - - if (runsServerless && this._env.activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl) { - throw new Error('Sign in to non-public clouds is not supported on the web.'); - } - - if (runsRemote || runsServerless) { - return this.createSessionWithoutLocalServer(scopeData); - } - - try { - return await this.createSessionWithLocalServer(scopeData); - } catch (e) { - this._logger.error(`[${scopeData.scopeStr}] Error creating session: ${e}`); - - // If the error was about starting the server, try directly hitting the login endpoint instead - if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') { - return this.createSessionWithoutLocalServer(scopeData); - } - - throw e; - } - } - - private async createSessionWithLocalServer(scopeData: IScopeData) { - this._logger.trace(`[${scopeData.scopeStr}] Starting login flow with local server`); - const codeVerifier = generateCodeVerifier(); - const codeChallenge = await generateCodeChallenge(codeVerifier); - const qs = new URLSearchParams({ - response_type: 'code', - response_mode: 'query', - client_id: scopeData.clientId, - redirect_uri: redirectUrl, - scope: scopeData.scopesToSend, - prompt: 'select_account', - code_challenge_method: 'S256', - code_challenge: codeChallenge, - }).toString(); - const loginUrl = new URL(`${scopeData.tenant}/oauth2/v2.0/authorize?${qs}`, this._env.activeDirectoryEndpointUrl).toString(); - const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl); - await server.start(); - - let codeToExchange; - try { - vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${server.port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); - const { code } = await server.waitForOAuthResponse(); - codeToExchange = code; - } finally { - setTimeout(() => { - void server.stop(); - }, 5000); - } - - const session = await this.exchangeCodeForSession(codeToExchange, codeVerifier, scopeData); - this._logger.trace(`[${scopeData.scopeStr}] '${session.id}' Sending change event for added session`); - this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); - this._logger.info(`[${scopeData.scopeStr}] '${session.id}' session successfully created!`); - return session; - } - - private async createSessionWithoutLocalServer(scopeData: IScopeData): Promise { - this._logger.trace(`[${scopeData.scopeStr}] Starting login flow without local server`); - let callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.microsoft-authentication`)); - const nonce = generateCodeVerifier(); - const callbackQuery = new URLSearchParams(callbackUri.query); - callbackQuery.set('nonce', encodeURIComponent(nonce)); - callbackUri = callbackUri.with({ - query: callbackQuery.toString() - }); - const state = encodeURIComponent(callbackUri.toString(true)); - const codeVerifier = generateCodeVerifier(); - const codeChallenge = await generateCodeChallenge(codeVerifier); - const signInUrl = new URL(`${scopeData.tenant}/oauth2/v2.0/authorize`, this._env.activeDirectoryEndpointUrl); - signInUrl.search = new URLSearchParams({ - response_type: 'code', - client_id: encodeURIComponent(scopeData.clientId), - response_mode: 'query', - redirect_uri: redirectUrl, - state, - scope: scopeData.scopesToSend, - prompt: 'select_account', - code_challenge_method: 'S256', - code_challenge: codeChallenge, - }).toString(); - const uri = vscode.Uri.parse(signInUrl.toString()); - vscode.env.openExternal(uri); - - let inputBox: vscode.InputBox | undefined; - const timeoutPromise = new Promise((_: (value: vscode.AuthenticationSession) => void, reject) => { - const wait = setTimeout(() => { - clearTimeout(wait); - inputBox?.dispose(); - reject('Login timed out.'); - }, 1000 * 60 * 5); - }); - - const existingNonces = this._pendingNonces.get(scopeData.scopeStr) || []; - this._pendingNonces.set(scopeData.scopeStr, [...existingNonces, nonce]); - - // Register a single listener for the URI callback, in case the user starts the login process multiple times - // before completing it. - let existingPromise = this._codeExchangePromises.get(scopeData.scopeStr); - if (!existingPromise) { - if (isSupportedEnvironment(callbackUri)) { - existingPromise = this.handleCodeResponse(scopeData); - } else { - inputBox = vscode.window.createInputBox(); - existingPromise = this.handleCodeInputBox(inputBox, codeVerifier, scopeData); - } - this._codeExchangePromises.set(scopeData.scopeStr, existingPromise); - } - - this._codeVerfifiers.set(nonce, codeVerifier); - - return Promise.race([existingPromise, timeoutPromise]) - .finally(() => { - this._pendingNonces.delete(scopeData.scopeStr); - this._codeExchangePromises.delete(scopeData.scopeStr); - this._codeVerfifiers.delete(nonce); - }); - } - - public async removeSessionById(sessionId: string, writeToDisk: boolean = true): Promise { - const tokenIndex = this._tokens.findIndex(token => token.sessionId === sessionId); - if (tokenIndex === -1) { - this._logger.warn(`'${sessionId}' Session not found to remove`); - return Promise.resolve(undefined); - } - - const token = this._tokens.splice(tokenIndex, 1)[0]; - this._logger.trace(`[${token.scope}] '${sessionId}' Queued removing session`); - return this._sequencer.queue(token.scope, () => this.removeSessionByIToken(token, writeToDisk)); - } - - public async clearSessions() { - this._logger.trace('Logging out of all sessions'); - this._tokens = []; - await this._tokenStorage.deleteAll(item => this.sessionMatchesEndpoint(item)); - - this._refreshTimeouts.forEach(timeout => { - clearTimeout(timeout); - }); - - this._refreshTimeouts.clear(); - this._logger.trace('All sessions logged out'); - } - - private async removeSessionByIToken(token: IToken, writeToDisk: boolean = true): Promise { - this._logger.info(`[${token.scope}] '${token.sessionId}' Logging out of session`); - this.removeSessionTimeout(token.sessionId); - - if (writeToDisk) { - await this._tokenStorage.delete(token.sessionId); - } - - const tokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId); - if (tokenIndex !== -1) { - this._tokens.splice(tokenIndex, 1); - } - - const session = this.convertToSessionSync(token); - this._logger.trace(`[${token.scope}] '${token.sessionId}' Sending change event for session that was removed`); - this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] }); - this._logger.info(`[${token.scope}] '${token.sessionId}' Logged out of session successfully!`); - return session; - } - - //#endregion - - //#region timeout - - private setSessionTimeout(sessionId: string, refreshToken: string, scopeData: IScopeData, timeout: number) { - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId}' Setting refresh timeout for ${timeout} milliseconds`); - this.removeSessionTimeout(sessionId); - this._refreshTimeouts.set(sessionId, setTimeout(async () => { - try { - const refreshedToken = await this.refreshToken(refreshToken, scopeData, sessionId); - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId}' Sending change event for session that was refreshed`); - this._sessionChangeEmitter.fire({ added: [], removed: [], changed: [this.convertToSessionSync(refreshedToken)] }); - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId}' refresh timeout complete`); - } catch (e) { - if (e.message !== REFRESH_NETWORK_FAILURE) { - vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); - await this.removeSessionById(sessionId); - } - } - }, timeout)); - } - - private removeSessionTimeout(sessionId: string): void { - const timeout = this._refreshTimeouts.get(sessionId); - if (timeout) { - clearTimeout(timeout); - this._refreshTimeouts.delete(sessionId); - } - } - - //#endregion - - //#region convert operations - - private convertToTokenSync(json: ITokenResponse, scopeData: IScopeData, existingId?: string): IToken { - let claims = undefined; - this._logger.trace(`[${scopeData.scopeStr}] '${existingId ?? 'new'}' Attempting to parse token response.`); - - try { - if (json.id_token) { - claims = JSON.parse(base64Decode(json.id_token.split('.')[1])); - } else { - this._logger.warn(`[${scopeData.scopeStr}] '${existingId ?? 'new'}' Attempting to parse access_token instead since no id_token was included in the response.`); - claims = JSON.parse(base64Decode(json.access_token.split('.')[1])); - } - } catch (e) { - throw e; - } - - const id = `${claims.tid}/${(claims.oid ?? (claims.altsecid ?? '' + claims.ipd ?? ''))}`; - const sessionId = existingId || `${id}/${randomUUID()}`; - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId}' Token response parsed successfully.`); - return { - expiresIn: json.expires_in, - expiresAt: json.expires_in ? Date.now() + json.expires_in * 1000 : undefined, - accessToken: json.access_token, - idToken: json.id_token, - refreshToken: json.refresh_token, - scope: scopeData.scopeStr, - sessionId, - account: { - label: claims.preferred_username ?? claims.email ?? claims.unique_name ?? 'user@example.com', - id, - type: claims.tid === MSA_TID || claims.tid === MSA_PASSTHRU_TID ? MicrosoftAccountType.MSA : MicrosoftAccountType.AAD - } - }; - } - - /** - * Return a session object without checking for expiry and potentially refreshing. - * @param token The token information. - */ - private convertToSessionSync(token: IToken): vscode.AuthenticationSession { - return { - id: token.sessionId, - accessToken: token.accessToken!, - idToken: token.idToken, - account: token.account, - scopes: token.scope.split(' ') - }; - } - - private async convertToSession(token: IToken, scopeData: IScopeData): Promise { - if (token.accessToken && (!token.expiresAt || token.expiresAt > Date.now())) { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Token available from cache${token.expiresAt ? `, expires in ${token.expiresAt - Date.now()} milliseconds` : ''}.`); - return { - id: token.sessionId, - accessToken: token.accessToken, - idToken: token.idToken, - account: token.account, - scopes: scopeData.originalScopes ?? scopeData.scopes - }; - } - - try { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Token expired or unavailable, trying refresh`); - const refreshedToken = await this.refreshToken(token.refreshToken, scopeData, token.sessionId); - if (refreshedToken.accessToken) { - return { - id: token.sessionId, - accessToken: refreshedToken.accessToken, - idToken: refreshedToken.idToken, - account: token.account, - // We always prefer the original scopes requested since that array is used as a key in the AuthService - scopes: scopeData.originalScopes ?? scopeData.scopes - }; - } else { - throw new Error(); - } - } catch (e) { - throw new Error('Unavailable due to network problems'); - } - } - - //#endregion - - //#region refresh logic - - private refreshToken(refreshToken: string, scopeData: IScopeData, sessionId?: string): Promise { - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId ?? 'new'}' Queued refreshing token`); - return this._sequencer.queue(scopeData.scopeStr, () => this.doRefreshToken(refreshToken, scopeData, sessionId)); - } - - private async doRefreshToken(refreshToken: string, scopeData: IScopeData, sessionId?: string): Promise { - this._logger.trace(`[${scopeData.scopeStr}] '${sessionId ?? 'new'}' Refreshing token`); - const postData = new URLSearchParams({ - refresh_token: refreshToken, - client_id: scopeData.clientId, - grant_type: 'refresh_token', - scope: scopeData.scopesToSend - }).toString(); - - try { - const json = await this.fetchTokenResponse(postData, scopeData); - const token = this.convertToTokenSync(json, scopeData, sessionId); - if (token.expiresIn) { - this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER); - } - this.setToken(token, scopeData); - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Token refresh success`); - return token; - } catch (e) { - if (e.message === REFRESH_NETWORK_FAILURE) { - // We were unable to refresh because of a network failure (i.e. the user lost internet access). - // so set up a timeout to try again later. We only do this if we have a session id to reference later. - if (sessionId) { - this.setSessionTimeout(sessionId, refreshToken, scopeData, AzureActiveDirectoryService.POLLING_CONSTANT); - } - throw e; - } - this._logger.error(`[${scopeData.scopeStr}] '${sessionId ?? 'new'}' Refreshing token failed: ${e.message}`); - throw e; - } - } - - //#endregion - - //#region scope parsers - - private getClientId(scopes: string[]) { - return scopes.reduce((prev, current) => { - if (current.startsWith('VSCODE_CLIENT_ID:')) { - return current.split('VSCODE_CLIENT_ID:')[1]; - } - return prev; - }, undefined) ?? DEFAULT_CLIENT_ID; - } - - private getTenantId(scopes: string[]) { - return scopes.reduce((prev, current) => { - if (current.startsWith('VSCODE_TENANT:')) { - return current.split('VSCODE_TENANT:')[1]; - } - return prev; - }, undefined) ?? DEFAULT_TENANT; - } - - //#endregion - - //#region oauth flow - - private async handleCodeResponse(scopeData: IScopeData): Promise { - let uriEventListener: vscode.Disposable; - return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { - uriEventListener = this._uriHandler.event(async (uri: vscode.Uri) => { - try { - const query = new URLSearchParams(uri.query); - let code = query.get('code'); - let nonce = query.get('nonce'); - if (Array.isArray(code)) { - code = code[0]; - } - if (!code) { - throw new Error('No code included in query'); - } - if (Array.isArray(nonce)) { - nonce = nonce[0]; - } - if (!nonce) { - throw new Error('No nonce included in query'); - } - - const acceptedStates = this._pendingNonces.get(scopeData.scopeStr) || []; - // Workaround double encoding issues of state in web - if (!acceptedStates.includes(nonce) && !acceptedStates.includes(decodeURIComponent(nonce))) { - throw new Error('Nonce does not match.'); - } - - const verifier = this._codeVerfifiers.get(nonce) ?? this._codeVerfifiers.get(decodeURIComponent(nonce)); - if (!verifier) { - throw new Error('No available code verifier'); - } - - const session = await this.exchangeCodeForSession(code, verifier, scopeData); - this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); - this._logger.info(`[${scopeData.scopeStr}] '${session.id}' session successfully created!`); - resolve(session); - } catch (err) { - reject(err); - } - }); - }).then(result => { - uriEventListener.dispose(); - return result; - }).catch(err => { - uriEventListener.dispose(); - throw err; - }); - } - - private async handleCodeInputBox(inputBox: vscode.InputBox, verifier: string, scopeData: IScopeData): Promise { - this._logger.trace(`[${scopeData.scopeStr}] Starting login flow with input box`); - inputBox.ignoreFocusOut = true; - inputBox.title = vscode.l10n.t('Microsoft Authentication'); - inputBox.prompt = vscode.l10n.t('Provide the authorization code to complete the sign in flow.'); - inputBox.placeholder = vscode.l10n.t('Paste authorization code here...'); - return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => { - inputBox.show(); - inputBox.onDidAccept(async () => { - const code = inputBox.value; - if (code) { - inputBox.dispose(); - const session = await this.exchangeCodeForSession(code, verifier, scopeData); - this._logger.trace(`[${scopeData.scopeStr}] '${session.id}' sending session changed event because session was added.`); - this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); - this._logger.trace(`[${scopeData.scopeStr}] '${session.id}' session successfully created!`); - resolve(session); - } - }); - inputBox.onDidHide(() => { - if (!inputBox.value) { - inputBox.dispose(); - reject('Cancelled'); - } - }); - }); - } - - private async exchangeCodeForSession(code: string, codeVerifier: string, scopeData: IScopeData): Promise { - this._logger.trace(`[${scopeData.scopeStr}] Exchanging login code for session`); - let token: IToken | undefined; - try { - const postData = new URLSearchParams({ - grant_type: 'authorization_code', - code: code, - client_id: scopeData.clientId, - scope: scopeData.scopesToSend, - code_verifier: codeVerifier, - redirect_uri: redirectUrl - }).toString(); - - const json = await this.fetchTokenResponse(postData, scopeData); - this._logger.trace(`[${scopeData.scopeStr}] Exchanging code for token succeeded!`); - token = this.convertToTokenSync(json, scopeData); - } catch (e) { - this._logger.error(`[${scopeData.scopeStr}] Error exchanging code for token: ${e}`); - throw e; - } - - if (token.expiresIn) { - this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER); - } - this.setToken(token, scopeData); - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Exchanging login code for session succeeded!`); - return await this.convertToSession(token, scopeData); - } - - private async fetchTokenResponse(postData: string, scopeData: IScopeData): Promise { - let endpointUrl: string; - if (this._env.activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl) { - // If this is for sovereign clouds, don't try using the proxy endpoint, which supports only public cloud - endpointUrl = this._env.activeDirectoryEndpointUrl; - } else { - const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - endpointUrl = proxyEndpoints?.microsoft || this._env.activeDirectoryEndpointUrl; - } - const endpoint = new URL(`${scopeData.tenant}/oauth2/v2.0/token`, endpointUrl); - - let attempts = 0; - while (attempts <= 3) { - attempts++; - let result; - let errorMessage: string | undefined; - try { - result = await fetching(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length.toString() - }, - body: postData - }); - } catch (e) { - errorMessage = e.message ?? e; - } - - if (!result || result.status > 499) { - if (attempts > 3) { - this._logger.error(`[${scopeData.scopeStr}] Fetching token failed: ${result ? await result.text() : errorMessage}`); - break; - } - // Exponential backoff - await new Promise(resolve => setTimeout(resolve, 5 * attempts * attempts * 1000)); - continue; - } else if (!result.ok) { - // For 4XX errors, the user may actually have an expired token or have changed - // their password recently which is throwing a 4XX. For this, we throw an error - // so that the user can be prompted to sign in again. - throw new Error(await result.text()); - } - - return await result.json() as ITokenResponse; - } - - throw new Error(REFRESH_NETWORK_FAILURE); - } - - //#endregion - - //#region storage operations - - private setToken(token: IToken, scopeData: IScopeData): void { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Setting token`); - - const existingTokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId); - if (existingTokenIndex > -1) { - this._tokens.splice(existingTokenIndex, 1, token); - } else { - this._tokens.push(token); - } - - // Don't await because setting the token is only useful for any new windows that open. - void this.storeToken(token, scopeData); - } - - private async storeToken(token: IToken, scopeData: IScopeData): Promise { - if (!vscode.window.state.focused) { - if (this._pendingTokensToStore.has(token.sessionId)) { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Window is not focused, replacing token to be stored`); - } else { - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Window is not focused, pending storage of token`); - } - this._pendingTokensToStore.set(token.sessionId, token); - return; - } - - await this._tokenStorage.store(token.sessionId, { - id: token.sessionId, - refreshToken: token.refreshToken, - scope: token.scope, - account: token.account, - endpoint: this._env.activeDirectoryEndpointUrl, - }); - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Stored token`); - } - - private async storePendingTokens(): Promise { - if (this._pendingTokensToStore.size === 0) { - this._logger.trace('No pending tokens to store'); - return; - } - - const tokens = [...this._pendingTokensToStore.values()]; - this._pendingTokensToStore.clear(); - - this._logger.trace(`Storing ${tokens.length} pending tokens...`); - await Promise.allSettled(tokens.map(async token => { - this._logger.trace(`[${token.scope}] '${token.sessionId}' Storing pending token`); - await this._tokenStorage.store(token.sessionId, { - id: token.sessionId, - refreshToken: token.refreshToken, - scope: token.scope, - account: token.account, - endpoint: this._env.activeDirectoryEndpointUrl, - }); - this._logger.trace(`[${token.scope}] '${token.sessionId}' Stored pending token`); - })); - this._logger.trace('Done storing pending tokens'); - } - - private async checkForUpdates(e: IDidChangeInOtherWindowEvent): Promise { - for (const key of e.added) { - const session = await this._tokenStorage.get(key); - if (!session) { - this._logger.error('session not found that was apparently just added'); - continue; - } - - if (!this.sessionMatchesEndpoint(session)) { - // If the session wasn't made for this login endpoint, ignore this update - continue; - } - - const matchesExisting = this._tokens.some(token => token.scope === session.scope && token.sessionId === session.id); - if (!matchesExisting && session.refreshToken) { - try { - const scopes = session.scope.split(' '); - const scopeData: IScopeData = { - scopes, - scopeStr: session.scope, - // filter our special scopes - scopesToSend: scopes.filter(s => !s.startsWith('VSCODE_')).join(' '), - clientId: this.getClientId(scopes), - tenant: this.getTenantId(scopes), - }; - this._logger.trace(`[${scopeData.scopeStr}] '${session.id}' Session added in another window`); - const token = await this.refreshToken(session.refreshToken, scopeData, session.id); - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Sending change event for session that was added`); - this._sessionChangeEmitter.fire({ added: [this.convertToSessionSync(token)], removed: [], changed: [] }); - this._logger.trace(`[${scopeData.scopeStr}] '${token.sessionId}' Session added in another window added here`); - continue; - } catch (e) { - // Network failures will automatically retry on next poll. - if (e.message !== REFRESH_NETWORK_FAILURE) { - vscode.window.showErrorMessage(vscode.l10n.t('You have been signed out because reading stored authentication information failed.')); - await this.removeSessionById(session.id); - } - continue; - } - } - } - - for (const { value } of e.removed) { - this._logger.trace(`[${value.scope}] '${value.id}' Session removed in another window`); - if (!this.sessionMatchesEndpoint(value)) { - // If the session wasn't made for this login endpoint, ignore this update - this._logger.trace(`[${value.scope}] '${value.id}' Session doesn't match endpoint. Skipping...`); - continue; - } - - await this.removeSessionById(value.id, false); - this._logger.trace(`[${value.scope}] '${value.id}' Session removed in another window removed here`); - } - - // NOTE: We don't need to handle changed sessions because all that really would give us is a new refresh token - // because access tokens are not stored in Secret Storage due to their short lifespan. This new refresh token - // is not useful in this window because we really only care about the lifetime of the _access_ token which we - // are already managing (see usages of `setSessionTimeout`). - // However, in order to minimize the amount of times we store tokens, if a token was stored via another window, - // we cancel any pending token storage operations. - for (const sessionId of e.updated) { - if (this._pendingTokensToStore.delete(sessionId)) { - this._logger.trace(`'${sessionId}' Cancelled pending token storage because token was updated in another window`); - } - } - } - - private sessionMatchesEndpoint(session: IStoredSession): boolean { - // For older sessions with no endpoint set, it can be assumed to be the default endpoint - session.endpoint ||= defaultActiveDirectoryEndpointUrl; - - return session.endpoint === this._env.activeDirectoryEndpointUrl; - } - - //#endregion -} diff --git a/extensions/microsoft-authentication/src/UriEventHandler.ts b/extensions/microsoft-authentication/src/UriEventHandler.ts deleted file mode 100644 index 3dc753af..00000000 --- a/extensions/microsoft-authentication/src/UriEventHandler.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; - -export class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { - public handleUri(uri: vscode.Uri) { - this.fire(uri); - } -} diff --git a/extensions/microsoft-authentication/src/betterSecretStorage.ts b/extensions/microsoft-authentication/src/betterSecretStorage.ts deleted file mode 100644 index 3cc85406..00000000 --- a/extensions/microsoft-authentication/src/betterSecretStorage.ts +++ /dev/null @@ -1,248 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import Logger from './logger'; -import { Event, EventEmitter, ExtensionContext, SecretStorage, SecretStorageChangeEvent } from 'vscode'; - -export interface IDidChangeInOtherWindowEvent { - added: string[]; - updated: string[]; - removed: Array<{ key: string; value: T }>; -} - -export class BetterTokenStorage { - // set before and after _tokensPromise is set so getTokens can handle multiple operations. - private _operationInProgress = false; - // the current state. Don't use this directly and call getTokens() so that you ensure you - // have awaited for all operations. - private _tokensPromise: Promise> = Promise.resolve(new Map()); - - // The vscode SecretStorage instance for this extension. - private readonly _secretStorage: SecretStorage; - - private _didChangeInOtherWindow = new EventEmitter>(); - public onDidChangeInOtherWindow: Event> = this._didChangeInOtherWindow.event; - - /** - * - * @param keylistKey The key in the secret storage that will hold the list of keys associated with this instance of BetterTokenStorage - * @param context the vscode Context used to register disposables and retreive the vscode.SecretStorage for this instance of VS Code - */ - constructor(private keylistKey: string, context: ExtensionContext) { - this._secretStorage = context.secrets; - context.subscriptions.push(context.secrets.onDidChange((e) => this.handleSecretChange(e))); - this.initialize(); - } - - private initialize(): void { - this._operationInProgress = true; - this._tokensPromise = new Promise((resolve, _) => { - this._secretStorage.get(this.keylistKey).then( - keyListStr => { - if (!keyListStr) { - resolve(new Map()); - return; - } - - const keyList: Array = JSON.parse(keyListStr); - // Gather promises that contain key value pairs our of secret storage - const promises = keyList.map(key => new Promise<{ key: string; value: string | undefined }>((res, rej) => { - this._secretStorage.get(key).then((value) => { - res({ key, value }); - }, rej); - })); - Promise.allSettled(promises).then((results => { - const tokens = new Map(); - results.forEach(p => { - if (p.status === 'fulfilled' && p.value.value) { - const secret = this.parseSecret(p.value.value); - tokens.set(p.value.key, secret); - } else if (p.status === 'rejected') { - Logger.error(p.reason); - } else { - Logger.error('Key was not found in SecretStorage.'); - } - }); - resolve(tokens); - })); - }, - err => { - Logger.error(err); - resolve(new Map()); - }); - }); - this._operationInProgress = false; - } - - async get(key: string): Promise { - const tokens = await this.getTokens(); - return tokens.get(key); - } - - async getAll(predicate?: (item: T) => boolean): Promise { - const tokens = await this.getTokens(); - const values = new Array(); - for (const [_, value] of tokens) { - if (!predicate || predicate(value)) { - values.push(value); - } - } - return values; - } - - async store(key: string, value: T): Promise { - const tokens = await this.getTokens(); - - const isAddition = !tokens.has(key); - tokens.set(key, value); - const valueStr = this.serializeSecret(value); - this._operationInProgress = true; - this._tokensPromise = new Promise((resolve, _) => { - const promises = [this._secretStorage.store(key, valueStr)]; - - // if we are adding a secret we need to update the keylist too - if (isAddition) { - promises.push(this.updateKeyList(tokens)); - } - - Promise.allSettled(promises).then(results => { - results.forEach(r => { - if (r.status === 'rejected') { - Logger.error(r.reason); - } - }); - resolve(tokens); - }); - }); - this._operationInProgress = false; - } - - async delete(key: string): Promise { - const tokens = await this.getTokens(); - if (!tokens.has(key)) { - return; - } - tokens.delete(key); - - this._operationInProgress = true; - this._tokensPromise = new Promise((resolve, _) => { - Promise.allSettled([ - this._secretStorage.delete(key), - this.updateKeyList(tokens) - ]).then(results => { - results.forEach(r => { - if (r.status === 'rejected') { - Logger.error(r.reason); - } - }); - resolve(tokens); - }); - }); - this._operationInProgress = false; - } - - async deleteAll(predicate?: (item: T) => boolean): Promise { - const tokens = await this.getTokens(); - const promises = []; - for (const [key, value] of tokens) { - if (!predicate || predicate(value)) { - promises.push(this.delete(key)); - } - } - await Promise.all(promises); - } - - private async updateKeyList(tokens: Map) { - const keyList = []; - for (const [key] of tokens) { - keyList.push(key); - } - - const keyListStr = JSON.stringify(keyList); - await this._secretStorage.store(this.keylistKey, keyListStr); - } - - protected parseSecret(secret: string): T { - return JSON.parse(secret); - } - - protected serializeSecret(secret: T): string { - return JSON.stringify(secret); - } - - // This is the one way to get tokens to ensure all other operations that - // came before you have been processed. - private async getTokens(): Promise> { - let tokens; - do { - tokens = await this._tokensPromise; - } while (this._operationInProgress); - return tokens; - } - - // This is a crucial function that handles whether or not the token has changed in - // a different window of VS Code and sends the necessary event if it has. - // Scenarios this should cover: - // * Added in another window - // * Updated in another window - // * Deleted in another window - // * Added in this window - // * Updated in this window - // * Deleted in this window - private async handleSecretChange(e: SecretStorageChangeEvent) { - const key = e.key; - - // The KeyList is only a list of keys to aid initial start up of VS Code to know which - // Keys are associated with this handler. - if (key === this.keylistKey) { - return; - } - const tokens = await this.getTokens(); - - this._operationInProgress = true; - this._tokensPromise = new Promise((resolve, _) => { - this._secretStorage.get(key).then( - storageSecretStr => { - if (!storageSecretStr) { - // true -> secret was deleted in another window - // false -> secret was deleted in this window - if (tokens.has(key)) { - const value = tokens.get(key)!; - tokens.delete(key); - this._didChangeInOtherWindow.fire({ added: [], updated: [], removed: [{ key, value }] }); - } - return tokens; - } - - const storageSecret = this.parseSecret(storageSecretStr); - const cachedSecret = tokens.get(key); - - if (!cachedSecret) { - // token was added in another window - tokens.set(key, storageSecret); - this._didChangeInOtherWindow.fire({ added: [key], updated: [], removed: [] }); - return tokens; - } - - const cachedSecretStr = this.serializeSecret(cachedSecret); - if (storageSecretStr !== cachedSecretStr) { - // token was updated in another window - tokens.set(key, storageSecret); - this._didChangeInOtherWindow.fire({ added: [], updated: [key], removed: [] }); - } - - // what's in our token cache and what's in storage must be the same - // which means this should cover the last two scenarios of - // Added in this window & Updated in this window. - return tokens; - }, - err => { - Logger.error(err); - return tokens; - }).then(resolve); - }); - this._operationInProgress = false; - } -} diff --git a/extensions/microsoft-authentication/src/browser/authServer.ts b/extensions/microsoft-authentication/src/browser/authServer.ts deleted file mode 100644 index 60b53c71..00000000 --- a/extensions/microsoft-authentication/src/browser/authServer.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function startServer(_: any): any { - throw new Error('Not implemented'); -} - -export function createServer(_: any): any { - throw new Error('Not implemented'); -} diff --git a/extensions/microsoft-authentication/src/browser/buffer.ts b/extensions/microsoft-authentication/src/browser/buffer.ts deleted file mode 100644 index 794bb19f..00000000 --- a/extensions/microsoft-authentication/src/browser/buffer.ts +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function base64Encode(text: string): string { - return btoa(text); -} - -export function base64Decode(text: string): string { - // modification of https://stackoverflow.com/a/38552302 - const replacedCharacters = text.replace(/-/g, '+').replace(/_/g, '/'); - const decodedText = decodeURIComponent(atob(replacedCharacters).split('').map(function (c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); - return decodedText; -} diff --git a/extensions/microsoft-authentication/src/browser/crypto.ts b/extensions/microsoft-authentication/src/browser/crypto.ts deleted file mode 100644 index 37a1b433..00000000 --- a/extensions/microsoft-authentication/src/browser/crypto.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const crypto = globalThis.crypto; diff --git a/extensions/microsoft-authentication/src/browser/fetch.ts b/extensions/microsoft-authentication/src/browser/fetch.ts deleted file mode 100644 index f7f69f1a..00000000 --- a/extensions/microsoft-authentication/src/browser/fetch.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export const fetching = fetch; diff --git a/extensions/microsoft-authentication/src/common/async.ts b/extensions/microsoft-authentication/src/common/async.ts deleted file mode 100644 index 527b5bbb..00000000 --- a/extensions/microsoft-authentication/src/common/async.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from 'vscode'; - -export class SequencerByKey { - - private promiseMap = new Map>(); - - queue(key: TKey, promiseTask: () => Promise): Promise { - const runningPromise = this.promiseMap.get(key) ?? Promise.resolve(); - const newPromise = runningPromise - .catch(() => { }) - .then(promiseTask) - .finally(() => { - if (this.promiseMap.get(key) === newPromise) { - this.promiseMap.delete(key); - } - }); - this.promiseMap.set(key, newPromise); - return newPromise; - } -} - -export class IntervalTimer extends Disposable { - - private _token: any; - - constructor() { - super(() => this.cancel()); - this._token = -1; - } - - cancel(): void { - if (this._token !== -1) { - clearInterval(this._token); - this._token = -1; - } - } - - cancelAndSet(runner: () => void, interval: number): void { - this.cancel(); - this._token = setInterval(() => { - runner(); - }, interval); - } -} diff --git a/extensions/microsoft-authentication/src/common/uri.ts b/extensions/microsoft-authentication/src/common/uri.ts deleted file mode 100644 index 7382cc2f..00000000 --- a/extensions/microsoft-authentication/src/common/uri.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { env, UIKind, Uri } from 'vscode'; - -const LOCALHOST_ADDRESSES = ['localhost', '127.0.0.1', '0:0:0:0:0:0:0:1', '::1']; -function isLocalhost(uri: Uri): boolean { - if (!/^https?$/i.test(uri.scheme)) { - return false; - } - const host = uri.authority.split(':')[0]; - return LOCALHOST_ADDRESSES.indexOf(host) >= 0; -} - -export function isSupportedEnvironment(uri: Uri): boolean { - if (env.uiKind === UIKind.Desktop) { - return true; - } - // local development (localhost:* or 127.0.0.1:*) - if (isLocalhost(uri)) { - return true; - } - // At this point we should only ever see https - if (uri.scheme !== 'https') { - return false; - } - - return ( - // vscode.dev & insiders.vscode.dev - /(?:^|\.)vscode\.dev$/.test(uri.authority) || - // github.dev & codespaces - /(?:^|\.)github\.dev$/.test(uri.authority) || - // github.dev/codespaces local setup (github.localhost) - /(?:^|\.)github\.localhost$/.test(uri.authority) - ); -} diff --git a/extensions/microsoft-authentication/src/cryptoUtils.ts b/extensions/microsoft-authentication/src/cryptoUtils.ts deleted file mode 100644 index 582dae74..00000000 --- a/extensions/microsoft-authentication/src/cryptoUtils.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { base64Encode } from './node/buffer'; -import { crypto } from './node/crypto'; - -export function randomUUID() { - return crypto.randomUUID(); -} - -function dec2hex(dec: number): string { - return ('0' + dec.toString(16)).slice(-2); -} - -export function generateCodeVerifier(): string { - const array = new Uint32Array(56 / 2); - crypto.getRandomValues(array); - return Array.from(array, dec2hex).join(''); -} - -function sha256(plain: string | undefined) { - const encoder = new TextEncoder(); - const data = encoder.encode(plain); - return crypto.subtle.digest('SHA-256', data); -} - -function base64urlencode(a: ArrayBuffer) { - let str = ''; - const bytes = new Uint8Array(a); - const len = bytes.byteLength; - for (let i = 0; i < len; i++) { - str += String.fromCharCode(bytes[i]); - } - return base64Encode(str) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/, ''); -} - -export async function generateCodeChallenge(v: string) { - const hashed = await sha256(v); - const base64encoded = base64urlencode(hashed); - return base64encoded; -} diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts deleted file mode 100644 index 02cfb464..00000000 --- a/extensions/microsoft-authentication/src/extension.ts +++ /dev/null @@ -1,181 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Environment, EnvironmentParameters } from '@azure/ms-rest-azure-env'; -import { AzureActiveDirectoryService, IStoredSession } from './AADHelper'; -import { BetterTokenStorage } from './betterSecretStorage'; -import { UriEventHandler } from './UriEventHandler'; -import TelemetryReporter from '@vscode/extension-telemetry'; - -async function initMicrosoftSovereignCloudAuthProvider(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter, uriHandler: UriEventHandler, tokenStorage: BetterTokenStorage): Promise { - const environment = vscode.workspace.getConfiguration('microsoft-sovereign-cloud').get('environment'); - let authProviderName: string | undefined; - if (!environment) { - return undefined; - } - - if (environment === 'custom') { - const customEnv = vscode.workspace.getConfiguration('microsoft-sovereign-cloud').get('customEnvironment'); - if (!customEnv) { - const res = await vscode.window.showErrorMessage(vscode.l10n.t('You must also specify a custom environment in order to use the custom environment auth provider.'), vscode.l10n.t('Open settings')); - if (res) { - await vscode.commands.executeCommand('workbench.action.openSettingsJson', 'microsoft-sovereign-cloud.customEnvironment'); - } - return undefined; - } - try { - Environment.add(customEnv); - } catch (e) { - const res = await vscode.window.showErrorMessage(vscode.l10n.t('Error validating custom environment setting: {0}', e.message), vscode.l10n.t('Open settings')); - if (res) { - await vscode.commands.executeCommand('workbench.action.openSettings', 'microsoft-sovereign-cloud.customEnvironment'); - } - return undefined; - } - authProviderName = customEnv.name; - } else { - authProviderName = environment; - } - - const env = Environment.get(authProviderName); - if (!env) { - const res = await vscode.window.showErrorMessage(vscode.l10n.t('The environment `{0}` is not a valid environment.', authProviderName), vscode.l10n.t('Open settings')); - return undefined; - } - - const aadService = new AzureActiveDirectoryService( - vscode.window.createOutputChannel(vscode.l10n.t('Microsoft Sovereign Cloud Authentication'), { log: true }), - context, - uriHandler, - tokenStorage, - telemetryReporter, - env); - await aadService.initialize(); - - const disposable = vscode.authentication.registerAuthenticationProvider('microsoft-sovereign-cloud', authProviderName, { - onDidChangeSessions: aadService.onDidChangeSessions, - getSessions: (scopes: string[]) => aadService.getSessions(scopes), - createSession: async (scopes: string[]) => { - try { - /* __GDPR__ - "login" : { - "owner": "TylerLeonhardt", - "comment": "Used to determine the usage of the Microsoft Sovereign Cloud Auth Provider.", - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." } - } - */ - telemetryReporter.sendTelemetryEvent('loginMicrosoftSovereignCloud', { - // Get rid of guids from telemetry. - scopes: JSON.stringify(scopes.map(s => s.replace(/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i, '{guid}'))), - }); - - return await aadService.createSession(scopes); - } catch (e) { - /* __GDPR__ - "loginFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users run into issues with the login flow." } - */ - telemetryReporter.sendTelemetryEvent('loginMicrosoftSovereignCloudFailed'); - - throw e; - } - }, - removeSession: async (id: string) => { - try { - /* __GDPR__ - "logout" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users log out." } - */ - telemetryReporter.sendTelemetryEvent('logoutMicrosoftSovereignCloud'); - - await aadService.removeSessionById(id); - } catch (e) { - /* __GDPR__ - "logoutFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often fail to log out." } - */ - telemetryReporter.sendTelemetryEvent('logoutMicrosoftSovereignCloudFailed'); - } - } - }, { supportsMultipleAccounts: true }); - - context.subscriptions.push(disposable); - return disposable; -} - -export async function activate(context: vscode.ExtensionContext) { - const aiKey: string = context.extension.packageJSON.aiKey; - const telemetryReporter = new TelemetryReporter(aiKey); - - const uriHandler = new UriEventHandler(); - context.subscriptions.push(uriHandler); - context.subscriptions.push(vscode.window.registerUriHandler(uriHandler)); - const betterSecretStorage = new BetterTokenStorage('microsoft.login.keylist', context); - - const loginService = new AzureActiveDirectoryService( - vscode.window.createOutputChannel(vscode.l10n.t('Microsoft Authentication'), { log: true }), - context, - uriHandler, - betterSecretStorage, - telemetryReporter, - Environment.AzureCloud); - await loginService.initialize(); - - context.subscriptions.push(vscode.authentication.registerAuthenticationProvider('microsoft', 'Microsoft', { - onDidChangeSessions: loginService.onDidChangeSessions, - getSessions: (scopes: string[]) => loginService.getSessions(scopes), - createSession: async (scopes: string[]) => { - try { - /* __GDPR__ - "login" : { - "owner": "TylerLeonhardt", - "comment": "Used to determine the usage of the Microsoft Auth Provider.", - "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight", "comment": "Used to determine what scope combinations are being requested." } - } - */ - telemetryReporter.sendTelemetryEvent('login', { - // Get rid of guids from telemetry. - scopes: JSON.stringify(scopes.map(s => s.replace(/[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i, '{guid}'))), - }); - - return await loginService.createSession(scopes); - } catch (e) { - /* __GDPR__ - "loginFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users run into issues with the login flow." } - */ - telemetryReporter.sendTelemetryEvent('loginFailed'); - - throw e; - } - }, - removeSession: async (id: string) => { - try { - /* __GDPR__ - "logout" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users log out." } - */ - telemetryReporter.sendTelemetryEvent('logout'); - - await loginService.removeSessionById(id); - } catch (e) { - /* __GDPR__ - "logoutFailed" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often fail to log out." } - */ - telemetryReporter.sendTelemetryEvent('logoutFailed'); - } - } - }, { supportsMultipleAccounts: true })); - - let microsoftSovereignCloudAuthProviderDisposable = await initMicrosoftSovereignCloudAuthProvider(context, telemetryReporter, uriHandler, betterSecretStorage); - - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(async e => { - if (e.affectsConfiguration('microsoft-sovereign-cloud')) { - microsoftSovereignCloudAuthProviderDisposable?.dispose(); - microsoftSovereignCloudAuthProviderDisposable = await initMicrosoftSovereignCloudAuthProvider(context, telemetryReporter, uriHandler, betterSecretStorage); - } - })); - - return; -} - -// this method is called when your extension is deactivated -export function deactivate() { } diff --git a/extensions/microsoft-authentication/src/logger.ts b/extensions/microsoft-authentication/src/logger.ts deleted file mode 100644 index 38ae6c73..00000000 --- a/extensions/microsoft-authentication/src/logger.ts +++ /dev/null @@ -1,9 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; - -const Logger = vscode.window.createOutputChannel(vscode.l10n.t('Microsoft Authentication'), { log: true }); -export default Logger; diff --git a/extensions/microsoft-authentication/src/node/authServer.ts b/extensions/microsoft-authentication/src/node/authServer.ts deleted file mode 100644 index de08c6fc..00000000 --- a/extensions/microsoft-authentication/src/node/authServer.ts +++ /dev/null @@ -1,198 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as http from 'http'; -import { URL } from 'url'; -import * as fs from 'fs'; -import * as path from 'path'; -import { randomBytes } from 'crypto'; - -function sendFile(res: http.ServerResponse, filepath: string) { - fs.readFile(filepath, (err, body) => { - if (err) { - console.error(err); - res.writeHead(404); - res.end(); - } else { - res.writeHead(200, { - 'content-length': body.length, - }); - res.end(body); - } - }); -} - -interface IOAuthResult { - code: string; - state: string; -} - -interface ILoopbackServer { - /** - * If undefined, the server is not started yet. - */ - port: number | undefined; - - /** - * The nonce used - */ - nonce: string; - - /** - * The state parameter used in the OAuth flow. - */ - state: string | undefined; - - /** - * Starts the server. - * @returns The port to listen on. - * @throws If the server fails to start. - * @throws If the server is already started. - */ - start(): Promise; - /** - * Stops the server. - * @throws If the server is not started. - * @throws If the server fails to stop. - */ - stop(): Promise; - /** - * Returns a promise that resolves to the result of the OAuth flow. - */ - waitForOAuthResponse(): Promise; -} - -export class LoopbackAuthServer implements ILoopbackServer { - private readonly _server: http.Server; - private readonly _resultPromise: Promise; - private _startingRedirect: URL; - - public nonce = randomBytes(16).toString('base64'); - public port: number | undefined; - - public set state(state: string | undefined) { - if (state) { - this._startingRedirect.searchParams.set('state', state); - } else { - this._startingRedirect.searchParams.delete('state'); - } - } - public get state(): string | undefined { - return this._startingRedirect.searchParams.get('state') ?? undefined; - } - - constructor(serveRoot: string, startingRedirect: string) { - if (!serveRoot) { - throw new Error('serveRoot must be defined'); - } - if (!startingRedirect) { - throw new Error('startingRedirect must be defined'); - } - this._startingRedirect = new URL(startingRedirect); - let deferred: { resolve: (result: IOAuthResult) => void; reject: (reason: any) => void }; - this._resultPromise = new Promise((resolve, reject) => deferred = { resolve, reject }); - - this._server = http.createServer((req, res) => { - const reqUrl = new URL(req.url!, `http://${req.headers.host}`); - switch (reqUrl.pathname) { - case '/signin': { - const receivedNonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); - if (receivedNonce !== this.nonce) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); - res.end(); - } - res.writeHead(302, { location: this._startingRedirect.toString() }); - res.end(); - break; - } - case '/callback': { - const code = reqUrl.searchParams.get('code') ?? undefined; - const state = reqUrl.searchParams.get('state') ?? undefined; - const nonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); - if (!code || !state || !nonce) { - res.writeHead(400); - res.end(); - return; - } - if (this.state !== state) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('State does not match.')}` }); - res.end(); - throw new Error('State does not match.'); - } - if (this.nonce !== nonce) { - res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); - res.end(); - throw new Error('Nonce does not match.'); - } - deferred.resolve({ code, state }); - res.writeHead(302, { location: '/' }); - res.end(); - break; - } - // Serve the static files - case '/': - sendFile(res, path.join(serveRoot, 'index.html')); - break; - default: - // substring to get rid of leading '/' - sendFile(res, path.join(serveRoot, reqUrl.pathname.substring(1))); - break; - } - }); - } - - public start(): Promise { - return new Promise((resolve, reject) => { - if (this._server.listening) { - throw new Error('Server is already started'); - } - const portTimeout = setTimeout(() => { - reject(new Error('Timeout waiting for port')); - }, 5000); - this._server.on('listening', () => { - const address = this._server.address(); - if (typeof address === 'string') { - this.port = parseInt(address); - } else if (address instanceof Object) { - this.port = address.port; - } else { - throw new Error('Unable to determine port'); - } - - clearTimeout(portTimeout); - - // set state which will be used to redirect back to vscode - this.state = `http://127.0.0.1:${this.port}/callback?nonce=${encodeURIComponent(this.nonce)}`; - - resolve(this.port); - }); - this._server.on('error', err => { - reject(new Error(`Error listening to server: ${err}`)); - }); - this._server.on('close', () => { - reject(new Error('Closed')); - }); - this._server.listen(0, '127.0.0.1'); - }); - } - - public stop(): Promise { - return new Promise((resolve, reject) => { - if (!this._server.listening) { - throw new Error('Server is not started'); - } - this._server.close((err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } - - public waitForOAuthResponse(): Promise { - return this._resultPromise; - } -} diff --git a/extensions/microsoft-authentication/src/node/buffer.ts b/extensions/microsoft-authentication/src/node/buffer.ts deleted file mode 100644 index 1ed028d4..00000000 --- a/extensions/microsoft-authentication/src/node/buffer.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export function base64Encode(text: string): string { - return Buffer.from(text, 'binary').toString('base64'); -} - -export function base64Decode(text: string): string { - return Buffer.from(text, 'base64').toString('utf8'); -} diff --git a/extensions/microsoft-authentication/src/node/crypto.ts b/extensions/microsoft-authentication/src/node/crypto.ts deleted file mode 100644 index 1b45ad99..00000000 --- a/extensions/microsoft-authentication/src/node/crypto.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as nodeCrypto from 'crypto'; - -export const crypto: Crypto = nodeCrypto.webcrypto as any; diff --git a/extensions/microsoft-authentication/src/node/fetch.ts b/extensions/microsoft-authentication/src/node/fetch.ts deleted file mode 100644 index 58718078..00000000 --- a/extensions/microsoft-authentication/src/node/fetch.ts +++ /dev/null @@ -1,7 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import fetch from 'node-fetch'; - -export const fetching = fetch; diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json deleted file mode 100644 index 4b9d06d1..00000000 --- a/extensions/microsoft-authentication/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "baseUrl": ".", - "experimentalDecorators": true, - "module": "commonjs", - "moduleResolution": "node", - "noFallthroughCasesInSwitch": true, - "noUnusedLocals": false, - "outDir": "dist", - "resolveJsonModule": true, - "rootDir": "src", - "skipLibCheck": true, - "sourceMap": true, - "lib": [ - "WebWorker" - ] - }, - "exclude": [ - "node_modules" - ], - "include": [ - "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.idToken.d.ts" - ] -} diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock deleted file mode 100644 index afa82e57..00000000 --- a/extensions/microsoft-authentication/yarn.lock +++ /dev/null @@ -1,210 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@azure/ms-rest-azure-env@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz#45809f89763a480924e21d3c620cd40866771625" - integrity sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw== - -"@microsoft/1ds-core-js@4.0.3", "@microsoft/1ds-core-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz#c8a92c623745a9595e06558a866658480c33bdf9" - integrity sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/1ds-post-js@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz#cfcb20bb23fb6215d3f0732f60f5b7df3e624f86" - integrity sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ== - dependencies: - "@microsoft/1ds-core-js" "4.0.3" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-channel-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz#247b6fe2158fad9826cbcdf7304f885766b36624" - integrity sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A== - dependencies: - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-common@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz#c4aa53ba343f5b3c7fbf54cddd3c86a5bdcd95dc" - integrity sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q== - dependencies: - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-core-js@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz#008308b786930d94a1de8a1fbb4af0351b74653e" - integrity sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g== - dependencies: - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/applicationinsights-shims@3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" - integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@microsoft/applicationinsights-web-basic@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz#9a23323276b4a5a0dc6a352e2de5d75e3c16b534" - integrity sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ== - dependencies: - "@microsoft/applicationinsights-channel-js" "3.0.4" - "@microsoft/applicationinsights-common" "3.0.4" - "@microsoft/applicationinsights-core-js" "3.0.4" - "@microsoft/applicationinsights-shims" "3.0.1" - "@microsoft/dynamicproto-js" "^2.0.2" - "@nevware21/ts-async" ">= 0.3.0 < 2.x" - "@nevware21/ts-utils" ">= 0.10.1 < 2.x" - -"@microsoft/dynamicproto-js@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" - integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== - dependencies: - "@nevware21/ts-utils" ">= 0.9.4 < 2.x" - -"@nevware21/ts-async@>= 0.3.0 < 2.x": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" - integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== - dependencies: - "@nevware21/ts-utils" ">= 0.10.0 < 2.x" - -"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.10.1 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" - integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== - -"@types/node-fetch@^2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" - integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*": - version "14.0.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806" - integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw== - -"@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== - -"@types/randombytes@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/randombytes/-/randombytes-2.0.0.tgz#0087ff5e60ae68023b9bc4398b406fea7ad18304" - integrity sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA== - dependencies: - "@types/node" "*" - -"@types/sha.js@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/sha.js/-/sha.js-2.4.0.tgz#bce682ef860b40f419d024fa08600c3b8d24bb01" - integrity sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ== - dependencies: - "@types/node" "*" - -"@types/uuid@8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" - integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== - -"@vscode/extension-telemetry@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz#8c6c61e253ff304f46045f04edd60059b144417a" - integrity sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ== - dependencies: - "@microsoft/1ds-core-js" "^4.0.3" - "@microsoft/1ds-post-js" "^4.0.3" - "@microsoft/applicationinsights-web-basic" "^3.0.4" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -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: - 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" - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" diff --git a/extensions/tunnel-forwarding/.vscode/launch.json b/extensions/tunnel-forwarding/.vscode/launch.json deleted file mode 100644 index d3fabaa1..00000000 --- a/extensions/tunnel-forwarding/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Extension", - "type": "extensionHost", - "request": "launch", - "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "env": { "VSCODE_FORWARDING_IS_DEV": "1" } // load the CLI from OSS - } - ] -} diff --git a/extensions/tunnel-forwarding/.vscodeignore b/extensions/tunnel-forwarding/.vscodeignore deleted file mode 100644 index 36e8b071..00000000 --- a/extensions/tunnel-forwarding/.vscodeignore +++ /dev/null @@ -1,5 +0,0 @@ -src/** -tsconfig.json -out/** -extension.webpack.config.js -yarn.lock \ No newline at end of file diff --git a/extensions/tunnel-forwarding/extension.webpack.config.js b/extensions/tunnel-forwarding/extension.webpack.config.js deleted file mode 100644 index b474e65c..00000000 --- a/extensions/tunnel-forwarding/extension.webpack.config.js +++ /dev/null @@ -1,20 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check - -'use strict'; - -const withDefaults = require('../shared.webpack.config'); - -module.exports = withDefaults({ - context: __dirname, - entry: { - extension: './src/extension.ts', - }, - resolve: { - mainFields: ['module', 'main'] - } -}); diff --git a/extensions/tunnel-forwarding/media/icon.png b/extensions/tunnel-forwarding/media/icon.png deleted file mode 100644 index 2c90d30a1f054c51157a1bb4497e79e41d10eda4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7191 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 Mm#*rS=-|Wt2f~m<<^TWy diff --git a/extensions/tunnel-forwarding/package.json b/extensions/tunnel-forwarding/package.json deleted file mode 100644 index 76c61e4d..00000000 --- a/extensions/tunnel-forwarding/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "tunnel-forwarding", - "displayName": "%displayName%", - "description": "%description%", - "version": "1.0.0", - "publisher": "vscode", - "license": "MIT", - "engines": { - "vscode": "^1.82.0" - }, - "icon": "media/icon.png", - "capabilities": { - "virtualWorkspaces": false, - "untrustedWorkspaces": { - "supported": true - } - }, - "enabledApiProposals": [ - "resolvers", - "tunnelFactory" - ], - "activationEvents": [ - "onTunnel" - ], - "contributes": { - "commands": [ - { - "category": "%category%", - "command": "tunnel-forwarding.showLog", - "title": "%command.showLog%", - "enablement": "tunnelForwardingHasLog" - }, - { - "category": "%category%", - "command": "tunnel-forwarding.restart", - "title": "%command.restart%", - "enablement": "tunnelForwardingIsRunning" - } - ] - }, - "main": "./out/extension", - "scripts": { - "compile": "gulp compile-extension:tunnel-forwarding", - "watch": "gulp watch-extension:tunnel-forwarding" - }, - "devDependencies": { - "@types/node": "18.x" - }, - "prettier": { - "printWidth": 100, - "trailingComma": "all", - "singleQuote": true, - "arrowParens": "avoid" - }, - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode.git" - } -} diff --git a/extensions/tunnel-forwarding/package.nls.json b/extensions/tunnel-forwarding/package.nls.json deleted file mode 100644 index 1102147e..00000000 --- a/extensions/tunnel-forwarding/package.nls.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "displayName": "Local Tunnel Port Forwarding", - "description": "Allows forwarding local ports to be accessible over the internet.", - "category": "Port Forwarding", - "command.showLog": "Show Log", - "command.restart": "Restart Forwarding System" -} diff --git a/extensions/tunnel-forwarding/src/deferredPromise.ts b/extensions/tunnel-forwarding/src/deferredPromise.ts deleted file mode 100644 index 54b0737f..00000000 --- a/extensions/tunnel-forwarding/src/deferredPromise.ts +++ /dev/null @@ -1,62 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export type ValueCallback = (value: T | Promise) => void; - -const enum DeferredOutcome { - Resolved, - Rejected -} - -/** - * Copied from src\vs\base\common\async.ts - */ -export class DeferredPromise { - - private completeCallback!: ValueCallback; - private errorCallback!: (err: unknown) => void; - private outcome?: { outcome: DeferredOutcome.Rejected; value: any } | { outcome: DeferredOutcome.Resolved; value: T }; - - public get isRejected() { - return this.outcome?.outcome === DeferredOutcome.Rejected; - } - - public get isResolved() { - return this.outcome?.outcome === DeferredOutcome.Resolved; - } - - public get isSettled() { - return !!this.outcome; - } - - public get value() { - return this.outcome?.outcome === DeferredOutcome.Resolved ? this.outcome?.value : undefined; - } - - public readonly p: Promise; - - constructor() { - this.p = new Promise((c, e) => { - this.completeCallback = c; - this.errorCallback = e; - }); - } - - public complete(value: T) { - return new Promise(resolve => { - this.completeCallback(value); - this.outcome = { outcome: DeferredOutcome.Resolved, value }; - resolve(); - }); - } - - public error(err: unknown) { - return new Promise(resolve => { - this.errorCallback(err); - this.outcome = { outcome: DeferredOutcome.Rejected, value: err }; - resolve(); - }); - } -} diff --git a/extensions/tunnel-forwarding/src/extension.ts b/extensions/tunnel-forwarding/src/extension.ts deleted file mode 100644 index 951fc6f5..00000000 --- a/extensions/tunnel-forwarding/src/extension.ts +++ /dev/null @@ -1,330 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import { DeferredPromise } from './deferredPromise'; -import { splitNewLines } from './split'; - -export const enum TunnelPrivacyId { - Private = 'private', - Public = 'public', -} - -/** - * Timeout after the last port forwarding is disposed before we'll tear down - * the CLI. This is primarily used since privacy changes to port will appear - * as a dispose+re-create call, and we don't want to have to restart the CLI. - */ -const CLEANUP_TIMEOUT = 10_000; - -const cliPath = process.env.VSCODE_FORWARDING_IS_DEV - ? path.join(__dirname, '../../../cli/target/debug/code') - : path.join( - vscode.env.appRoot, - process.platform === 'darwin' ? 'bin' : '../../bin', - vscode.env.appQuality === 'stable' ? 'code-tunnel' : 'code-tunnel-insiders', - ) + (process.platform === 'win32' ? '.exe' : ''); - -class Tunnel implements vscode.Tunnel { - private readonly disposeEmitter = new vscode.EventEmitter(); - public readonly onDidDispose = this.disposeEmitter.event; - public localAddress!: string; - - constructor( - public readonly remoteAddress: { port: number; host: string }, - public readonly privacy: TunnelPrivacyId, - ) { } - - public setPortFormat(formatString: string) { - this.localAddress = formatString.replace('{port}', String(this.remoteAddress.port)); - } - - dispose() { - this.disposeEmitter.fire(); - } -} - -const enum State { - Starting, - Active, - Inactive, - Error, -} - -type StateT = - | { state: State.Inactive } - | { state: State.Starting; process: ChildProcessWithoutNullStreams; cleanupTimeout?: NodeJS.Timeout } - | { state: State.Active; portFormat: string; process: ChildProcessWithoutNullStreams; cleanupTimeout?: NodeJS.Timeout } - | { state: State.Error; error: string }; - -export async function activate(context: vscode.ExtensionContext) { - if (vscode.env.remoteAuthority) { - return; // forwarding is local-only at the moment - } - - const logger = new Logger(vscode.l10n.t('Port Forwarding')); - const provider = new TunnelProvider(logger, context); - - context.subscriptions.push( - vscode.commands.registerCommand('tunnel-forwarding.showLog', () => logger.show()), - vscode.commands.registerCommand('tunnel-forwarding.restart', () => provider.restart()), - - provider.onDidStateChange(s => { - vscode.commands.executeCommand('setContext', 'tunnelForwardingIsRunning', s.state !== State.Inactive); - }), - - await vscode.workspace.registerTunnelProvider( - provider, - { - tunnelFeatures: { - elevation: false, - privacyOptions: [ - { themeIcon: 'globe', id: TunnelPrivacyId.Public, label: vscode.l10n.t('Public') }, - { themeIcon: 'lock', id: TunnelPrivacyId.Private, label: vscode.l10n.t('Private') }, - ], - }, - }, - ), - ); -} - -export function deactivate() { } - -class Logger { - private outputChannel?: vscode.LogOutputChannel; - - constructor(private readonly label: string) { } - - public show(): void { - return this.outputChannel?.show(); - } - - public clear() { - this.outputChannel?.clear(); - } - - public log( - logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error', - message: string, - ...args: unknown[] - ) { - if (!this.outputChannel) { - this.outputChannel = vscode.window.createOutputChannel(this.label, { log: true }); - vscode.commands.executeCommand('setContext', 'tunnelForwardingHasLog', true); - } - this.outputChannel[logLevel](message, ...args); - } -} - -const didWarnPublicKey = 'didWarnPublic'; - -class TunnelProvider implements vscode.TunnelProvider { - private readonly tunnels = new Set(); - private readonly stateChange = new vscode.EventEmitter(); - private _state: StateT = { state: State.Inactive }; - - private get state(): StateT { - return this._state; - } - - private set state(state: StateT) { - this._state = state; - this.stateChange.fire(state); - } - - public readonly onDidStateChange = this.stateChange.event; - - constructor(private readonly logger: Logger, private readonly context: vscode.ExtensionContext) { } - - /** @inheritdoc */ - public async provideTunnel(tunnelOptions: vscode.TunnelOptions): Promise { - if (tunnelOptions.privacy === TunnelPrivacyId.Public) { - if (!(await this.consentPublicPort(tunnelOptions.remoteAddress.port))) { - return; - } - } - - const tunnel = new Tunnel( - tunnelOptions.remoteAddress, - (tunnelOptions.privacy as TunnelPrivacyId) || TunnelPrivacyId.Private, - ); - - this.tunnels.add(tunnel); - tunnel.onDidDispose(() => { - this.tunnels.delete(tunnel); - this.updateActivePortsIfRunning(); - }); - - switch (this.state.state) { - case State.Error: - case State.Inactive: - await this.setupPortForwardingProcess(); - // fall through since state is now starting - case State.Starting: - this.updateActivePortsIfRunning(); - return new Promise((resolve, reject) => { - const l = this.stateChange.event(state => { - if (state.state === State.Active) { - tunnel.setPortFormat(state.portFormat); - l.dispose(); - resolve(tunnel); - } else if (state.state === State.Error) { - l.dispose(); - reject(new Error(state.error)); - } - }); - }); - case State.Active: - tunnel.setPortFormat(this.state.portFormat); - this.updateActivePortsIfRunning(); - return tunnel; - } - } - - /** Re/starts the port forwarding system. */ - public async restart() { - this.killRunningProcess(); - await this.setupPortForwardingProcess(); // will show progress - this.updateActivePortsIfRunning(); - } - - private async consentPublicPort(portNumber: number) { - const didWarn = this.context.globalState.get(didWarnPublicKey, false); - if (didWarn) { - return true; - } - - const continueOpt = vscode.l10n.t('Continue'); - const dontShowAgain = vscode.l10n.t("Don't show again"); - const r = await vscode.window.showWarningMessage( - vscode.l10n.t("You're about to create a publicly forwarded port. Anyone on the internet will be able to connect to the service listening on port {0}. You should only proceed if this service is secure and non-sensitive.", portNumber), - { modal: true }, - continueOpt, - dontShowAgain, - ); - if (r === continueOpt) { - // continue - } else if (r === dontShowAgain) { - await this.context.globalState.update(didWarnPublicKey, true); - } else { - return false; - } - - return true; - } - - private isInStateWithProcess(process: ChildProcessWithoutNullStreams) { - return ( - (this.state.state === State.Starting || this.state.state === State.Active) && - this.state.process === process - ); - } - - private killRunningProcess() { - if (this.state.state === State.Starting || this.state.state === State.Active) { - this.logger.log('info', '[forwarding] no more ports, stopping forwarding CLI'); - this.state.process.kill(); - this.state = { state: State.Inactive }; - } - } - - private updateActivePortsIfRunning() { - if (this.state.state !== State.Starting && this.state.state !== State.Active) { - return; - } - - const ports = [...this.tunnels].map(t => ({ number: t.remoteAddress.port, privacy: t.privacy })); - this.state.process.stdin.write(`${JSON.stringify(ports)}\n`); - - if (ports.length === 0 && !this.state.cleanupTimeout) { - this.state.cleanupTimeout = setTimeout(() => this.killRunningProcess(), CLEANUP_TIMEOUT); - } else if (ports.length > 0 && this.state.cleanupTimeout) { - clearTimeout(this.state.cleanupTimeout); - this.state.cleanupTimeout = undefined; - } - } - - private async setupPortForwardingProcess() { - const session = await vscode.authentication.getSession('github', ['user:email', 'read:org'], { - createIfNone: true, - }); - - const args = [ - '--verbose', - 'tunnel', - 'forward-internal', - '--provider', - 'github', - '--access-token', - session.accessToken, - ]; - - this.logger.log('info', '[forwarding] starting CLI'); - const child = spawn(cliPath, args, { stdio: 'pipe', env: { ...process.env, NO_COLOR: '1' } }); - this.state = { state: State.Starting, process: child }; - - const progressP = new DeferredPromise(); - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t({ - comment: ['do not change link format [Show Log](command), only change the text "Show Log"'], - message: 'Starting port forwarding system ([Show Log]({0}))', - args: ['command:tunnel-forwarding.showLog'] - }), - }, - () => progressP.p, - ); - - let lastPortFormat: string | undefined; - child.on('exit', status => { - const msg = `[forwarding] exited with code ${status}`; - this.logger.log('info', msg); - progressP.complete(); // make sure to clear progress on unexpected exit - if (this.isInStateWithProcess(child)) { - this.state = { state: State.Error, error: msg }; - } - }); - - child.on('error', err => { - this.logger.log('error', `[forwarding] ${err}`); - progressP.complete(); // make sure to clear progress on unexpected exit - if (this.isInStateWithProcess(child)) { - this.state = { state: State.Error, error: String(err) }; - } - }); - - child.stdout - .pipe(splitNewLines()) - .on('data', line => this.logger.log('info', `[forwarding] ${line}`)) - .resume(); - - child.stderr - .pipe(splitNewLines()) - .on('data', line => { - try { - const l: { port_format: string } = JSON.parse(line); - if (l.port_format && l.port_format !== lastPortFormat) { - this.state = { - state: State.Active, - portFormat: l.port_format, process: child, - cleanupTimeout: 'cleanupTimeout' in this.state ? this.state.cleanupTimeout : undefined, - }; - progressP.complete(); - } - } catch (e) { - this.logger.log('error', `[forwarding] ${line}`); - } - }) - .resume(); - - await new Promise((resolve, reject) => { - child.on('spawn', resolve); - child.on('error', reject); - }); - } -} diff --git a/extensions/tunnel-forwarding/src/split.ts b/extensions/tunnel-forwarding/src/split.ts deleted file mode 100644 index 33ad055a..00000000 --- a/extensions/tunnel-forwarding/src/split.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Transform } from 'stream'; - -export const splitNewLines = () => new StreamSplitter('\n'.charCodeAt(0)); - -/** - * Copied and simplified from src\vs\base\node\nodeStreams.ts - * - * Exception: does not include the split character in the output. - */ -export class StreamSplitter extends Transform { - private buffer: Buffer | undefined; - - constructor(private readonly splitter: number) { - super(); - } - - override _transform(chunk: Buffer, _encoding: string, callback: (error?: Error | null, data?: any) => void): void { - if (!this.buffer) { - this.buffer = chunk; - } else { - this.buffer = Buffer.concat([this.buffer, chunk]); - } - - let offset = 0; - while (offset < this.buffer.length) { - const index = this.buffer.indexOf(this.splitter, offset); - if (index === -1) { - break; - } - - this.push(this.buffer.subarray(offset, index)); - offset = index + 1; - } - - this.buffer = offset === this.buffer.length ? undefined : this.buffer.subarray(offset); - callback(); - } - - override _flush(callback: (error?: Error | null, data?: any) => void): void { - if (this.buffer) { - this.push(this.buffer); - } - - callback(); - } -} diff --git a/extensions/tunnel-forwarding/tsconfig.json b/extensions/tunnel-forwarding/tsconfig.json deleted file mode 100644 index 4769a9fa..00000000 --- a/extensions/tunnel-forwarding/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": "./out", - "downlevelIteration": true, - "types": [ - "node" - ] - }, - "include": [ - "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", - "../../src/vscode-dts/vscode.proposed.tunnelFactory.d.ts" - ] -} diff --git a/extensions/tunnel-forwarding/yarn.lock b/extensions/tunnel-forwarding/yarn.lock deleted file mode 100644 index 8a3d10f2..00000000 --- a/extensions/tunnel-forwarding/yarn.lock +++ /dev/null @@ -1,8 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==