提交 4d064bc7 编写于 作者: B Benjamin Pasero

proxy auth - introduce new login dialog behind a setting

上级 be56be09
......@@ -157,3 +157,14 @@ export interface CrashReporterStartOptions {
*/
globalExtra?: Record<string, string>;
}
/**
* Additional information around a `app.on('login')` event.
*/
export interface AuthInfo {
isProxy: boolean;
scheme: string;
host: string;
port: number;
realm: string;
}
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron';
import { app, ipcMain as ipc, systemPreferences, shell, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
......@@ -111,17 +111,69 @@ export class CodeApplication extends Disposable {
// Dispose on shutdown
this.lifecycleMainService.onWillShutdown(() => this.dispose());
// Login handler for proxy support
if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') === true) {
let handledProxyLogins = 0;
app.on('login', (event, webContents, req, authInfo, cb) => {
if (!authInfo.isProxy) {
return; // only for proxy
}
this.logService.trace('app#login (proxy) - enter');
if (!this.windowsMainService) {
this.logService.trace('app#login (proxy) - exit - too early to handle');
return; // too early to handle
}
// Find suitable window to show dialog
const window = this.windowsMainService.getWindowByWebContents(webContents) || this.windowsMainService.getLastActiveWindow();
if (!window) {
this.logService.trace('app#login (proxy) - exit - no opened window found');
return; // unexpected
}
// Limit logins to 1 per session to avoid duplicate login prompts
handledProxyLogins++;
if (handledProxyLogins > 1) {
return; // only once
}
// Signal we handle this on our own
event.preventDefault();
// Open proxy dialog
this.logService.trace(`app#login (proxy) - asking window ${window.id} to handle proxy login`);
const payload = { authInfo, replyChannel: `vscode:proxyAuthResponse:${generateUuid()}` };
window.sendWhenReady('vscode:openProxyAuthDialog', payload);
// Handle reply
const proxyAuthResponseHandler = (event: Event, channel: string, credentials: { username: string, password: string }) => {
if (channel === payload.replyChannel) {
this.logService.trace(`app#login (proxy) - exit - received credentials from window ${window.id}`);
webContents.off('ipc-message', proxyAuthResponseHandler);
cb(credentials.username, credentials.password);
}
};
webContents.on('ipc-message', proxyAuthResponseHandler);
});
}
// Contextmenu via IPC support
registerContextMenuListener();
app.on('accessibility-support-changed', (event: Event, accessibilitySupportEnabled: boolean) => {
// Accessibility change event
app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => {
if (this.windowsMainService) {
this.windowsMainService.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
}
});
app.on('activate', (event: Event, hasVisibleWindows: boolean) => {
this.logService.trace('App#activate');
// macOS dock activate
app.on('activate', (event, hasVisibleWindows) => {
this.logService.trace('app#activate');
// Mac only event: open new window when we get activated
if (!hasVisibleWindows && this.windowsMainService) {
......@@ -134,7 +186,7 @@ export class CodeApplication extends Disposable {
// !!! DO NOT CHANGE without consulting the documentation !!!
//
app.on('remote-require', (event, sender, module) => {
this.logService.trace('App#on(remote-require): prevented');
this.logService.trace('app#on(remote-require): prevented');
event.preventDefault();
});
......@@ -164,8 +216,8 @@ export class CodeApplication extends Disposable {
event.preventDefault();
});
app.on('web-contents-created', (_event: Event, contents) => {
contents.on('will-attach-webview', (event: Event, webPreferences, params) => {
app.on('web-contents-created', (event, contents) => {
contents.on('will-attach-webview', (event, webPreferences, params) => {
const isValidWebviewSource = (source: string | undefined): boolean => {
if (!source) {
......@@ -207,7 +259,7 @@ export class CodeApplication extends Disposable {
event.preventDefault();
});
contents.on('new-window', (event: Event, url: string) => {
contents.on('new-window', (event, url) => {
event.preventDefault(); // prevent code that wants to open links
shell.openExternal(url);
......@@ -226,8 +278,8 @@ export class CodeApplication extends Disposable {
let macOpenFileURIs: IWindowOpenable[] = [];
let runningTimeout: NodeJS.Timeout | null = null;
app.on('open-file', (event: Event, path: string) => {
this.logService.trace('App#open-file: ', path);
app.on('open-file', (event, path) => {
this.logService.trace('app#open-file: ', path);
event.preventDefault();
// Keep in array because more might come!
......@@ -386,7 +438,9 @@ export class CodeApplication extends Disposable {
}
// Setup Auth Handler
this._register(new ProxyAuthHandler());
if (this.configurationService.getValue('window.enableExperimentalProxyLoginDialog') !== true) {
this._register(new ProxyAuthHandler());
}
// Open Windows
const windows = appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, electronIpcServer, sharedProcessClient));
......
......@@ -116,6 +116,7 @@ export interface IWindowSettings {
enableMenuBarMnemonics: boolean;
closeWhenEmpty: boolean;
clickThroughInactive: boolean;
enableExperimentalProxyLoginDialog: boolean;
}
export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService, isExtensionDevelopment = environment.isExtensionDevelopment): 'native' | 'custom' {
......
......@@ -12,7 +12,7 @@ import { IProcessEnvironment } from 'vs/base/common/platform';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { URI } from 'vs/base/common/uri';
import { Rectangle, BrowserWindow } from 'electron';
import { Rectangle, BrowserWindow, WebContents } from 'electron';
import { IDisposable } from 'vs/base/common/lifecycle';
export interface IWindowState {
......@@ -112,6 +112,7 @@ export interface IWindowsMainService {
getLastActiveWindow(): ICodeWindow | undefined;
getWindowById(windowId: number): ICodeWindow | undefined;
getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined;
getWindows(): ICodeWindow[];
getWindowCount(): number;
}
......
......@@ -14,7 +14,7 @@ import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/e
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { IStateService } from 'vs/platform/state/node/state';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { screen, BrowserWindow, MessageBoxOptions, Display, app } from 'electron';
import { screen, BrowserWindow, MessageBoxOptions, Display, app, WebContents } from 'electron';
import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -1664,6 +1664,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
return arrays.firstOrDefault(res);
}
getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined {
const win = BrowserWindow.fromWebContents(webContents);
if (win) {
return this.getWindowById(win.id);
}
return undefined;
}
getWindows(): ICodeWindow[] {
return WindowsMainService.WINDOWS;
}
......
......@@ -36,6 +36,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
private updateMode: string | undefined;
private debugConsoleWordWrap: boolean | undefined;
private accessibilitySupport: 'on' | 'off' | 'auto' | undefined;
private enableExperimentalProxyLoginDialog: boolean | undefined;
constructor(
@IHostService private readonly hostService: IHostService,
......@@ -97,6 +98,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
changed = true;
}
}
// Experimental proxy login dialog
if (typeof config.window?.enableExperimentalProxyLoginDialog === 'boolean' && config.window.enableExperimentalProxyLoginDialog !== this.enableExperimentalProxyLoginDialog) {
this.enableExperimentalProxyLoginDialog = config.window.enableExperimentalProxyLoginDialog;
changed = true;
}
}
// Notify only when changed and we are the focused window (avoids notification spam across windows)
......
......@@ -292,6 +292,12 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('window.clickThroughInactive', "If enabled, clicking on an inactive window will both activate the window and trigger the element under the mouse if it is clickable. If disabled, clicking anywhere on an inactive window will activate it only and a second click is required on the element."),
'included': isMacintosh
},
'window.enableExperimentalProxyLoginDialog': {
'type': 'boolean',
'default': false,
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('window.enableExperimentalProxyLoginDialog', "Enables a new login dialog for proxy authentication. Requires a restart to take effect."),
}
}
});
......
......@@ -63,6 +63,8 @@ import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IAddressProvider, IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { AuthInfo } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
export class NativeWindow extends Disposable {
......@@ -107,7 +109,8 @@ export class NativeWindow extends Disposable {
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
@IProductService private readonly productService: IProductService,
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IDialogService private readonly dialogService: IDialogService
) {
super();
......@@ -199,7 +202,28 @@ export class NativeWindow extends Disposable {
setFullscreen(false);
});
// accessibility support changed event
// Proxy Login Dialog
ipcRenderer.on('vscode:openProxyAuthDialog', async (event: unknown, payload: { authInfo: AuthInfo, replyChannel: string }) => {
const result = await this.dialogService.input('question', nls.localize('proxyAuthRequired', "Proxy Authentication Required"),
[
nls.localize({ key: 'loginButton', comment: ['&& denotes a mnemonic'] }, "&&Log In"),
nls.localize({ key: 'cancelButton', comment: ['&& denotes a mnemonic'] }, "&&Cancel")
],
[
{ placeholder: nls.localize('username', "Username") },
{ placeholder: nls.localize('password', "Password"), type: 'password' }
],
{
cancelId: 1,
detail: nls.localize('proxyDetail', "The proxy {0} requires a userame and password.", `${payload.authInfo.host}:${payload.authInfo.port}`)
});
if (result.choice !== 1 && result.values) {
ipcRenderer.send(payload.replyChannel, { username: result.values[0], password: result.values[1] });
}
});
// Accessibility support changed event
ipcRenderer.on('vscode:accessibilitySupportChanged', (event: unknown, accessibilitySupportEnabled: boolean) => {
this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled);
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册