Remove CredentialsService & keytar (#192224)

* Remove CredentialsService & keytar

ref https://github.com/microsoft/vscode/issues/115215
fixes https://github.com/microsoft/vscode/issues/143395

* compile

* remove imports

* rip the bandaid
This commit is contained in:
Tyler James Leonhardt
2023-09-05 17:47:30 -07:00
committed by GitHub
parent a8eda33032
commit 5134662139
38 changed files with 44 additions and 1410 deletions

View File

@@ -221,8 +221,7 @@ jobs:
- name: Compile Integration Tests
run: yarn --cwd test/integration/browser compile
# This is required for keytar unittests, otherwise we hit
# https://github.com/atom/node-keytar/issues/76
# This is required for SecretStorage unittests
- name: Create temporary keychain
run: |
security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain

View File

@@ -79,13 +79,6 @@ kerberos/src/**
kerberos/node_modules/**
!kerberos/**/*.node
keytar/binding.gyp
keytar/build/**
keytar/src/**
keytar/script/**
keytar/node_modules/**
!keytar/**/*.node
node-pty/binding.gyp
node-pty/build/**
node-pty/src/**

View File

@@ -45,7 +45,7 @@ async function main(buildDir) {
});
fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t'));
// Verify if native module architecture is correct
const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'keytar.node']);
const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'kerberos.node']);
const lipoOutput = await (0, cross_spawn_promise_1.spawn)('lipo', ['-archs', findOutput.replace(/\n$/, '')]);
if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') {
throw new Error(`Invalid arch, got : ${lipoOutput}`);
@@ -57,4 +57,4 @@ if (require.main === module) {
process.exit(1);
});
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWTtZQUNaLGNBQWM7WUFDZCxRQUFRO1NBQ1I7UUFDRCxVQUFVO1FBQ1YsS0FBSyxFQUFFLElBQUk7S0FDWCxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDMUIsc0JBQXNCLEVBQUUsa0JBQWtCO0tBQzFDLENBQUMsQ0FBQztJQUNILEVBQUUsQ0FBQyxhQUFhLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLGtEQUFrRDtJQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUM7SUFDN0UsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFBLDJCQUFLLEVBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRixJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLGNBQWMsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ3JEO0FBQ0YsQ0FBQztBQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDakMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUFDO0NBQ0gifQ==
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWTtZQUNaLGNBQWM7WUFDZCxRQUFRO1NBQ1I7UUFDRCxVQUFVO1FBQ1YsS0FBSyxFQUFFLElBQUk7S0FDWCxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDMUIsc0JBQXNCLEVBQUUsa0JBQWtCO0tBQzFDLENBQUMsQ0FBQztJQUNILEVBQUUsQ0FBQyxhQUFhLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLGtEQUFrRDtJQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUM7SUFDL0UsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFBLDJCQUFLLEVBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRixJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLGNBQWMsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ3JEO0FBQ0YsQ0FBQztBQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDakMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUFDO0NBQ0gifQ==

View File

@@ -51,7 +51,7 @@ async function main(buildDir?: string) {
fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t'));
// Verify if native module architecture is correct
const findOutput = await spawn('find', [outAppPath, '-name', 'keytar.node']);
const findOutput = await spawn('find', [outAppPath, '-name', 'kerberos.node']);
const lipoOutput = await spawn('lipo', ['-archs', findOutput.replace(/\n$/, '')]);
if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') {
throw new Error(`Invalid arch, got : ${lipoOutput}`);

View File

@@ -3,8 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// keytar depends on a native module shipped in vscode, so this is
// how we load it
import * as vscode from 'vscode';
import { Log } from './logger';

View File

@@ -20,9 +20,6 @@ module.exports = withBrowserDefaults({
entry: {
extension: './src/extension.ts',
},
externals: {
'keytar': 'commonjs keytar',
},
resolve: {
alias: {
'./node/crypto': path.resolve(__dirname, 'src/browser/crypto'),

View File

@@ -12,9 +12,6 @@ const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts',
},
externals: {
'keytar': 'commonjs keytar'
extension: './src/extension.ts'
}
});

View File

@@ -36,7 +36,6 @@ suite('vscode - automatic language detection', () => {
"outDir": "../out/vs",
"target": "es2020",
"types": [
"keytar",
"mocha",
"semver",
"sinon",

View File

@@ -84,7 +84,6 @@
"https-proxy-agent": "^2.2.3",
"jschardet": "3.0.0",
"kerberos": "^2.0.1",
"keytar": "7.9.0",
"minimist": "^1.2.6",
"native-is-elevated": "0.7.0",
"native-keymap": "^3.3.4",
@@ -118,7 +117,6 @@
"@types/gulp-svgmin": "^1.2.1",
"@types/http-proxy-agent": "^2.0.1",
"@types/kerberos": "^1.1.2",
"@types/keytar": "^4.4.0",
"@types/minimist": "^1.2.1",
"@types/mocha": "^9.1.1",
"@types/node": "18.x",

View File

@@ -19,7 +19,6 @@
"https-proxy-agent": "^2.2.3",
"jschardet": "3.0.0",
"kerberos": "^2.0.1",
"keytar": "7.9.0",
"minimist": "^1.2.6",
"native-watchdog": "^1.4.1",
"node-pty": "1.1.0-beta1",

View File

@@ -141,29 +141,6 @@ agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0:
dependencies:
debug "^4.3.4"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
are-we-there-yet@~1.1.2:
version "1.1.7"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -210,26 +187,11 @@ chownr@^1.1.1:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
cookie@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@3.1.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -263,21 +225,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
detect-libc@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -326,20 +278,6 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
has-unicode "^2.0.0"
object-assign "^4.1.0"
signal-exit "^3.0.0"
string-width "^1.0.1"
strip-ansi "^3.0.1"
wide-align "^1.1.0"
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
@@ -350,11 +288,6 @@ graceful-fs@4.2.11:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
http-proxy-agent@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
@@ -400,7 +333,7 @@ ieee754@^1.1.13:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
inherits@^2.0.3, inherits@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -420,18 +353,6 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
dependencies:
number-is-nan "^1.0.0"
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
@@ -444,11 +365,6 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
jschardet@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882"
@@ -463,14 +379,6 @@ kerberos@^2.0.1:
node-addon-api "^4.3.0"
prebuild-install "7.1.1"
keytar@7.9.0:
version "7.9.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb"
integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==
dependencies:
node-addon-api "^4.3.0"
prebuild-install "^7.0.1"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -567,26 +475,6 @@ node-pty@1.1.0-beta1:
dependencies:
nan "^2.17.0"
npmlog@^4.0.1:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
gauge "~2.7.3"
set-blocking "~2.0.0"
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -622,30 +510,6 @@ prebuild-install@7.1.1:
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
prebuild-install@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"
integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==
dependencies:
detect-libc "^2.0.0"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^3.3.0"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^4.0.0"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
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"
@@ -669,19 +533,6 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
readable-stream@^2.0.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.1.1, readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
@@ -696,11 +547,6 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
semver@^7.3.5:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
@@ -708,16 +554,6 @@ semver@^7.3.5:
dependencies:
lru-cache "^6.0.0"
set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
signal-exit@^3.0.0:
version "3.0.6"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
@@ -754,24 +590,6 @@ socks@^2.7.1:
ip "^2.0.0"
smart-buffer "^4.2.0"
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
dependencies:
code-point-at "^1.0.0"
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -779,27 +597,6 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -845,7 +642,7 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
@@ -865,13 +662,6 @@ vscode-textmate@9.0.0:
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c"
integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==
wide-align@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"

View File

@@ -8,7 +8,6 @@
"resolveJsonModule": true,
"outDir": "../out/vs",
"types": [
"keytar",
"mocha",
"semver",
"sinon",

View File

@@ -31,7 +31,6 @@ import { localize } from 'vs/nls';
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials';
import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc';
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics';
import { DiagnosticsMainService, IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService';
@@ -97,7 +96,6 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService';
import { IPolicyService } from 'vs/platform/policy/common/policy';
import { PolicyChannel } from 'vs/platform/policy/common/policyIpc';
import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile';
@@ -964,9 +962,6 @@ export class CodeApplication extends Disposable {
// Native Host
services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService, undefined, false /* proxied to other processes */));
// Credentials
services.set(ICredentialsMainService, new SyncDescriptor(CredentialsNativeMainService));
// Webview Manager
services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService));
@@ -1095,10 +1090,6 @@ export class CodeApplication extends Disposable {
const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService));
mainProcessElectronServer.registerChannel('encryption', encryptionChannel);
// Credentials
const credentialsChannel = ProxyChannel.fromService(accessor.get(ICredentialsMainService));
mainProcessElectronServer.registerChannel('credentials', credentialsChannel);
// Signing
const signChannel = ProxyChannel.fromService(accessor.get(ISignService));
mainProcessElectronServer.registerChannel('sign', signChannel);

