Skip to content

Commit 919303e

Browse files
authored
Merge branch 'main' into merogge/notification
2 parents 2b2d9dd + a9fcc43 commit 919303e

File tree

58 files changed

+2617
-1977
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2617
-1977
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"files.readonlyInclude": {
3737
"**/node_modules/**": true,
3838
"**/yarn.lock": true,
39+
"**/Cargo.lock": true,
3940
"src/vs/workbench/workbench.web.main.css": true,
4041
"src/vs/workbench/workbench.desktop.main.css": true,
4142
"src/vs/workbench/workbench.desktop.main.nls.js": true,

cli/src/commands/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ pub enum OutputFormat {
548548
#[derive(Args, Clone, Debug, Default)]
549549
pub struct ExistingTunnelArgs {
550550
/// Name you'd like to assign preexisting tunnel to use to connect the tunnel
551+
/// Old option, new code sohuld just use `--name`.
551552
#[clap(long, hide = true)]
552553
pub tunnel_name: Option<String>,
553554

cli/src/commands/tunnels.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,31 @@ impl From<AuthProvider> for crate::auth::AuthProvider {
5959
}
6060
}
6161

62-
impl From<ExistingTunnelArgs> for Option<dev_tunnels::ExistingTunnel> {
63-
fn from(d: ExistingTunnelArgs) -> Option<dev_tunnels::ExistingTunnel> {
64-
if let (Some(tunnel_id), Some(tunnel_name), Some(cluster), Some(host_token)) =
65-
(d.tunnel_id, d.tunnel_name, d.cluster, d.host_token)
66-
{
62+
fn fulfill_existing_tunnel_args(
63+
d: ExistingTunnelArgs,
64+
name_arg: &Option<String>,
65+
) -> Option<dev_tunnels::ExistingTunnel> {
66+
let tunnel_name = d.tunnel_name.or_else(|| name_arg.clone());
67+
68+
match (d.tunnel_id, d.cluster, d.host_token) {
69+
(Some(tunnel_id), None, Some(host_token)) => {
70+
let i = tunnel_id.find('.')?;
6771
Some(dev_tunnels::ExistingTunnel {
68-
tunnel_id,
72+
tunnel_id: tunnel_id[..i].to_string(),
73+
cluster: tunnel_id[i + 1..].to_string(),
6974
tunnel_name,
7075
host_token,
71-
cluster,
7276
})
73-
} else {
74-
None
7577
}
78+
79+
(Some(tunnel_id), Some(cluster), Some(host_token)) => Some(dev_tunnels::ExistingTunnel {
80+
tunnel_id,
81+
tunnel_name,
82+
host_token,
83+
cluster,
84+
}),
85+
86+
_ => None,
7687
}
7788
}
7889

@@ -412,8 +423,10 @@ async fn serve_with_csa(
412423
let auth = Auth::new(&paths, log.clone());
413424
let mut dt = dev_tunnels::DevTunnels::new(&log, auth, &paths);
414425
loop {
415-
let tunnel = if let Some(d) = gateway_args.tunnel.clone().into() {
416-
dt.start_existing_tunnel(d).await
426+
let tunnel = if let Some(t) =
427+
fulfill_existing_tunnel_args(gateway_args.tunnel.clone(), &gateway_args.name)
428+
{
429+
dt.start_existing_tunnel(t).await
417430
} else {
418431
dt.start_new_launcher_tunnel(gateway_args.name.as_deref(), gateway_args.random_name)
419432
.await

cli/src/tunnels/dev_tunnels.rs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ lazy_static! {
229229
#[derive(Clone, Debug)]
230230
pub struct ExistingTunnel {
231231
/// Name you'd like to assign preexisting tunnel to use to connect to the VS Code Server
232-
pub tunnel_name: String,
232+
pub tunnel_name: Option<String>,
233233

234234
/// Token to authenticate and use preexisting tunnel
235235
pub host_token: String,
@@ -393,7 +393,12 @@ impl DevTunnels {
393393
};
394394

395395
tunnel = self
396-
.sync_tunnel_tags(&persisted.name, tunnel, &HOST_TUNNEL_REQUEST_OPTIONS)
396+
.sync_tunnel_tags(
397+
&self.client,
398+
&persisted.name,
399+
tunnel,
400+
&HOST_TUNNEL_REQUEST_OPTIONS,
401+
)
397402
.await?;
398403

399404
let locator = TunnelLocator::try_from(&tunnel).unwrap();
@@ -532,6 +537,7 @@ impl DevTunnels {
532537
/// other version tags.
533538
async fn sync_tunnel_tags(
534539
&self,
540+
client: &TunnelManagementClient,
535541
name: &str,
536542
tunnel: Tunnel,
537543
options: &TunnelRequestOptions,
@@ -558,7 +564,7 @@ impl DevTunnels {
558564
let result = spanf!(
559565
self.log,
560566
self.log.span("dev-tunnel.protocol-tag-update"),
561-
self.client.update_tunnel(&tunnel_update, options)
567+
client.update_tunnel(&tunnel_update, options)
562568
);
563569

564570
result.map_err(|e| wrap(e, "tunnel tag update failed").into())
@@ -639,6 +645,12 @@ impl DevTunnels {
639645
Ok(())
640646
}
641647

648+
fn get_placeholder_name() -> String {
649+
let mut n = clean_hostname_for_tunnel(&gethostname::gethostname().to_string_lossy());
650+
n.make_ascii_lowercase();
651+
n
652+
}
653+
642654
async fn get_name_for_tunnel(
643655
&mut self,
644656
preferred_name: Option<&str>,
@@ -670,10 +682,7 @@ impl DevTunnels {
670682
use_random_name = true;
671683
}
672684

673-
let mut placeholder_name =
674-
clean_hostname_for_tunnel(&gethostname::gethostname().to_string_lossy());
675-
placeholder_name.make_ascii_lowercase();
676-
685+
let mut placeholder_name = Self::get_placeholder_name();
677686
if !is_name_free(&placeholder_name) {
678687
for i in 2.. {
679688
let fixed_name = format!("{}{}", placeholder_name, i);
@@ -715,7 +724,10 @@ impl DevTunnels {
715724
tunnel: ExistingTunnel,
716725
) -> Result<ActiveTunnel, AnyError> {
717726
let tunnel_details = PersistedTunnel {
718-
name: tunnel.tunnel_name,
727+
name: match tunnel.tunnel_name {
728+
Some(n) => n,
729+
None => Self::get_placeholder_name(),
730+
},
719731
id: tunnel.tunnel_id,
720732
cluster: tunnel.cluster,
721733
};
@@ -725,10 +737,23 @@ impl DevTunnels {
725737
tunnel.host_token.clone(),
726738
));
727739

740+
let client = mgmt.into();
741+
self.sync_tunnel_tags(
742+
&client,
743+
&tunnel_details.name,
744+
Tunnel {
745+
cluster_id: Some(tunnel_details.cluster.clone()),
746+
tunnel_id: Some(tunnel_details.id.clone()),
747+
..Default::default()
748+
},
749+
&HOST_TUNNEL_REQUEST_OPTIONS,
750+
)
751+
.await?;
752+
728753
self.start_tunnel(
729754
tunnel_details.locator(),
730755
&tunnel_details,
731-
mgmt.into(),
756+
client,
732757
StaticAccessTokenProvider::new(tunnel.host_token),
733758
)
734759
.await

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ module.exports = withBrowserDefaults({
2121
'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js'),
2222
'./node/authServer': path.resolve(__dirname, 'src/browser/authServer'),
2323
'./node/crypto': path.resolve(__dirname, 'src/browser/crypto'),
24-
'./node/fetch': path.resolve(__dirname, 'src/browser/fetch')
24+
'./node/fetch': path.resolve(__dirname, 'src/browser/fetch'),
25+
'./node/buffer': path.resolve(__dirname, 'src/browser/buffer'),
2526
}
2627
}
2728
});

src/vscode-dts/vscode.proposed.quickPickItemIcon.d.ts renamed to extensions/github-authentication/src/browser/buffer.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,6 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
declare module 'vscode' {
7-
/**
8-
* Represents an item that can be selected from
9-
* a list of items.
10-
*/
11-
export interface QuickPickItem {
12-
/**
13-
* The icon path or {@link ThemeIcon} for the QuickPickItem.
14-
*/
15-
iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon;
16-
}
6+
export function base64Encode(text: string): string {
7+
return btoa(text);
178
}

extensions/github-authentication/src/common/logger.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ export class Log {
2626
this.output.error(message);
2727
}
2828

29+
public warn(message: string): void {
30+
this.output.warn(message);
31+
}
2932
}

extensions/github-authentication/src/flows.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ const allFlows: IFlow[] = [
201201
supportsGitHubEnterpriseServer: false,
202202
supportsHostedGitHubEnterprise: true,
203203
supportsRemoteExtensionHost: true,
204-
supportsWebWorkerExtensionHost: true,
204+
// Web worker can't open a port to listen for the redirect
205+
supportsWebWorkerExtensionHost: false,
205206
// exchanging a code for a token requires a client secret
206207
supportsNoClientSecret: false,
207208
supportsSupportedClients: true,

extensions/github-authentication/src/github.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
363363
sessions.splice(sessionIndex, 1);
364364

365365
await this.storeSessions(sessions);
366+
await this._githubServer.logout(session);
366367

367368
this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] });
368369
} else {

extensions/github-authentication/src/githubServer.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { crypto } from './node/crypto';
1212
import { fetching } from './node/fetch';
1313
import { ExtensionHost, GitHubTarget, getFlows } from './flows';
1414
import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
15+
import { Config } from './config';
16+
import { base64Encode } from './node/buffer';
1517

1618
// This is the error message that we throw if the login was cancelled for any reason. Extensions
1719
// calling `getSession` can handle this error to know that the user cancelled the login.
@@ -22,6 +24,7 @@ const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect';
2224

2325
export interface IGitHubServer {
2426
login(scopes: string): Promise<string>;
27+
logout(session: vscode.AuthenticationSession): Promise<void>;
2528
getUserInfo(token: string): Promise<{ id: string; accountName: string }>;
2629
sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise<void>;
2730
friendlyName: string;
@@ -78,9 +81,14 @@ export class GitHubServer implements IGitHubServer {
7881
}
7982

8083
// TODO@joaomoreno TODO@TylerLeonhardt
84+
private _isNoCorsEnvironment: boolean | undefined;
8185
private async isNoCorsEnvironment(): Promise<boolean> {
86+
if (this._isNoCorsEnvironment !== undefined) {
87+
return this._isNoCorsEnvironment;
88+
}
8289
const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/dummy`));
83-
return (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority));
90+
this._isNoCorsEnvironment = (uri.scheme === 'https' && /^((insiders\.)?vscode|github)\./.test(uri.authority)) || (uri.scheme === 'http' && /^localhost/.test(uri.authority));
91+
return this._isNoCorsEnvironment;
8492
}
8593

8694
public async login(scopes: string): Promise<string> {
@@ -144,6 +152,58 @@ export class GitHubServer implements IGitHubServer {
144152
throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.');
145153
}
146154

155+
public async logout(session: vscode.AuthenticationSession): Promise<void> {
156+
this._logger.trace(`Deleting session (${session.id}) from server...`);
157+
158+
if (!Config.gitHubClientSecret) {
159+
this._logger.warn('No client secret configured for GitHub authentication. The token has been deleted with best effort on this system, but we are unable to delete the token on server without the client secret.');
160+
return;
161+
}
162+
163+
// Only attempt to delete OAuth tokens. They are always prefixed with `gho_`.
164+
// https://docs.github.com/en/rest/apps/oauth-applications#about-oauth-apps-and-oauth-authorizations-of-github-apps
165+
if (!session.accessToken.startsWith('gho_')) {
166+
this._logger.warn('The token being deleted is not an OAuth token. It has been deleted locally, but we cannot delete it on server.');
167+
return;
168+
}
169+
170+
if (!isSupportedTarget(this._type, this._ghesUri)) {
171+
this._logger.trace('GitHub.com and GitHub hosted GitHub Enterprise are the only options that support deleting tokens on the server. Skipping.');
172+
return;
173+
}
174+
175+
const authHeader = 'Basic ' + base64Encode(`${Config.gitHubClientId}:${Config.gitHubClientSecret}`);
176+
const uri = this.getServerUri(`/applications/${Config.gitHubClientId}/token`);
177+
178+
try {
179+
// Defined here: https://docs.github.com/en/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-token
180+
const result = await fetching(uri.toString(true), {
181+
method: 'DELETE',
182+
headers: {
183+
Accept: 'application/vnd.github+json',
184+
Authorization: authHeader,
185+
'X-GitHub-Api-Version': '2022-11-28',
186+
'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})`
187+
},
188+
body: JSON.stringify({ access_token: session.accessToken }),
189+
});
190+
191+
if (result.status === 204) {
192+
this._logger.trace(`Successfully deleted token from session (${session.id}) from server.`);
193+
return;
194+
}
195+
196+
try {
197+
const body = await result.text();
198+
throw new Error(body);
199+
} catch (e) {
200+
throw new Error(`${result.status} ${result.statusText}`);
201+
}
202+
} catch (e) {
203+
this._logger.warn('Failed to delete token from server.' + e.message ?? e);
204+
}
205+
}
206+
147207
private getServerUri(path: string = '') {
148208
const apiUri = this.baseUri;
149209
// github.com and Hosted GitHub Enterprise instances

0 commit comments

Comments
 (0)