mirror of
https://github.com/Eugeny/tabby.git
synced 2025-10-04 05:54:57 +00:00
Experimental UAC start-as-admin wrapper (fixes #511)
This commit is contained in:
@@ -21,6 +21,7 @@ export interface SessionOptions {
|
||||
width?: number
|
||||
height?: number
|
||||
pauseAfterExit?: boolean
|
||||
runAsAdministrator?: boolean
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
|
@@ -32,6 +32,13 @@
|
||||
i.fas.fa-plus.mr-2
|
||||
| Add
|
||||
|
||||
.form-line(*ngIf='uac.isAvailable')
|
||||
.header
|
||||
.title Run as administrator
|
||||
toggle(
|
||||
[(ngModel)]='profile.sessionOptions.runAsAdministrator',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Working directory
|
||||
input.form-control(
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { UACService } from '../services/uac.service'
|
||||
import { Profile } from '../api'
|
||||
|
||||
@Component({
|
||||
@@ -9,6 +10,7 @@ export class EditProfileModalComponent {
|
||||
profile: Profile
|
||||
|
||||
constructor (
|
||||
public uac: UACService,
|
||||
private modalInstance: NgbActiveModal,
|
||||
) {
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ h3.mb-3 Shell
|
||||
) {{shell.name}}
|
||||
|
||||
|
||||
.form-line(*ngIf='hostApp.platform === Platform.Windows')
|
||||
.form-line(*ngIf='isConPTYAvailable')
|
||||
.header
|
||||
.title Use ConPTY
|
||||
.description Enables the experimental Windows ConPTY API
|
||||
|
@@ -5,6 +5,7 @@ import { ConfigService, ElectronService, HostAppService, Platform } from 'termin
|
||||
import { EditProfileModalComponent } from './editProfileModal.component'
|
||||
import { IShell, Profile } from '../api'
|
||||
import { TerminalService } from '../services/terminal.service'
|
||||
import { UACService } from '../services/uac.service'
|
||||
|
||||
@Component({
|
||||
template: require('./shellSettingsTab.component.pug'),
|
||||
@@ -13,9 +14,11 @@ export class ShellSettingsTabComponent {
|
||||
shells: IShell[] = []
|
||||
profiles: Profile[] = []
|
||||
Platform = Platform
|
||||
isConPTYAvailable: boolean
|
||||
private configSubscription: Subscription
|
||||
|
||||
constructor (
|
||||
uac: UACService,
|
||||
public config: ConfigService,
|
||||
public hostApp: HostAppService,
|
||||
private electron: ElectronService,
|
||||
@@ -27,6 +30,7 @@ export class ShellSettingsTabComponent {
|
||||
this.reload()
|
||||
})
|
||||
this.reload()
|
||||
this.isConPTYAvailable = uac.isAvailable
|
||||
}
|
||||
|
||||
async ngOnInit () {
|
||||
|
@@ -7,6 +7,7 @@ import { AppService, ConfigService, BaseTabComponent, BaseTabProcess, ElectronSe
|
||||
import { Session, SessionsService } from '../services/sessions.service'
|
||||
import { TerminalService } from '../services/terminal.service'
|
||||
import { TerminalFrontendService } from '../services/terminalFrontend.service'
|
||||
import { UACService } from '../services/uac.service'
|
||||
|
||||
import { TerminalDecorator, ResizeEvent, SessionOptions } from '../api'
|
||||
import { Frontend } from '../frontends/frontend'
|
||||
@@ -52,6 +53,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
private terminalContainersService: TerminalFrontendService,
|
||||
public config: ConfigService,
|
||||
private toastr: ToastrService,
|
||||
private uac: UACService,
|
||||
@Optional() @Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
||||
) {
|
||||
super()
|
||||
@@ -205,7 +207,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
|
||||
async buildContextMenu (): Promise<Electron.MenuItemConstructorOptions[]> {
|
||||
let shells = await this.terminalService.shells$.toPromise()
|
||||
return [
|
||||
let items: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: 'New terminal',
|
||||
click: () => this.zone.run(() => {
|
||||
@@ -221,6 +223,23 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
}),
|
||||
})),
|
||||
},
|
||||
]
|
||||
|
||||
if (this.uac.isAvailable) {
|
||||
items.push({
|
||||
label: 'New as admin',
|
||||
submenu: shells.map(shell => ({
|
||||
label: shell.name,
|
||||
click: () => this.zone.run(async () => {
|
||||
let options = this.terminalService.optionsFromShell(shell)
|
||||
options.runAsAdministrator = true
|
||||
this.terminalService.openTabWithOptions(options)
|
||||
}),
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
items = items.concat([
|
||||
{
|
||||
label: 'New with profile',
|
||||
submenu: this.config.store.terminal.profiles.length ? this.config.store.terminal.profiles.map(profile => ({
|
||||
@@ -255,7 +274,9 @@ export class TerminalTabComponent extends BaseTabComponent {
|
||||
})
|
||||
}
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
detachTermContainerHandlers () {
|
||||
|
@@ -3,6 +3,7 @@ import { Injectable, Inject } from '@angular/core'
|
||||
import { AppService, Logger, LogService, ConfigService } from 'terminus-core'
|
||||
import { IShell, ShellProvider, SessionOptions } from '../api'
|
||||
import { TerminalTabComponent } from '../components/terminalTab.component'
|
||||
import { UACService } from './uac.service'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TerminalService {
|
||||
@@ -14,6 +15,7 @@ export class TerminalService {
|
||||
constructor (
|
||||
private app: AppService,
|
||||
private config: ConfigService,
|
||||
private uac: UACService,
|
||||
@Inject(ShellProvider) private shellProviders: ShellProvider[],
|
||||
log: LogService,
|
||||
) {
|
||||
@@ -61,7 +63,7 @@ export class TerminalService {
|
||||
return this.openTabWithOptions(sessionOptions)
|
||||
}
|
||||
|
||||
optionsFromShell (shell: IShell) {
|
||||
optionsFromShell (shell: IShell): SessionOptions {
|
||||
return {
|
||||
command: shell.command,
|
||||
args: shell.args || [],
|
||||
@@ -70,6 +72,9 @@ export class TerminalService {
|
||||
}
|
||||
|
||||
openTabWithOptions (sessionOptions: SessionOptions): TerminalTabComponent {
|
||||
if (sessionOptions.runAsAdministrator && this.uac.isAvailable) {
|
||||
sessionOptions = this.uac.patchSessionOptionsForUAC(sessionOptions)
|
||||
}
|
||||
this.logger.log('Using session options:', sessionOptions)
|
||||
|
||||
return this.app.openNewTab(
|
||||
|
43
terminus-terminal/src/services/uac.service.ts
Normal file
43
terminus-terminal/src/services/uac.service.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ElectronService, HostAppService, Platform } from 'terminus-core'
|
||||
import { SessionOptions } from '../api'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UACService {
|
||||
isAvailable = false
|
||||
|
||||
constructor (
|
||||
hostApp: HostAppService,
|
||||
private electron: ElectronService,
|
||||
) {
|
||||
this.isAvailable = hostApp.platform === Platform.Windows
|
||||
&& parseFloat(os.release()) >= 10
|
||||
&& parseInt(os.release().split('.')[2]) >= 17692
|
||||
}
|
||||
|
||||
patchSessionOptionsForUAC (sessionOptions: SessionOptions): SessionOptions {
|
||||
let helperPath = path.join(
|
||||
path.dirname(this.electron.app.getPath('exe')),
|
||||
'resources',
|
||||
'extras',
|
||||
'UAC.exe',
|
||||
)
|
||||
|
||||
if (process.env.DEV) {
|
||||
helperPath = path.join(
|
||||
path.dirname(this.electron.app.getPath('exe')),
|
||||
'..', '..', '..',
|
||||
'extras',
|
||||
'UAC.exe',
|
||||
)
|
||||
}
|
||||
|
||||
let options = { ...sessionOptions }
|
||||
options.args = [options.command, ...options.args]
|
||||
options.command = helperPath
|
||||
return options
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user