View File

@@ -9,10 +9,8 @@ import { Event } from 'vs/base/common/event';
import { hash } from 'vs/base/common/hash';
import { Disposable } from 'vs/base/common/lifecycle';
import { generateUuid } from 'vs/base/common/uuid';
import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials';
import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
@@ -58,7 +56,6 @@ enum ProxyAuthState {
export class ProxyAuthHandler extends Disposable {
private readonly OLD_PROXY_CREDENTIALS_SERVICE_KEY = `${this.productService.urlProtocol}.proxy-credentials`;
private readonly PROXY_CREDENTIALS_SERVICE_KEY = 'proxy-credentials://';
private pendingProxyResolve: Promise<Credentials | undefined> | undefined = undefined;
@@ -70,10 +67,8 @@ export class ProxyAuthHandler extends Disposable {
constructor(
@ILogService private readonly logService: ILogService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@ICredentialsMainService private readonly credentialsService: ICredentialsMainService,
@IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService,
@IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService,
@IProductService private readonly productService: IProductService
@IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService
) {
super();
@@ -145,34 +140,6 @@ export class ProxyAuthHandler extends Disposable {
return undefined;
}
// TODO: remove this migration in a release or two.
private async getAndMigrateProxyCredentials(authInfoHash: string): Promise<{ storedUsername: string | undefined; storedPassword: string | undefined }> {
// Find any previously stored credentials
try {
let encryptedSerializedProxyCredentials = this.applicationStorageMainService.get(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION);
let decryptedSerializedProxyCredentials: string | undefined;
if (!encryptedSerializedProxyCredentials) {
encryptedSerializedProxyCredentials = await this.credentialsService.getPassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash) ?? undefined;
if (encryptedSerializedProxyCredentials) {
// re-encrypt to force new encryption algorithm to apply
decryptedSerializedProxyCredentials = await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials);
encryptedSerializedProxyCredentials = await this.encryptionMainService.encrypt(decryptedSerializedProxyCredentials);
this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedProxyCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE);
// Remove it from the old location since it's in the new location.
await this.credentialsService.deletePassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash);
}
}
if (encryptedSerializedProxyCredentials) {
const credentials: Credentials = JSON.parse(decryptedSerializedProxyCredentials ?? await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials));
return { storedUsername: credentials.username, storedPassword: credentials.password };
}
} catch (error) {
this.logService.error(error); // handle errors by asking user for login via dialog
}
return { storedUsername: undefined, storedPassword: undefined };
}
private async doResolveProxyCredentials(authInfo: AuthInfo): Promise<Credentials | undefined> {
this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo);
@@ -181,7 +148,20 @@ export class ProxyAuthHandler extends Disposable {
// given the properties of the auth request
// (see https://github.com/microsoft/vscode/issues/109497)
const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port }));
const { storedUsername, storedPassword } = await this.getAndMigrateProxyCredentials(authInfoHash);
let storedUsername: string | undefined;
let storedPassword: string | undefined;
try {
// Try to find stored credentials for the given auth info
const encryptedValue = this.applicationStorageMainService.get(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION);
if (encryptedValue) {
const credentials: Credentials = JSON.parse(await this.encryptionMainService.decrypt(encryptedValue));
storedUsername = credentials.username;
storedPassword = credentials.password;
}
} catch (error) {
this.logService.error(error); // handle errors by asking user for login via dialog
}
// Reply with stored credentials unless we used them already.
// In that case we need to show a login dialog again because
@@ -230,7 +210,13 @@ export class ProxyAuthHandler extends Disposable {
try {
if (reply.remember) {
const encryptedSerializedCredentials = await this.encryptionMainService.encrypt(JSON.stringify(credentials));
this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE);
this.applicationStorageMainService.store(
this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash,
encryptedSerializedCredentials,
StorageScope.APPLICATION,
// Always store in machine scope because we do not want these values to be synced
StorageTarget.MACHINE
);
} else {
this.applicationStorageMainService.remove(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION);
}

View File

@@ -1,84 +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 { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ICredentialsService = createDecorator<ICredentialsService>('credentialsService');
export interface ICredentialsProvider {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;
clear?(): Promise<void>;
}
export interface ICredentialsChangeEvent {
service?: string;
account: string;
}
export interface ICredentialsService extends ICredentialsProvider {
readonly _serviceBrand: undefined;
readonly onDidChangePassword: Event<ICredentialsChangeEvent>;
/*
* Each CredentialsService must provide a prefix that will be used
* by the SecretStorage API when storing secrets.
* This is a method that returns a Promise so that it can be defined in
* the main process and proxied on the renderer side.
*/
getSecretStoragePrefix(): Promise<string>;
}
export const ICredentialsMainService = createDecorator<ICredentialsMainService>('credentialsMainService');
export interface ICredentialsMainService extends ICredentialsService { }
interface ISecretVault {
[service: string]: { [account: string]: string } | undefined;
}
export class InMemoryCredentialsProvider implements ICredentialsProvider {
private secretVault: ISecretVault = {};
async getPassword(service: string, account: string): Promise<string | null> {
return this.secretVault[service]?.[account] ?? null;
}
async setPassword(service: string, account: string, password: string): Promise<void> {
this.secretVault[service] = this.secretVault[service] ?? {};
this.secretVault[service]![account] = password;
}
async deletePassword(service: string, account: string): Promise<boolean> {
if (!this.secretVault[service]?.[account]) {
return false;
}
delete this.secretVault[service]![account];
if (Object.keys(this.secretVault[service]!).length === 0) {
delete this.secretVault[service];
}
return true;
}
async findPassword(service: string): Promise<string | null> {
return JSON.stringify(this.secretVault[service]) ?? null;
}
async findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
const credentials: { account: string; password: string }[] = [];
for (const account of Object.keys(this.secretVault[service] || {})) {
credentials.push({ account, password: this.secretVault[service]![account] });
}
return credentials;
}
async clear(): Promise<void> {
this.secretVault = {};
}
}

View File

@@ -1,264 +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 { ICredentialsChangeEvent, ICredentialsMainService, InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { isWindows } from 'vs/base/common/platform';
import { retry, SequencerByKey } from 'vs/base/common/async';
interface ChunkedPassword {
content: string;
hasNextChunk: boolean;
}
export type KeytarModule = typeof import('keytar');
export abstract class BaseCredentialsMainService extends Disposable implements ICredentialsMainService {
private static readonly MAX_PASSWORD_LENGTH = 2500;
private static readonly PASSWORD_CHUNK_SIZE = BaseCredentialsMainService.MAX_PASSWORD_LENGTH - 100;
declare readonly _serviceBrand: undefined;
private _onDidChangePassword: Emitter<ICredentialsChangeEvent> = this._register(new Emitter());
readonly onDidChangePassword = this._onDidChangePassword.event;
protected _keytarCache: KeytarModule | undefined;
private _sequencer = new SequencerByKey<string>();
constructor(
@ILogService protected readonly logService: ILogService,
) {
super();
}
//#region abstract
public abstract getSecretStoragePrefix(): Promise<string>;
protected abstract withKeytar(): Promise<KeytarModule>;
/**
* An optional method that subclasses can implement to assist in surfacing
* Keytar load errors to the user in a friendly way.
*/
protected abstract surfaceKeytarLoadError?: (err: any) => void;
//#endregion
async getPassword(service: string, account: string): Promise<string | null> {
this.logService.trace('Going to get password from keytar:', service, account);
let keytar: KeytarModule;
try {
keytar = await this.withKeytar();
} catch (e) {
// for get operations, we don't want to surface errors to the user
return null;
}
return await this._sequencer.queue(service + account, () => this.doGetPassword(keytar, service, account));
}
private async doGetPassword(keytar: KeytarModule, service: string, account: string): Promise<string | null> {
this.logService.trace('Doing get password from keytar:', service, account);
const password = await retry(() => keytar.getPassword(service, account), 50, 3);
if (!password) {
this.logService.trace('Did not get a password from keytar for account:', account);
return password;
}
let content: string | undefined;
let hasNextChunk: boolean | undefined;
try {
const parsed: ChunkedPassword = JSON.parse(password);
content = parsed.content;
hasNextChunk = parsed.hasNextChunk;
} catch {
// Ignore this similar to how we ignore parse errors in the delete
// because on non-windows this will not be a JSON string.
}
if (!content || !hasNextChunk) {
this.logService.trace('Got password from keytar for account:', account);
return password;
}
try {
let index = 1;
while (hasNextChunk) {
const nextChunk = await retry(() => keytar.getPassword(service, `${account}-${index}`), 50, 3);
const result: ChunkedPassword = JSON.parse(nextChunk!);
content += result.content;
hasNextChunk = result.hasNextChunk;
index++;
}
this.logService.trace(`Got ${index}-chunked password from keytar for account:`, account);
return content;
} catch (e) {
this.logService.error(e);
return password;
}
}
async setPassword(service: string, account: string, password: string): Promise<void> {
this.logService.trace('Going to set password using keytar:', service, account);
let keytar: KeytarModule;
try {
keytar = await this.withKeytar();
} catch (e) {
this.surfaceKeytarLoadError?.(e);
throw e;
}
await this._sequencer.queue(service + account, () => this.doSetPassword(keytar, service, account, password));
this._onDidChangePassword.fire({ service, account });
}
private async doSetPassword(keytar: KeytarModule, service: string, account: string, password: string): Promise<void> {
this.logService.trace('Doing set password from keytar:', service, account);
if (!isWindows) {
await retry(() => keytar.setPassword(service, account, password), 50, 3);
this.logService.trace('Set password from keytar for account:', account);
return;
}
// On Windows, we sometimes have to chunk the password because the Windows Credential Manager only allows passwords of a max length.
// So to make sure we can store passwords of any length, we chunk the longer passwords and store it as multiple passwords.
// To ensure we store any password correctly, we first delete any existing password, chunks and all, and then store the new ones.
await this.doDeletePassword(keytar, service, account);
// if it's a short password, just store it
if (password.length <= BaseCredentialsMainService.PASSWORD_CHUNK_SIZE) {
await retry(() => keytar.setPassword(service, account, password), 50, 3);
this.logService.trace('Set password from keytar for account:', account);
return;
}
// otherwise, chunk it and store it
let index = 0;
let chunk = 0;
let hasNextChunk = true;
while (hasNextChunk) {
const passwordChunk = password.substring(index, index + BaseCredentialsMainService.PASSWORD_CHUNK_SIZE);
index += BaseCredentialsMainService.PASSWORD_CHUNK_SIZE;
hasNextChunk = password.length - index > 0;
const content: ChunkedPassword = {
content: passwordChunk,
hasNextChunk: hasNextChunk
};
await retry(() => keytar.setPassword(service, chunk ? `${account}-${chunk}` : account, JSON.stringify(content)), 50, 3);
chunk++;
}
this.logService.trace(`Set${chunk ? ` ${chunk}-chunked` : ''} password from keytar for account:`, account);
}
async deletePassword(service: string, account: string): Promise<boolean> {
this.logService.trace('Going to delete password using keytar:', service, account);
let keytar: KeytarModule;
try {
keytar = await this.withKeytar();
} catch (e) {
this.surfaceKeytarLoadError?.(e);
throw e;
}
const result = await this._sequencer.queue(service + account, () => this.doDeletePassword(keytar, service, account));
if (result) {
this._onDidChangePassword.fire({ service, account });
}
return result;
}
private async doDeletePassword(keytar: KeytarModule, service: string, account: string): Promise<boolean> {
this.logService.trace('Doing delete password from keytar:', service, account);
const password = await keytar.getPassword(service, account);
if (!password) {
this.logService.trace('Did not get a password to delete from keytar for account:', account);
return false;
}
let content: string | undefined;
let hasNextChunk: boolean | undefined;
try {
const possibleChunk = JSON.parse(password);
content = possibleChunk.content;
hasNextChunk = possibleChunk.hasNextChunk;
} catch {
// When the password is saved the entire JSON payload is encrypted then stored, thus the result from getPassword might not be valid JSON
// https://github.com/microsoft/vscode/blob/c22cb87311b5eb1a3bf5600d18733f7485355dc0/src/vs/workbench/api/browser/mainThreadSecretState.ts#L83
// However in the chunked case we JSONify each chunk after encryption so for the chunked case we do expect valid JSON here
// https://github.com/microsoft/vscode/blob/708cb0c507d656b760f9d08115b8ebaf8964fd73/src/vs/platform/credentials/common/credentialsMainService.ts#L128
// Empty catch here just as in getPassword because we expect to handle both JSON cases and non JSON cases here it's not an error case to fail to parse
// https://github.com/microsoft/vscode/blob/708cb0c507d656b760f9d08115b8ebaf8964fd73/src/vs/platform/credentials/common/credentialsMainService.ts#L76
}
let index = 0;
if (content && hasNextChunk) {
try {
// need to delete additional chunks
index++;
while (hasNextChunk) {
const accountWithIndex = `${account}-${index}`;
const nextChunk = await keytar.getPassword(service, accountWithIndex);
await keytar.deletePassword(service, accountWithIndex);
const result: ChunkedPassword = JSON.parse(nextChunk!);
hasNextChunk = result.hasNextChunk;
index++;
}
} catch (e) {
this.logService.error(e);
}
}
// Delete the first account to determine deletion success
if (await keytar.deletePassword(service, account)) {
this.logService.trace(`Deleted${index ? ` ${index}-chunked` : ''} password from keytar for account:`, account);
return true;
}
this.logService.trace(`Keytar failed to delete${index ? ` ${index}-chunked` : ''} password for account:`, account);
return false;
}
async findPassword(service: string): Promise<string | null> {
let keytar: KeytarModule;
try {
keytar = await this.withKeytar();
} catch (e) {
// for get operations, we don't want to surface errors to the user
return null;
}
return await keytar.findPassword(service);
}
async findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
let keytar: KeytarModule;
try {
keytar = await this.withKeytar();
} catch (e) {
// for get operations, we don't want to surface errors to the user
return [];
}
return await keytar.findCredentials(service);
}
public clear(): Promise<void> {
if (this._keytarCache instanceof InMemoryCredentialsProvider) {
return this._keytarCache.clear();
}
// We don't know how to properly clear Keytar because we don't know
// what services have stored credentials. For reference, a "service" is an extension.
// TODO: should we clear credentials for the built-in auth extensions?
return Promise.resolve();
}
}

View File

@@ -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 { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService';
export class CredentialsNativeMainService extends BaseCredentialsMainService {
constructor(
@ILogService logService: ILogService,
@INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService,
@IProductService private readonly productService: IProductService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
super(logService);
}
// If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the
// client would store the credentials.
public override async getSecretStoragePrefix() { return Promise.resolve(this.productService.urlProtocol); }
protected async withKeytar(): Promise<KeytarModule> {
if (this._keytarCache) {
return this._keytarCache;
}
if (this.environmentMainService.disableKeytar) {
this.logService.info('Keytar is disabled. Using in-memory credential store instead.');
this._keytarCache = new InMemoryCredentialsProvider();
return this._keytarCache;
}
const keytarCache = await import('keytar');
// Try using keytar to see if it throws or not.
await keytarCache.findCredentials('test-keytar-loads');
this._keytarCache = keytarCache;
return this._keytarCache;
}
protected override surfaceKeytarLoadError = (err: any) => {
this.windowsMainService.sendToFocused('vscode:showCredentialsError', err.message ?? err);
};
}

View File

@@ -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 { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/productService';
import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService';
export class CredentialsWebMainService extends BaseCredentialsMainService {
// Since we fallback to the in-memory credentials provider, we do not need to surface any Keytar load errors
// to the user.
protected surfaceKeytarLoadError?: (err: any) => void;
constructor(
@ILogService logService: ILogService,
@INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService,
@IProductService private readonly productService: IProductService,
) {
super(logService);
}
// If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the
// client would store the credentials.
public override async getSecretStoragePrefix() { return Promise.resolve(`${this.productService.urlProtocol}-server`); }
protected async withKeytar(): Promise<KeytarModule> {
if (this._keytarCache) {
return this._keytarCache;
}
if (this.environmentMainService.disableKeytar) {
this.logService.info('Keytar is disabled. Using in-memory credential store instead.');
this._keytarCache = new InMemoryCredentialsProvider();
return this._keytarCache;
}
try {
this._keytarCache = await import('keytar');
// Try using keytar to see if it throws or not.
await this._keytarCache.findCredentials('test-keytar-loads');
} catch (e) {
this.logService.warn(
`Using the in-memory credential store as the operating system's credential store could not be accessed. Please see https://aka.ms/vscode-server-keyring on how to set this up. Details: ${e.message ?? e}`);
this._keytarCache = new InMemoryCredentialsProvider();
}
return this._keytarCache;
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { isWindows } from 'vs/base/common/platform';
import { flakySuite } from 'vs/base/test/common/testUtils';
function testErrorMessage(module: string): string {
@@ -74,28 +74,6 @@ flakySuite('Native Modules (all platforms)', () => {
});
});
(isLinux ? suite.skip : suite)('Native Modules (Windows, macOS)', () => {
test('keytar', async () => {
const keytar = await import('keytar');
const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`;
try {
await keytar.setPassword(name, 'foo', 'bar');
assert.strictEqual(await keytar.findPassword(name), 'bar');
assert.strictEqual((await keytar.findCredentials(name)).length, 1);
assert.strictEqual(await keytar.getPassword(name, 'foo'), 'bar');
await keytar.deletePassword(name, 'foo');
assert.strictEqual(await keytar.getPassword(name, 'foo'), null);
} catch (err) {
try {
await keytar.deletePassword(name, 'foo'); // try to clean up
} catch { }
throw err;
}
});
});
(!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => {
(process.type === 'renderer' ? test.skip /* TODO@electron module is not context aware yet and thus cannot load in Electron renderer used by tests */ : test)('@vscode/windows-mutex', async () => {

View File

@@ -11,12 +11,10 @@ import * as path from 'vs/base/common/path';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { getMachineId } from 'vs/base/node/id';
import { Promises } from 'vs/base/node/pfs';
import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc';
import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials';
import { CredentialsWebMainService } from 'vs/platform/credentials/node/credentialsMainService';
import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
import { IDownloadService } from 'vs/platform/download/common/download';
import { DownloadServiceChannelClient } from 'vs/platform/download/common/downloadIpc';
@@ -201,8 +199,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
const ptyHostService = instantiationService.createInstance(PtyHostService, ptyHostStarter);
services.set(IPtyService, ptyHostService);
services.set(ICredentialsMainService, new SyncDescriptor(CredentialsWebMainService));
instantiationService.invokeFunction(accessor => {
const extensionManagementService = accessor.get(INativeServerExtensionManagementService);
const extensionsScannerService = accessor.get(IExtensionsScannerService);
@@ -227,9 +223,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority));
socketServer.registerChannel('extensions', channel);
const credentialsChannel = ProxyChannel.fromService<RemoteAgentConnectionContext>(accessor.get(ICredentialsMainService));
socketServer.registerChannel('credentials', credentialsChannel);
// clean up extensions folder
remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp());

View File

@@ -42,7 +42,6 @@ import './mainThreadErrors';
import './mainThreadExtensionService';
import './mainThreadFileSystem';
import './mainThreadFileSystemEventService';
import './mainThreadKeytar';
import './mainThreadLanguageFeatures';
import './mainThreadLanguages';
import './mainThreadLogService';

View File

@@ -1,41 +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 { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape } from 'vs/workbench/api/common/extHost.protocol';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
constructor(
_extHostContext: IExtHostContext,
@ICredentialsService private readonly _credentialsService: ICredentialsService,
) { }
async $getPassword(service: string, account: string): Promise<string | null> {
return this._credentialsService.getPassword(service, account);
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
return this._credentialsService.setPassword(service, account, password);
}
async $deletePassword(service: string, account: string): Promise<boolean> {
return this._credentialsService.deletePassword(service, account);
}
async $findPassword(service: string): Promise<string | null> {
return this._credentialsService.findPassword(service);
}
async $findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
return this._credentialsService.findCredentials(service);
}
dispose(): void {
//
}
}

View File

@@ -5,146 +5,28 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { ExtHostContext, ExtHostSecretStateShape, MainContext, MainThreadSecretStateShape } from '../common/extHost.protocol';
import { ILogService } from 'vs/platform/log/common/log';
import { SequencerByKey } from 'vs/base/common/async';
import { ISecretStorageService } from 'vs/platform/secrets/common/secrets';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService';
class OldMainThreadSecretState extends Disposable implements MainThreadSecretStateShape {
private secretStoragePrefix = this.credentialsService.getSecretStoragePrefix();
constructor(
private readonly _proxy: ExtHostSecretStateShape,
private readonly credentialsService: ICredentialsService,
private readonly encryptionService: IEncryptionService,
private readonly logService: ILogService,
) {
super();
this._register(this.credentialsService.onDidChangePassword(async e => {
const extensionId = e.service?.substring((await this.secretStoragePrefix).length);
if (extensionId) {
this._proxy.$onDidChangePassword({ extensionId, key: e.account });
}
}));
}
private async getFullKey(extensionId: string): Promise<string> {
return `${await this.secretStoragePrefix}${extensionId}`;
}
async $getPassword(extensionId: string, key: string): Promise<string | undefined> {
this.logService.trace(`MainThreadSecretState#getPassword: Getting password for ${extensionId} extension: `, key);
const fullKey = await this.getFullKey(extensionId);
const password = await this.credentialsService.getPassword(fullKey, key);
if (!password) {
this.logService.trace('MainThreadSecretState#getPassword: No password found for: ', key);
return undefined;
}
let decrypted: string | null;
try {
this.logService.trace('MainThreadSecretState#getPassword: Decrypting password for: ', key);
decrypted = await this.encryptionService.decrypt(password);
} catch (e) {
this.logService.error(e);
this.logService.trace('MainThreadSecretState#getPassword: Trying migration for: ', key);
// If we are on a platform that newly started encrypting secrets before storing them,
// then passwords previously stored were stored un-encrypted (NOTE: but still being stored in a secure keyring).
// When we try to decrypt a password that wasn't encrypted previously, the encryption service will throw.
// To recover gracefully, we first try to encrypt & store the password (essentially migrating the secret to the new format)
// and then we try to read it and decrypt again.
const encryptedForSet = await this.encryptionService.encrypt(password);
await this.credentialsService.setPassword(fullKey, key, encryptedForSet);
const passwordEncrypted = await this.credentialsService.getPassword(fullKey, key);
decrypted = passwordEncrypted && await this.encryptionService.decrypt(passwordEncrypted);
}
if (decrypted) {
try {
const value = JSON.parse(decrypted);
if (value.extensionId === extensionId) {
this.logService.trace('MainThreadSecretState#getPassword: Password found for: ', key);
return value.content;
}
} catch (parseError) {
// We may not be able to parse it, but we keep the secret in the keychain anyway just in case
// it decrypts correctly in the future.
this.logService.error(parseError);
throw new Error('Unable to parse decrypted password');
}
}
this.logService.trace('MainThreadSecretState#getPassword: No password found for: ', key);
return undefined;
}
async $setPassword(extensionId: string, key: string, value: string): Promise<void> {
this.logService.trace(`MainThreadSecretState#setPassword: Setting password for ${extensionId} extension: `, key);
const fullKey = await this.getFullKey(extensionId);
const toEncrypt = JSON.stringify({
extensionId,
content: value
});
this.logService.trace('MainThreadSecretState#setPassword: Encrypting password for: ', key);
const encrypted = await this.encryptionService.encrypt(toEncrypt);
this.logService.trace('MainThreadSecretState#setPassword: Storing password for: ', key);
return await this.credentialsService.setPassword(fullKey, key, encrypted);
}
async $deletePassword(extensionId: string, key: string): Promise<void> {
try {
const fullKey = await this.getFullKey(extensionId);
await this.credentialsService.deletePassword(fullKey, key);
} catch (_) {
throw new Error('Cannot delete password');
}
}
}
@extHostNamedCustomer(MainContext.MainThreadSecretState)
export class MainThreadSecretState extends Disposable implements MainThreadSecretStateShape {
private readonly _proxy: ExtHostSecretStateShape;
// TODO: Remove this when all known embedders implement a secret storage provider
private readonly _oldMainThreadSecretState: OldMainThreadSecretState | undefined;
private readonly _sequencer = new SequencerByKey<string>();
// TODO: Remove this when we remove the old API
private secretStoragePrefix = this.credentialsService.getSecretStoragePrefix();
constructor(
extHostContext: IExtHostContext,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@ILogService private readonly logService: ILogService,
// TODO: Remove this when we remove the old API
@ICredentialsService private readonly credentialsService: ICredentialsService,
// TODO: Remove this when we remove the old API
@IEncryptionService private readonly encryptionService: IEncryptionService,
@IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService,
@IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSecretState);
// If the embedder doesn't implement a secret storage provider, then we need to use the old API
// to ensure that secrets are still stored in a secure way. This is only temporary until all
// embedders implement a secret storage provider.
// TODO: Remove this when all known embedders implement a secret storage provider
if (environmentService.options?.credentialsProvider && !environmentService.options?.secretStorageProvider) {
this._oldMainThreadSecretState = this._register(new OldMainThreadSecretState(
this._proxy,
credentialsService,
encryptionService,
logService
));
}
this._register(this.secretStorageService.onDidChangeSecret((e: string) => {
try {
const { extensionId, key } = this.parseKey(e);
@@ -163,23 +45,9 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre
}
private async doGetPassword(extensionId: string, key: string): Promise<string | undefined> {
// TODO: Remove this when all known embedders implement a secret storage provider
if (this._oldMainThreadSecretState) {
return await this._oldMainThreadSecretState.$getPassword(extensionId, key);
}
const fullKey = this.getKey(extensionId, key);
const password = await this.secretStorageService.get(fullKey);
if (!password) {
this.logService.trace('[mainThreadSecretState] No password found for: ', extensionId, key);
// TODO: Remove this when we remove the old API
const password = await this.getAndDeleteOldPassword(extensionId, key);
return password;
}
this.logService.trace('[mainThreadSecretState] Password found for: ', extensionId, key);
this.logService.trace(`[mainThreadSecretState] ${password ? 'P' : 'No p'}assword found for: `, extensionId, key);
return password;
}
@@ -189,11 +57,6 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre
}
private async doSetPassword(extensionId: string, key: string, value: string): Promise<void> {
// TODO: Remove this when all known embedders implement a secret storage provider
if (this._oldMainThreadSecretState) {
return await this._oldMainThreadSecretState.$setPassword(extensionId, key, value);
}
const fullKey = this.getKey(extensionId, key);
await this.secretStorageService.set(fullKey, value);
this.logService.trace('[mainThreadSecretState] Password set for: ', extensionId, key);
@@ -205,11 +68,6 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre
}
private async doDeletePassword(extensionId: string, key: string): Promise<void> {
// TODO: Remove this when all known embedders implement a secret storage provider
if (this._oldMainThreadSecretState) {
return await this._oldMainThreadSecretState.$deletePassword(extensionId, key);
}
const fullKey = this.getKey(extensionId, key);
await this.secretStorageService.delete(fullKey);
this.logService.trace('[mainThreadSecretState] Password deleted for: ', extensionId, key);
@@ -222,83 +80,4 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre
private parseKey(key: string): { extensionId: string; key: string } {
return JSON.parse(key);
}
//#region Old API
// Delete this all when we remove the old API
private async getAndDeleteOldPassword(extensionId: string, key: string): Promise<string | undefined> {
const password = await this.getOldPassword(extensionId, key);
if (password) {
const fullKey = this.getKey(extensionId, key);
this.logService.trace('[mainThreadSecretState] Setting old password to new location for: ', extensionId, key);
await this.secretStorageService.set(fullKey, password);
this.logService.trace('[mainThreadSecretState] Old Password set to new location for: ', extensionId, key);
if (this.secretStorageService.type === 'persisted') {
this.logService.trace('[mainThreadSecretState] Deleting old password for since it was persisted in the new location: ', extensionId, key);
await this.deleteOldPassword(extensionId, key);
}
}
return password;
}
private async getOldPassword(extensionId: string, key: string): Promise<string | undefined> {
this.logService.trace(`[mainThreadSecretState] Getting old password for ${extensionId} extension: `, key);
const fullKey = `${await this.secretStoragePrefix}${extensionId}`;
const password = await this.credentialsService.getPassword(fullKey, key);
if (!password) {
this.logService.trace('[mainThreadSecretState] No old password found for: ', extensionId, key);
return undefined;
}
let decrypted: string | null;
try {
this.logService.trace('[mainThreadSecretState] Decrypting old password for: ', extensionId, key);
decrypted = await this.encryptionService.decrypt(password);
} catch (e) {
this.logService.error(e);
this.logService.trace('[mainThreadSecretState] Trying old migration for: ', extensionId, key);
// If we are on a platform that newly started encrypting secrets before storing them,
// then passwords previously stored were stored un-encrypted (NOTE: but still being stored in a secure keyring).
// When we try to decrypt a password that wasn't encrypted previously, the encryption service will throw.
// To recover gracefully, we first try to encrypt & store the password (essentially migrating the secret to the new format)
// and then we try to read it and decrypt again.
const encryptedForSet = await this.encryptionService.encrypt(password);
await this.credentialsService.setPassword(fullKey, key, encryptedForSet);
const passwordEncrypted = await this.credentialsService.getPassword(fullKey, key);
decrypted = passwordEncrypted && await this.encryptionService.decrypt(passwordEncrypted);
}
if (decrypted) {
try {
const value = JSON.parse(decrypted);
if (value.extensionId === extensionId) {
this.logService.trace('[mainThreadSecretState] Old password found for: ', extensionId, key);
return value.content;
}
} catch (parseError) {
// We may not be able to parse it, but we keep the secret in the keychain anyway just in case
// it decrypts correctly in the future.
this.logService.error(parseError);
return undefined;
}
}
this.logService.trace('[mainThreadSecretState] No old password found for: ', extensionId, key);
return undefined;
}
private async deleteOldPassword(extensionId: string, key: string): Promise<void> {
try {
const fullKey = `${await this.secretStoragePrefix}${extensionId}`;
this.logService.trace(`[mainThreadSecretState] Deleting old password for ${extensionId} extension: `, key);
await this.credentialsService.deletePassword(fullKey, key);
this.logService.trace('[mainThreadSecretState] Old password deleted for: ', extensionId, key);
} catch (_) {
throw new Error('Cannot delete password');
}
}
//#endregion
}

View File

@@ -298,14 +298,6 @@ export interface MainThreadConsoleShape extends IDisposable {
$logExtensionHostMessage(msg: IRemoteConsoleLog): void;
}
export interface MainThreadKeytarShape extends IDisposable {
$getPassword(service: string, account: string): Promise<string | null>;
$setPassword(service: string, account: string, password: string): Promise<void>;
$deletePassword(service: string, account: string): Promise<boolean>;
$findPassword(service: string): Promise<string | null>;
$findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;
}
export interface IRegExpDto {
pattern: string;
flags?: string;
@@ -2631,7 +2623,6 @@ export const MainContext = {
MainThreadErrors: createProxyIdentifier<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createProxyIdentifier<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadDownloadService: createProxyIdentifier<MainThreadDownloadServiceShape>('MainThreadDownloadService'),
MainThreadKeytar: createProxyIdentifier<MainThreadKeytarShape>('MainThreadKeytar'),
MainThreadLanguageFeatures: createProxyIdentifier<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
MainThreadLanguages: createProxyIdentifier<MainThreadLanguagesShape>('MainThreadLanguages'),
MainThreadLogger: createProxyIdentifier<MainThreadLoggerShape>('MainThreadLogger'),

View File

@@ -15,7 +15,6 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ExtensionPaths, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { platform } from 'vs/base/common/process';
import { ILogService } from 'vs/platform/log/common/log';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
@@ -61,7 +60,6 @@ export abstract class RequireInterceptor {
const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex();
this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider, this._logService));
this.register(this._instaService.createInstance(KeytarNodeModuleFactory, extensionPaths));
this.register(this._instaService.createInstance(NodeModuleAliasingModuleFactory));
if (this._initData.remote.isRemote) {
this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths, this._initData.environment.appUriScheme));
@@ -187,97 +185,6 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory {
//#endregion
//#region --- keytar-module
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
findCredentials(service: string): Promise<Array<{ account: string; password: string }>>;
}
class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName: string = 'keytar';
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
private alternativeNames: Set<string> | undefined;
private _impl: IKeytarModule;
constructor(
private readonly _extensionPaths: ExtensionPaths,
@IExtHostRpcService rpcService: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
this._mainThreadTelemetry = rpcService.getProxy(MainContext.MainThreadTelemetry);
const { environment } = initData;
const mainThreadKeytar = rpcService.getProxy(MainContext.MainThreadKeytar);
if (environment.appRoot) {
let appRoot = environment.appRoot.fsPath;
if (platform === 'win32') {
appRoot = appRoot.replace(/\\/g, '/');
}
if (appRoot[appRoot.length - 1] === '/') {
appRoot = appRoot.substr(0, appRoot.length - 1);
}
this.alternativeNames = new Set();
this.alternativeNames.add(`${appRoot}/node_modules.asar/keytar`);
this.alternativeNames.add(`${appRoot}/node_modules/keytar`);
}
this._impl = {
getPassword: (service: string, account: string): Promise<string | null> => {
return mainThreadKeytar.$getPassword(service, account);
},
setPassword: (service: string, account: string, password: string): Promise<void> => {
return mainThreadKeytar.$setPassword(service, account, password);
},
deletePassword: (service: string, account: string): Promise<boolean> => {
return mainThreadKeytar.$deletePassword(service, account);
},
findPassword: (service: string): Promise<string | null> => {
return mainThreadKeytar.$findPassword(service);
},
findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
return mainThreadKeytar.$findCredentials(service);
}
};
}
public load(_request: string, parent: URI): any {
const ext = this._extensionPaths.findSubstr(parent);
type ShimmingKeytarClassification = {
owner: 'jrieken';
comment: 'Know when the keytar-shim was used';
extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension is question' };
};
this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingKeytarClassification>('shimming.keytar', { extension: ext?.identifier.value ?? 'unknown_extension' });
return this._impl;
}
public alternativeModuleName(name: string): string | undefined {
const length = name.length;
// We need at least something like: `?/keytar` which requires
// more than 7 characters.
if (length <= 7 || !this.alternativeNames) {
return undefined;
}
const sep = length - 7;
if ((name.charAt(sep) === '/' || name.charAt(sep) === '\\') && name.endsWith('keytar')) {
name = name.replace(/\\/g, '/');
if (this.alternativeNames.has(name)) {
return 'keytar';
}
}
return undefined;
}
}
//#endregion
//#region --- opn/open-module
interface OpenOptions {

View File

@@ -36,7 +36,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
import { ViewContainerLocation } from 'vs/workbench/common/views';
import { IPaneCompositePart } from 'vs/workbench/browser/parts/paneCompositePart';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ILogService } from 'vs/platform/log/common/log';
@@ -234,7 +233,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
private readonly problematicProviders: Set<string> = new Set();
private initialized = false;
private sessionFromEmbedder = new Lazy<Promise<AuthenticationSessionInfo | undefined>>(() => getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService));
private sessionFromEmbedder = new Lazy<Promise<AuthenticationSessionInfo | undefined>>(() => getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService));
constructor(
action: ActivityAction,
@@ -254,7 +253,6 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
@IStorageService private readonly storageService: IStorageService,
@IKeybindingService keybindingService: IKeybindingService,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@ICredentialsService private readonly credentialsService: ICredentialsService,
@ILogService private readonly logService: ILogService
) {
super(MenuId.AccountsContext, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService);

View File

@@ -12,7 +12,6 @@ import type { IUpdateProvider } from 'vs/workbench/services/update/browser/updat
import type { Event } from 'vs/base/common/event';
import type { IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService';
import type { IProductConfiguration } from 'vs/base/common/product';
import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials';
import type { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets';
import type { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel';
import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress';
@@ -210,12 +209,6 @@ export interface IWorkbenchConstructionOptions {
*/
readonly settingsSyncOptions?: ISettingsSyncOptions;
/**
* The credentials provider to store and retrieve secrets.
* TODO: Remove this in favor of the secret storage provider.
*/
readonly credentialsProvider?: ICredentialsProvider;
/**
* The secret storage provider to store and retrieve secrets.
*/

View File

@@ -62,9 +62,7 @@ import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } fr
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { mixin, safeStringify } from 'vs/base/common/objects';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { IndexedDB } from 'vs/base/browser/indexedDB';
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService';
import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -389,10 +387,6 @@ export class BrowserMain extends Disposable {
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Credentials Service
const credentialsService = new BrowserCredentialsService(environmentService, remoteAgentService, productService);
serviceCollection.set(ICredentialsService, credentialsService);
const encryptionService = new EncryptionService();
serviceCollection.set(IEncryptionService, encryptionService);
const secretStorageService = new BrowserSecretStorageService(storageService, encryptionService, environmentService, logService);
@@ -400,7 +394,7 @@ export class BrowserMain extends Disposable {
// Userdata Initialize Service
const userDataInitializers: IUserDataInitializer[] = [];
userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService));
userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService));
if (environmentService.options.profile) {
userDataInitializers.push(new UserDataProfileInitializer(environmentService, fileService, userDataProfileService, storageService, logService, uriIdentityService, requestService));
}
@@ -497,7 +491,6 @@ export class BrowserMain extends Disposable {
const dialogService = accessor.get(IDialogService);
const hostService = accessor.get(IHostService);
const storageService = accessor.get(IStorageService);
const credentialsService = accessor.get(ICredentialsService);
const logService = accessor.get(ILogService);
const result = await dialogService.confirm({
message: localize('reset user data message', "Would you like to reset your data (settings, keybindings, extensions, snippets and UI State) and reload?")
@@ -509,9 +502,6 @@ export class BrowserMain extends Disposable {
if (storageService instanceof BrowserStorageService) {
await storageService.clear();
}
if (typeof credentialsService.clear === 'function') {
await credentialsService.clear();
}
} catch (error) {
logService.error(error);
throw error;

View File

@@ -18,7 +18,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource, EDIT_SESSIONS_PENDING_KEY } from 'vs/workbench/contrib/editSessions/common/editSessions';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { generateUuid } from 'vs/base/common/uuid';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService';
import { isWeb } from 'vs/base/common/platform';
import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
@@ -82,8 +81,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
@IProductService private readonly productService: IProductService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IDialogService private readonly dialogService: IDialogService,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@ICredentialsService private readonly credentialsService: ICredentialsService
@ISecretStorageService private readonly secretStorageService: ISecretStorageService
) {
super();
@@ -280,7 +278,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
// If settings sync is already enabled, avoid asking again to authenticate
if (this.shouldAttemptEditSessionInit()) {
this.logService.info(`Reusing user data sync enablement`);
const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService);
const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService);
if (authenticationSessionInfo !== undefined) {
this.logService.info(`Using current authentication session with ID ${authenticationSessionInfo.id}`);
this.existingSessionId = authenticationSessionInfo.id;

View File

@@ -13,7 +13,6 @@ import * as nls from 'vs/nls';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Severity } from 'vs/platform/notification/common/notification';
@@ -80,14 +79,10 @@ export function addAccountUsage(storageService: IStorageService, providerId: str
// TODO: pull this out into its own service
export type AuthenticationSessionInfo = { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean };
export async function getCurrentAuthenticationSessionInfo(
// TODO: Remove when all known embedders implement SecretStorageProviders instead of CredentialsProviders
credentialsService: ICredentialsService,
secretStorageService: ISecretStorageService,
productService: IProductService
): Promise<AuthenticationSessionInfo | undefined> {
const authenticationSessionValue =
await secretStorageService.get(`${productService.urlProtocol}.loginAccount`)
?? await credentialsService.getPassword(`${productService.urlProtocol}.login`, 'account');
const authenticationSessionValue = await secretStorageService.get(`${productService.urlProtocol}.loginAccount`);
if (authenticationSessionValue) {
try {
const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue);

View File

@@ -1,82 +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 { ICredentialsService, ICredentialsProvider, ICredentialsChangeEvent, InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IProductService } from 'vs/platform/product/common/productService';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
export class BrowserCredentialsService extends Disposable implements ICredentialsService {
declare readonly _serviceBrand: undefined;
private _onDidChangePassword = this._register(new Emitter<ICredentialsChangeEvent>());
readonly onDidChangePassword = this._onDidChangePassword.event;
private credentialsProvider: ICredentialsProvider;
private _secretStoragePrefix: Promise<string>;
public async getSecretStoragePrefix() { return this._secretStoragePrefix; }
constructor(
@IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService,
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@IProductService private readonly productService: IProductService
) {
super();
if (
environmentService.remoteAuthority
&& !environmentService.options?.credentialsProvider
&& !environmentService.options?.secretStorageProvider
) {
// If we have a remote authority but the embedder didn't provide a credentialsProvider,
// we can use the CredentialsService on the remote side
const remoteCredentialsService = ProxyChannel.toService<ICredentialsService>(remoteAgentService.getConnection()!.getChannel('credentials'));
this.credentialsProvider = remoteCredentialsService;
this._secretStoragePrefix = remoteCredentialsService.getSecretStoragePrefix();
} else {
// fall back to InMemoryCredentialsProvider if none was given to us.
this.credentialsProvider = environmentService.options?.credentialsProvider ?? new InMemoryCredentialsProvider();
this._secretStoragePrefix = Promise.resolve(this.productService.urlProtocol);
}
}
getPassword(service: string, account: string): Promise<string | null> {
return this.credentialsProvider.getPassword(service, account);
}
async setPassword(service: string, account: string, password: string): Promise<void> {
await this.credentialsProvider.setPassword(service, account, password);
this._onDidChangePassword.fire({ service, account });
}
async deletePassword(service: string, account: string): Promise<boolean> {
const didDelete = await this.credentialsProvider.deletePassword(service, account);
if (didDelete) {
this._onDidChangePassword.fire({ service, account });
}
return didDelete;
}
findPassword(service: string): Promise<string | null> {
return this.credentialsProvider.findPassword(service);
}
findCredentials(service: string): Promise<Array<{ account: string; password: string }>> {
return this.credentialsProvider.findCredentials(service);
}
async clear(): Promise<void> {
if (this.credentialsProvider.clear) {
return this.credentialsProvider.clear();
}
}
}

View File

@@ -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 { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
registerMainProcessRemoteService(ICredentialsService, 'credentials');

View File

@@ -1,61 +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 { DisposableStore } from 'vs/base/common/lifecycle';
import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService';
import { TestEnvironmentService, TestRemoteAgentService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices';
suite('CredentialsService - web', () => {
const serviceId1 = 'test.credentialsService1';
const serviceId2 = 'test.credentialsService2';
const disposables = new DisposableStore();
let credentialsService: BrowserCredentialsService;
setup(async () => {
credentialsService = disposables.add(new BrowserCredentialsService(TestEnvironmentService, new TestRemoteAgentService(), TestProductService));
await credentialsService.setPassword(serviceId1, 'me1', '1');
await credentialsService.setPassword(serviceId1, 'me2', '2');
await credentialsService.setPassword(serviceId2, 'me3', '3');
});
teardown(() => disposables.clear());
test('Gets correct values for service', async () => {
const credentials = await credentialsService.findCredentials(serviceId1);
assert.strictEqual(credentials.length, 2);
assert.strictEqual(credentials[0].password, '1');
});
test('Gets correct value for credential', async () => {
const credentials = await credentialsService.getPassword(serviceId1, 'me1');
assert.strictEqual(credentials, '1');
});
test('Gets null for no account', async () => {
const credentials = await credentialsService.getPassword(serviceId1, 'doesnotexist');
assert.strictEqual(credentials, null);
});
test('Gets null for no service or a different service', async () => {
let credentials = await credentialsService.getPassword('doesnotexist', 'me1');
assert.strictEqual(credentials, null);
credentials = await credentialsService.getPassword(serviceId2, 'me1');
assert.strictEqual(credentials, null);
});
test('Delete removes the value', async () => {
const result = await credentialsService.deletePassword(serviceId1, 'me1');
assert.strictEqual(result, true);
const pass = await credentialsService.getPassword(serviceId1, 'me1');
assert.strictEqual(pass, null);
});
test('Clear removes all values for service', async () => {
await credentialsService.clear();
const credentials = await credentialsService.findCredentials(serviceId1);
assert.strictEqual(credentials.length, 0);
});
});

View File

@@ -30,7 +30,6 @@ import { isEqual } from 'vs/base/common/resources';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { TasksInitializer } from 'vs/platform/userDataSync/common/tasksSync';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
@@ -48,7 +47,6 @@ export class UserDataSyncInitializer implements IUserDataInitializer {
constructor(
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@ICredentialsService private readonly credentialsService: ICredentialsService,
@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
@IFileService private readonly fileService: IFileService,
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
@@ -92,7 +90,7 @@ export class UserDataSyncInitializer implements IUserDataInitializer {
let authenticationSession;
try {
authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService);
authenticationSession = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService);
} catch (error) {
this.logService.error(error);
}

View File

@@ -30,7 +30,6 @@ import { isWeb } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncStoreTypeSynchronizer } from 'vs/platform/userDataSync/common/globalStateSync';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { CancellationError } from 'vs/base/common/errors';
import { raceCancellationError } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
@@ -105,7 +104,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
@IProductService private readonly productService: IProductService,
@IExtensionService private readonly extensionService: IExtensionService,
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
@ICredentialsService private readonly credentialsService: ICredentialsService,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IProgressService private readonly progressService: IProgressService,
@@ -172,7 +170,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
private async initialize(): Promise<void> {
if (isWeb) {
const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService);
const authenticationSession = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService);
if (this.currentSessionId === undefined && authenticationSession?.id) {
if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider && this.environmentService.options.settingsSyncOptions.enabled) {
this.currentSessionId = authenticationSession.id;

View File

@@ -56,7 +56,6 @@ import 'vs/workbench/services/path/electron-sandbox/pathService';
import 'vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService';
import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService';
import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService';
import 'vs/workbench/services/credentials/electron-sandbox/credentialsService';
import 'vs/workbench/services/encryption/electron-sandbox/encryptionService';
import 'vs/workbench/services/secrets/electron-sandbox/secretStorageService';
import 'vs/workbench/services/localization/electron-sandbox/languagePackService';

View File

@@ -43,7 +43,6 @@ import 'vs/workbench/services/extensionManagement/browser/webExtensionsScannerSe
import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService';
import 'vs/workbench/services/extensionManagement/browser/extensionUrlTrustService';
import 'vs/workbench/services/telemetry/browser/telemetryService';
import 'vs/workbench/services/credentials/browser/credentialsService';
import 'vs/workbench/services/url/browser/urlService';
import 'vs/workbench/services/update/browser/updateService';
import 'vs/workbench/services/workspaces/browser/workspacesService';

View File

@@ -1006,11 +1006,6 @@
resolved "https://registry.yarnpkg.com/@types/kerberos/-/kerberos-1.1.2.tgz#2a774abd48f727852f697d74241e9de3aea8e646"
integrity sha512-cLixfcXjdj7qohLasmC1G4fh+en4e4g7mFZiG38D+K9rS9BRKFlq1JH5dGkQzICckbu4wM+RcwSa4VRHlBg7Rg==
"@types/keytar@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.0.tgz#ca24e6ee6d0df10c003aafe26e93113b8faf0d8e"
integrity sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA==
"@types/keyv@^3.1.4":
version "3.1.4"
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
@@ -1826,11 +1821,6 @@ append-buffer@^1.0.2:
dependencies:
buffer-equal "^1.0.0"
aproba@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
arch@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
@@ -1846,14 +1836,6 @@ are-docs-informative@^0.0.2:
resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963"
integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@@ -2847,11 +2829,6 @@ config-chain@^1.1.12:
ini "^1.3.4"
proto-list "~1.2.1"
console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
content-disposition@^0.5.4, content-disposition@~0.5.2:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@@ -4651,20 +4628,6 @@ functional-red-black-tree@^1.0.1:
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
dependencies:
aproba "^1.0.3"
console-control-strings "^1.0.0"
has-unicode "^2.0.0"
object-assign "^4.1.0"
signal-exit "^3.0.0"
string-width "^1.0.1"
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -5256,11 +5219,6 @@ has-symbols@^1.0.2:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
has-value@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -6238,14 +6196,6 @@ keygrip@~1.1.0:
dependencies:
tsscmp "1.0.6"
keytar@7.9.0:
version "7.9.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb"
integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==
dependencies:
node-addon-api "^4.3.0"
prebuild-install "^7.0.1"
keyv@^4.0.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56"
@@ -7265,16 +7215,6 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
npmlog@^4.0.1:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
dependencies:
are-we-there-yet "~1.1.2"
console-control-strings "~1.1.0"
gauge "~2.7.3"
set-blocking "~2.0.0"
nth-check@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
@@ -7294,7 +7234,7 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0:
object-assign@4.X, object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -8238,25 +8178,6 @@ prebuild-install@7.1.1:
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
prebuild-install@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870"
integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==
dependencies:
detect-libc "^2.0.0"
expand-template "^2.0.3"
github-from-package "0.0.0"
minimist "^1.2.3"
mkdirp-classic "^0.5.3"
napi-build-utils "^1.0.1"
node-abi "^3.3.0"
npmlog "^4.0.1"
pump "^3.0.0"
rc "^1.2.7"
simple-get "^4.0.0"
tar-fs "^2.0.0"
tunnel-agent "^0.6.0"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -8468,7 +8389,7 @@ read-pkg@^3.0.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -8928,7 +8849,7 @@ serialize-javascript@6.0.0, serialize-javascript@^6.0.0:
dependencies:
randombytes "^2.1.0"
set-blocking@^2.0.0, set-blocking@~2.0.0:
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
@@ -9352,7 +9273,7 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2", string-width@^2.1.0:
string-width@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -10644,13 +10565,6 @@ which@^1.2.14, which@^1.2.9:
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
dependencies:
string-width "^1.0.2 || 2"
wildcard@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"