diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index a74246fa4d636..587e8ab262a6b 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -296,6 +296,7 @@ function packageTask(platform, arch, opts) { .pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node', 'src/*.js'])) .pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) .pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node'])) + .pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['**/*.node'])) .pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) .pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['**/*.node'])) .pipe(util.cleanNodeModule('jschardet', ['dist/**'])) diff --git a/package.json b/package.json index 8535979bdf1b8..942a65274d43f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "jschardet": "1.6.0", "keytar": "^4.0.5", "minimist": "1.2.0", + "native-is-elevated": "^0.2.1", "native-keymap": "1.2.5", "native-watchdog": "0.3.0", "node-pty": "0.7.4", diff --git a/src/typings/native-is-elevated.d.ts b/src/typings/native-is-elevated.d.ts new file mode 100644 index 0000000000000..1b79764d6a056 --- /dev/null +++ b/src/typings/native-is-elevated.d.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'native-is-elevated' { + function isElevated(): boolean; + + export = isElevated; +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index bd5633c031bb4..885016057c45a 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -19,7 +19,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IAction, Action } from 'vs/base/common/actions'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -31,15 +30,21 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { Verbosity } from 'vs/platform/editor/common/editor'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; import URI from 'vs/base/common/uri'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +export interface ITitleProperties { + isPure?: boolean; + isAdmin?: boolean; +} + export class TitlebarPart extends Part implements ITitleService { public _serviceBrand: any; private static readonly NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]"); + private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[sudo]"); private static readonly NLS_EXTENSION_HOST = nls.localize('devExtensionWindowTitlePrefix', "[Extension Development Host]"); private static readonly TITLE_DIRTY = '\u25cf '; private static readonly TITLE_SEPARATOR = isMacintosh ? ' — ' : ' - '; // macOS uses special - separator @@ -52,7 +57,7 @@ export class TitlebarPart extends Part implements ITitleService { private isInactive: boolean; - private isPure: boolean; + private properties: ITitleProperties; private activeEditorListeners: IDisposable[]; constructor( @@ -63,7 +68,6 @@ export class TitlebarPart extends Part implements ITitleService { @IWindowsService private windowsService: IWindowsService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IEditorGroupService private editorGroupService: IEditorGroupService, - @IIntegrityService private integrityService: IIntegrityService, @IEnvironmentService private environmentService: IEnvironmentService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IThemeService themeService: IThemeService, @@ -71,7 +75,7 @@ export class TitlebarPart extends Part implements ITitleService { ) { super(id, { hasTitle: false }, themeService); - this.isPure = true; + this.properties = { isPure: true, isAdmin: false }; this.activeEditorListeners = []; this.init(); @@ -83,14 +87,6 @@ export class TitlebarPart extends Part implements ITitleService { // Initial window title when loading is done this.lifecycleService.when(LifecyclePhase.Running).then(() => this.setTitle(this.getWindowTitle())); - - // Integrity for window title - this.integrityService.isPure().then(r => { - if (!r.isPure) { - this.isPure = false; - this.setTitle(this.getWindowTitle()); - } - }); } private registerListeners(): void { @@ -149,7 +145,11 @@ export class TitlebarPart extends Part implements ITitleService { title = this.environmentService.appNameLong; } - if (!this.isPure) { + if (this.properties.isAdmin) { + title = `${title} ${TitlebarPart.NLS_USER_IS_ADMIN}`; + } + + if (!this.properties.isPure) { title = `${title} ${TitlebarPart.NLS_UNSUPPORTED}`; } @@ -161,6 +161,18 @@ export class TitlebarPart extends Part implements ITitleService { return title; } + public updateProperties(properties: ITitleProperties): void { + const isAdmin = typeof properties.isAdmin === 'boolean' ? properties.isAdmin : this.properties.isAdmin; + const isPure = typeof properties.isPure === 'boolean' ? properties.isPure : this.properties.isPure; + + if (isAdmin !== this.properties.isAdmin || isPure !== this.properties.isPure) { + this.properties.isAdmin = isAdmin; + this.properties.isPure = isPure; + + this.setTitle(this.getWindowTitle()); + } + } + /** * Possible template values: * diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index cf13abcc7ccb7..cf33913fe6d31 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -41,8 +41,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IntegrityServiceImpl } from 'vs/platform/integrity/node/integrityServiceImpl'; -import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; @@ -195,7 +193,7 @@ export class WorkbenchShell { // Root Warning if ((platform.isLinux || platform.isMacintosh) && process.getuid() === 0) { - this.messageService.show(Severity.Warning, nls.localize('runningAsRoot', "It is recommended not to run Code as 'root'.")); + this.messageService.show(Severity.Warning, nls.localize('runningAsRoot', "It is not recommended to run Code as 'root'.")); } // Set lifecycle phase to `Runnning` so that other contributions can now do something @@ -393,8 +391,6 @@ export class WorkbenchShell { serviceCollection.set(ICodeEditorService, new SyncDescriptor(CodeEditorServiceImpl)); - serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); - return [instantiationService, serviceCollection]; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index be4fdf873847d..9046f525ab906 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -21,7 +21,7 @@ import errors = require('vs/base/common/errors'); import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isWindows, isLinux, isMacintosh, isRootUser } from 'vs/base/common/platform'; import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput, IEditor, IResourceInput } from 'vs/platform/editor/common/editor'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; @@ -40,6 +40,8 @@ import { getServices } from 'vs/platform/instantiation/common/extensions'; import { Position, Parts, IPartService, ILayoutOptions, Dimension } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IntegrityServiceImpl } from 'vs/platform/integrity/node/integrityServiceImpl'; +import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService'; import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -512,6 +514,10 @@ export class Workbench implements IPartService { // Services we contribute serviceCollection.set(IPartService, this); + // Integrity + const integrityService = this.instantiationService.createInstance(IntegrityServiceImpl); + serviceCollection.set(IIntegrityService, integrityService); + // Clipboard serviceCollection.set(IClipboardService, new ClipboardService()); @@ -574,6 +580,17 @@ export class Workbench implements IPartService { this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART); this.toUnbind.push({ dispose: () => this.titlebarPart.shutdown() }); serviceCollection.set(ITitleService, this.titlebarPart); + integrityService.isPure().then(res => this.titlebarPart.updateProperties({ isPure: res.isPure })); + this.lifecycleService.when(LifecyclePhase.Running).then(() => { + let isAdminPromise: Promise; + if (isWindows) { + isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); + } else { + isAdminPromise = Promise.resolve(isRootUser); + } + + return isAdminPromise.then(isAdmin => this.titlebarPart.updateProperties({ isAdmin })); + }); // History serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); diff --git a/yarn.lock b/yarn.lock index f95110eef0888..93ff0a1f9ae57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3742,6 +3742,10 @@ nan@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" +native-is-elevated@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.2.1.tgz#70a2123a8575b9f624a3ef465d98cb74ae017385" + native-keymap@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-1.2.5.tgz#1035a9417b9a9340cf8097763a43c76d588165a5"