mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-19 17:58:39 +00:00
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:
committed by
GitHub
parent
a8eda33032
commit
5134662139
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
@@ -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/**
|
||||
|
||||
@@ -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==
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,7 +36,6 @@ suite('vscode - automatic language detection', () => {
|
||||
"outDir": "../out/vs",
|
||||
"target": "es2020",
|
||||
"types": [
|
||||
"keytar",
|
||||
"mocha",
|
||||
"semver",
|
||||
"sinon",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
214
remote/yarn.lock
214
remote/yarn.lock
@@ -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"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "../out/vs",
|
||||
"types": [
|
||||
"keytar",
|
||||
"mocha",
|
||||
"semver",
|
||||
"sinon",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ import './mainThreadErrors';
|
||||
import './mainThreadExtensionService';
|
||||
import './mainThreadFileSystem';
|
||||
import './mainThreadFileSystemEventService';
|
||||
import './mainThreadKeytar';
|
||||
import './mainThreadLanguageFeatures';
|
||||
import './mainThreadLanguages';
|
||||
import './mainThreadLogService';
|
||||
|
||||
@@ -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 {
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
94
yarn.lock
94
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user