Skip to content

Commit 79f3509

Browse files
authored
Merge branch 'main' into merogge/editor-h
2 parents 1b3a0bc + dd2441f commit 79f3509

File tree

15 files changed

+192
-99
lines changed

15 files changed

+192
-99
lines changed

cli/src/bin/code/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ fn make_logger(core: &args::CliCore) -> log::Logger {
133133
let tracer = SdkTracerProvider::builder().build().tracer("codecli");
134134
let mut log = log::Logger::new(tracer, log_level);
135135
if let Some(f) = &core.global_options.log_to_file {
136-
log = log.tee(log::FileLogSink::new(log_level, f).expect("expected to make file logger"))
136+
log = log
137+
.with_sink(log::FileLogSink::new(log_level, f).expect("expected to make file logger"))
137138
}
138139

139140
log

cli/src/tunnels/service_windows.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
use async_trait::async_trait;
77
use shell_escape::windows::escape as shell_escape;
8+
use std::os::windows::process::CommandExt;
89
use std::{
910
path::PathBuf,
1011
process::{Command, Stdio},
1112
};
13+
use winapi::um::winbase::{CREATE_NEW_PROCESS_GROUP, DETACHED_PROCESS};
1214
use winreg::{enums::HKEY_CURRENT_USER, RegKey};
1315

1416
use crate::{
@@ -21,6 +23,8 @@ use crate::{
2123

2224
use super::service::{tail_log_file, ServiceContainer, ServiceManager as CliServiceManager};
2325

26+
const DID_LAUNCH_AS_HIDDEN_PROCESS: &str = "VSCODE_CLI_DID_LAUNCH_AS_HIDDEN_PROCESS";
27+
2428
pub struct WindowsService {
2529
log: log::Logger,
2630
tunnel_lock: PathBuf,
@@ -90,7 +94,24 @@ impl CliServiceManager for WindowsService {
9094
launcher_paths: LauncherPaths,
9195
mut handle: impl 'static + ServiceContainer,
9296
) -> Result<(), AnyError> {
93-
handle.run_service(self.log, launcher_paths).await
97+
if std::env::var(DID_LAUNCH_AS_HIDDEN_PROCESS).is_ok() {
98+
return handle.run_service(self.log, launcher_paths).await;
99+
}
100+
101+
// Start as a hidden subprocess to avoid showing cmd.exe on startup.
102+
// Fixes https://github.com/microsoft/vscode/issues/184058
103+
// I also tried the winapi ShowWindow, but that didn't yield fruit.
104+
Command::new(std::env::current_exe().unwrap())
105+
.args(std::env::args().skip(1))
106+
.env(DID_LAUNCH_AS_HIDDEN_PROCESS, "1")
107+
.stderr(Stdio::null())
108+
.stdout(Stdio::null())
109+
.stdin(Stdio::null())
110+
.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS)
111+
.spawn()
112+
.map_err(|e| wrap(e, "error starting nested process"))?;
113+
114+
Ok(())
94115
}
95116

96117
async fn unregister(&self) -> Result<(), AnyError> {

extensions/microsoft-authentication/extension-browser.webpack.config.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ module.exports = withBrowserDefaults({
2424
'keytar': 'commonjs keytar',
2525
},
2626
resolve: {
27-
fallback: {
28-
'querystring': require.resolve('querystring-es3')
29-
},
3027
alias: {
3128
'./node/crypto': path.resolve(__dirname, 'src/browser/crypto'),
3229
'./node/authServer': path.resolve(__dirname, 'src/browser/authServer'),

extensions/microsoft-authentication/package.json

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,58 @@
4141
{
4242
"title": "Microsoft Sovereign Cloud",
4343
"properties": {
44-
"microsoft-sovereign-cloud.endpoint": {
45-
"anyOf": [
46-
{
47-
"type": "string"
44+
"microsoft-sovereign-cloud.environment": {
45+
"type": "string",
46+
"markdownDescription": "%microsoft-sovereign-cloud.environment.description%",
47+
"enum": [
48+
"ChinaCloud",
49+
"USGovernment",
50+
"custom"
51+
],
52+
"enumDescriptions": [
53+
"%microsoft-sovereign-cloud.environment.enumDescriptions.AzureChinaCloud%",
54+
"%microsoft-sovereign-cloud.environment.enumDescriptions.AzureUSGovernment%",
55+
"%microsoft-sovereign-cloud.environment.enumDescriptions.custom%"
56+
]
57+
},
58+
"microsoft-sovereign-cloud.customEnvironment": {
59+
"type": "object",
60+
"additionalProperties": true,
61+
"markdownDescription": "%microsoft-sovereign-cloud.customEnvironment.description%",
62+
"properties": {
63+
"name": {
64+
"type": "string",
65+
"description": "%microsoft-sovereign-cloud.customEnvironment.name.description%"
66+
},
67+
"portalUrl": {
68+
"type": "string",
69+
"description": "%microsoft-sovereign-cloud.customEnvironment.portalUrl.description%"
70+
},
71+
"managementEndpointUrl": {
72+
"type": "string",
73+
"description": "%microsoft-sovereign-cloud.customEnvironment.managementEndpointUrl.description%"
4874
},
49-
{
75+
"resourceManagerEndpointUrl": {
5076
"type": "string",
51-
"enum": [
52-
"Azure China",
53-
"Azure US Government"
54-
]
77+
"description": "%microsoft-sovereign-cloud.customEnvironment.resourceManagerEndpointUrl.description%"
78+
},
79+
"activeDirectoryEndpointUrl": {
80+
"type": "string",
81+
"description": "%microsoft-sovereign-cloud.customEnvironment.activeDirectoryEndpointUrl.description%"
82+
},
83+
"activeDirectoryResourceId": {
84+
"type": "string",
85+
"description": "%microsoft-sovereign-cloud.customEnvironment.activeDirectoryResourceId.description%"
5586
}
56-
],
57-
"description": "%microsoft-sovereign-cloud.endpoint.description%"
87+
},
88+
"required": [
89+
"name",
90+
"portalUrl",
91+
"managementEndpointUrl",
92+
"resourceManagerEndpointUrl",
93+
"activeDirectoryEndpointUrl",
94+
"activeDirectoryResourceId"
95+
]
5896
}
5997
}
6098
}
@@ -75,11 +113,11 @@
75113
"@types/node-fetch": "^2.5.7",
76114
"@types/randombytes": "^2.0.0",
77115
"@types/sha.js": "^2.4.0",
78-
"@types/uuid": "8.0.0",
79-
"querystring-es3": "^0.2.1"
116+
"@types/uuid": "8.0.0"
80117
},
81118
"dependencies": {
82119
"node-fetch": "2.6.7",
120+
"@azure/ms-rest-azure-env": "^2.0.0",
83121
"@vscode/extension-telemetry": "0.7.5"
84122
},
85123
"repository": {

extensions/microsoft-authentication/package.nls.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,27 @@
33
"description": "Microsoft authentication provider",
44
"signIn": "Sign In",
55
"signOut": "Sign Out",
6-
"microsoft-sovereign-cloud.endpoint.description": "Login endpoint for Azure authentication. Select a national cloud or enter the login URL for a custom Azure cloud."
6+
"microsoft-sovereign-cloud.environment.description": {
7+
"message": "The Sovereign Cloud to use for authentication. If you select `custom`, you must also set the `#microsoft-sovereign-cloud.customEnvironment#` setting.",
8+
"comment": [
9+
"{Locked='`#microsoft-sovereign-cloud.customEnvironment#`'}",
10+
"The `#microsoft-sovereign-cloud.customEnvironment#` syntax will turn into a link. Do not translate it."
11+
]
12+
},
13+
"microsoft-sovereign-cloud.environment.enumDescriptions.AzureChinaCloud": "Azure China",
14+
"microsoft-sovereign-cloud.environment.enumDescriptions.AzureUSGovernment": "Azure US Government",
15+
"microsoft-sovereign-cloud.environment.enumDescriptions.custom": "A custom Microsoft Sovereign Cloud",
16+
"microsoft-sovereign-cloud.customEnvironment.description": {
17+
"message": "The custom configuration for the Sovereign Cloud to use with the Microsoft Sovereign Cloud authentication provider. This along with setting `#microsoft-sovereign-cloud.environment#` to `custom` is required to use this feature.",
18+
"comment": [
19+
"{Locked='`#microsoft-sovereign-cloud.environment#`'}",
20+
"The `#microsoft-sovereign-cloud.environment#` syntax will turn into a link. Do not translate it."
21+
]
22+
},
23+
"microsoft-sovereign-cloud.customEnvironment.name.description": "The name of the custom Sovereign Cloud.",
24+
"microsoft-sovereign-cloud.customEnvironment.portalUrl.description": "The portal URL for the custom Sovereign Cloud.",
25+
"microsoft-sovereign-cloud.customEnvironment.managementEndpointUrl.description": "The management endpoint for the custom Sovereign Cloud.",
26+
"microsoft-sovereign-cloud.customEnvironment.resourceManagerEndpointUrl.description": "The resource manager endpoint for the custom Sovereign Cloud.",
27+
"microsoft-sovereign-cloud.customEnvironment.activeDirectoryEndpointUrl.description": "The Active Directory endpoint for the custom Sovereign Cloud.",
28+
"microsoft-sovereign-cloud.customEnvironment.activeDirectoryResourceId.description": "The Active Directory resource ID for the custom Sovereign Cloud."
729
}

extensions/microsoft-authentication/src/AADHelper.ts

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as vscode from 'vscode';
7-
import * as querystring from 'querystring';
87
import * as path from 'path';
98
import { isSupportedEnvironment } from './utils';
109
import { generateCodeChallenge, generateCodeVerifier, randomUUID } from './cryptoUtils';
@@ -14,9 +13,10 @@ import { base64Decode } from './node/buffer';
1413
import { fetching } from './node/fetch';
1514
import { UriEventHandler } from './UriEventHandler';
1615
import TelemetryReporter from '@vscode/extension-telemetry';
16+
import { Environment } from '@azure/ms-rest-azure-env';
1717

1818
const redirectUrl = 'https://vscode.dev/redirect';
19-
const defaultLoginEndpointUrl = 'https://login.microsoftonline.com/';
19+
const defaultActiveDirectoryEndpointUrl = Environment.AzureCloud.activeDirectoryEndpointUrl;
2020
const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56';
2121
const DEFAULT_TENANT = 'organizations';
2222
const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad';
@@ -102,7 +102,7 @@ export class AzureActiveDirectoryService {
102102
private readonly _uriHandler: UriEventHandler,
103103
private readonly _tokenStorage: BetterTokenStorage<IStoredSession>,
104104
private readonly _telemetryReporter: TelemetryReporter,
105-
private readonly _loginEndpointUrl: string = defaultLoginEndpointUrl
105+
private readonly _env: Environment
106106
) {
107107
_context.subscriptions.push(this._tokenStorage.onDidChangeInOtherWindow((e) => this.checkForUpdates(e)));
108108
}
@@ -301,7 +301,7 @@ export class AzureActiveDirectoryService {
301301
const runsRemote = vscode.env.remoteName !== undefined;
302302
const runsServerless = vscode.env.remoteName === undefined && vscode.env.uiKind === vscode.UIKind.Web;
303303

304-
if (runsServerless && this._loginEndpointUrl !== defaultLoginEndpointUrl) {
304+
if (runsServerless && this._env.activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl) {
305305
throw new Error('Sign in to non-public clouds is not supported on the web.');
306306
}
307307

@@ -338,7 +338,7 @@ export class AzureActiveDirectoryService {
338338
code_challenge_method: 'S256',
339339
code_challenge: codeChallenge,
340340
}).toString();
341-
const loginUrl = `${this._loginEndpointUrl}${scopeData.tenant}/oauth2/v2.0/authorize?${qs}`;
341+
const loginUrl = new URL(`${scopeData.tenant}/oauth2/v2.0/authorize?${qs}`, this._env.activeDirectoryEndpointUrl).toString();
342342
const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl);
343343
await server.start();
344344

@@ -368,8 +368,8 @@ export class AzureActiveDirectoryService {
368368
const state = encodeURIComponent(callbackUri.toString(true));
369369
const codeVerifier = generateCodeVerifier();
370370
const codeChallenge = await generateCodeChallenge(codeVerifier);
371-
const signInUrl = `${this._loginEndpointUrl}${scopeData.tenant}/oauth2/v2.0/authorize`;
372-
const oauthStartQuery = new URLSearchParams({
371+
const signInUrl = new URL(`${scopeData.tenant}/oauth2/v2.0/authorize`, this._env.activeDirectoryEndpointUrl);
372+
signInUrl.search = new URLSearchParams({
373373
response_type: 'code',
374374
client_id: encodeURIComponent(scopeData.clientId),
375375
response_mode: 'query',
@@ -379,8 +379,8 @@ export class AzureActiveDirectoryService {
379379
prompt: 'select_account',
380380
code_challenge_method: 'S256',
381381
code_challenge: codeChallenge,
382-
});
383-
const uri = vscode.Uri.parse(`${signInUrl}?${oauthStartQuery.toString()}`);
382+
}).toString();
383+
const uri = vscode.Uri.parse(signInUrl.toString());
384384
vscode.env.openExternal(uri);
385385

386386
let inputBox: vscode.InputBox | undefined;
@@ -601,19 +601,15 @@ export class AzureActiveDirectoryService {
601601

602602
private async doRefreshToken(refreshToken: string, scopeData: IScopeData, sessionId?: string): Promise<IToken> {
603603
this._logger.info(`Refreshing token for scopes: ${scopeData.scopeStr}`);
604-
const postData = querystring.stringify({
604+
const postData = new URLSearchParams({
605605
refresh_token: refreshToken,
606606
client_id: scopeData.clientId,
607607
grant_type: 'refresh_token',
608608
scope: scopeData.scopesToSend
609-
});
610-
611-
const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
612-
const endpointUrl = proxyEndpoints?.microsoft || this._loginEndpointUrl;
613-
const endpoint = `${endpointUrl}${scopeData.tenant}/oauth2/v2.0/token`;
609+
}).toString();
614610

615611
try {
616-
const json = await this.fetchTokenResponse(endpoint, postData, scopeData);
612+
const json = await this.fetchTokenResponse(postData, scopeData);
617613
const token = this.convertToTokenSync(json, scopeData, sessionId);
618614
if (token.expiresIn) {
619615
this.setSessionTimeout(token.sessionId, token.refreshToken, scopeData, token.expiresIn * AzureActiveDirectoryService.REFRESH_TIMEOUT_MODIFIER);
@@ -666,8 +662,9 @@ export class AzureActiveDirectoryService {
666662
return new Promise((resolve: (value: vscode.AuthenticationSession) => void, reject) => {
667663
uriEventListener = this._uriHandler.event(async (uri: vscode.Uri) => {
668664
try {
669-
const query = querystring.parse(uri.query);
670-
let { code, nonce } = query;
665+
const query = new URLSearchParams(uri.query);
666+
let code = query.get('code');
667+
let nonce = query.get('nonce');
671668
if (Array.isArray(code)) {
672669
code = code[0];
673670
}
@@ -735,28 +732,16 @@ export class AzureActiveDirectoryService {
735732
this._logger.info(`Exchanging login code for token for scopes: ${scopeData.scopeStr}`);
736733
let token: IToken | undefined;
737734
try {
738-
const postData = querystring.stringify({
735+
const postData = new URLSearchParams({
739736
grant_type: 'authorization_code',
740737
code: code,
741738
client_id: scopeData.clientId,
742739
scope: scopeData.scopesToSend,
743740
code_verifier: codeVerifier,
744741
redirect_uri: redirectUrl
745-
});
746-
747-
let endpointUrl: string;
742+
}).toString();
748743

749-
if (this._loginEndpointUrl !== defaultLoginEndpointUrl) {
750-
// If this is for sovereign clouds, don't try using the proxy endpoint, which supports only public cloud
751-
endpointUrl = this._loginEndpointUrl;
752-
} else {
753-
const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
754-
endpointUrl = proxyEndpoints?.microsoft || this._loginEndpointUrl;
755-
}
756-
757-
const endpoint = `${endpointUrl}${scopeData.tenant}/oauth2/v2.0/token`;
758-
759-
const json = await this.fetchTokenResponse(endpoint, postData, scopeData);
744+
const json = await this.fetchTokenResponse(postData, scopeData);
760745
this._logger.info(`Exchanging login code for token (for scopes: ${scopeData.scopeStr}) succeeded!`);
761746
token = this.convertToTokenSync(json, scopeData);
762747
} catch (e) {
@@ -772,7 +757,17 @@ export class AzureActiveDirectoryService {
772757
return await this.convertToSession(token, scopeData);
773758
}
774759

775-
private async fetchTokenResponse(endpoint: string, postData: string, scopeData: IScopeData): Promise<ITokenResponse> {
760+
private async fetchTokenResponse(postData: string, scopeData: IScopeData): Promise<ITokenResponse> {
761+
let endpointUrl: string;
762+
if (this._env.activeDirectoryEndpointUrl !== defaultActiveDirectoryEndpointUrl) {
763+
// If this is for sovereign clouds, don't try using the proxy endpoint, which supports only public cloud
764+
endpointUrl = this._env.activeDirectoryEndpointUrl;
765+
} else {
766+
const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
767+
endpointUrl = proxyEndpoints?.microsoft || this._env.activeDirectoryEndpointUrl;
768+
}
769+
const endpoint = new URL(`${scopeData.tenant}/oauth2/v2.0/token`, endpointUrl);
770+
776771
let attempts = 0;
777772
while (attempts <= 3) {
778773
attempts++;
@@ -869,7 +864,7 @@ export class AzureActiveDirectoryService {
869864
refreshToken: token.refreshToken,
870865
scope: token.scope,
871866
account: token.account,
872-
endpoint: this._loginEndpointUrl,
867+
endpoint: this._env.activeDirectoryEndpointUrl,
873868
});
874869
this._logger.info(`Stored token for scopes: ${scopeData.scopeStr}`);
875870
}
@@ -933,9 +928,9 @@ export class AzureActiveDirectoryService {
933928

934929
private sessionMatchesEndpoint(session: IStoredSession): boolean {
935930
// For older sessions with no endpoint set, it can be assumed to be the default endpoint
936-
session.endpoint ||= defaultLoginEndpointUrl;
931+
session.endpoint ||= defaultActiveDirectoryEndpointUrl;
937932

938-
return session.endpoint === this._loginEndpointUrl;
933+
return session.endpoint === this._env.activeDirectoryEndpointUrl;
939934
}
940935

941936
//#endregion

0 commit comments

Comments
 (0)