mirror of
https://github.com/Eugeny/tabby.git
synced 2024-12-25 03:22:58 +03:00
allow config encryption
This commit is contained in:
parent
7f18396926
commit
cbaf40bb82
@ -80,4 +80,5 @@ export abstract class PlatformService {
|
|||||||
abstract listFonts (): Promise<string[]>
|
abstract listFonts (): Promise<string[]>
|
||||||
abstract popupContextMenu (menu: MenuItemOptions[], event?: MouseEvent): void
|
abstract popupContextMenu (menu: MenuItemOptions[], event?: MouseEvent): void
|
||||||
abstract showMessageBox (options: MessageBoxOptions): Promise<MessageBoxResult>
|
abstract showMessageBox (options: MessageBoxOptions): Promise<MessageBoxResult>
|
||||||
|
abstract quit (): void
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class UnlockVaultModalComponent {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit (): void {
|
ngOnInit (): void {
|
||||||
this.rememberFor = window.localStorage.vaultRememberPassphraseFor ?? 0
|
this.rememberFor = parseInt(window.localStorage.vaultRememberPassphraseFor ?? 0)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.input.nativeElement.focus()
|
this.input.nativeElement.focus()
|
||||||
})
|
})
|
||||||
|
@ -23,3 +23,4 @@ electronFlags:
|
|||||||
enableAutomaticUpdates: true
|
enableAutomaticUpdates: true
|
||||||
version: 1
|
version: 1
|
||||||
vault: null
|
vault: null
|
||||||
|
encrypted: false
|
||||||
|
@ -4,6 +4,7 @@ import { Injectable, Inject } from '@angular/core'
|
|||||||
import { ConfigProvider } from '../api/configProvider'
|
import { ConfigProvider } from '../api/configProvider'
|
||||||
import { PlatformService } from '../api/platform'
|
import { PlatformService } from '../api/platform'
|
||||||
import { HostAppService } from './hostApp.service'
|
import { HostAppService } from './hostApp.service'
|
||||||
|
import { Vault, VaultService } from './vault.service'
|
||||||
const deepmerge = require('deepmerge')
|
const deepmerge = require('deepmerge')
|
||||||
|
|
||||||
const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
|
const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
|
||||||
@ -105,10 +106,15 @@ export class ConfigService {
|
|||||||
private constructor (
|
private constructor (
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private platform: PlatformService,
|
private platform: PlatformService,
|
||||||
|
private vault: VaultService,
|
||||||
@Inject(ConfigProvider) private configProviders: ConfigProvider[],
|
@Inject(ConfigProvider) private configProviders: ConfigProvider[],
|
||||||
) {
|
) {
|
||||||
this.defaults = this.mergeDefaults()
|
this.defaults = this.mergeDefaults()
|
||||||
this.init()
|
setTimeout(() => this.init())
|
||||||
|
vault.contentChanged$.subscribe(() => {
|
||||||
|
this.store.vault = vault.store
|
||||||
|
this.save()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeDefaults (): unknown {
|
mergeDefaults (): unknown {
|
||||||
@ -152,13 +158,16 @@ export class ConfigService {
|
|||||||
} else {
|
} else {
|
||||||
this._store = { version: LATEST_VERSION }
|
this._store = { version: LATEST_VERSION }
|
||||||
}
|
}
|
||||||
|
this._store = await this.maybeDecryptConfig(this._store)
|
||||||
this.migrate(this._store)
|
this.migrate(this._store)
|
||||||
this.store = new ConfigProxy(this._store, this.defaults)
|
this.store = new ConfigProxy(this._store, this.defaults)
|
||||||
|
this.vault.setStore(this.store.vault)
|
||||||
}
|
}
|
||||||
|
|
||||||
async save (): Promise<void> {
|
async save (): Promise<void> {
|
||||||
// Scrub undefined values
|
// Scrub undefined values
|
||||||
const cleanStore = JSON.parse(JSON.stringify(this._store))
|
let cleanStore = JSON.parse(JSON.stringify(this._store))
|
||||||
|
cleanStore = await this.maybeEncryptConfig(cleanStore)
|
||||||
await this.platform.saveConfig(yaml.dump(cleanStore))
|
await this.platform.saveConfig(yaml.dump(cleanStore))
|
||||||
this.emitChange()
|
this.emitChange()
|
||||||
this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store)))
|
this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store)))
|
||||||
@ -207,7 +216,7 @@ export class ConfigService {
|
|||||||
return services.filter(service => {
|
return services.filter(service => {
|
||||||
for (const pluginName in this.servicesCache) {
|
for (const pluginName in this.servicesCache) {
|
||||||
if (this.servicesCache[pluginName].includes(service.constructor)) {
|
if (this.servicesCache[pluginName].includes(service.constructor)) {
|
||||||
return !this.store.pluginBlacklist.includes(pluginName)
|
return !this.store?.pluginBlacklist?.includes(pluginName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -227,6 +236,7 @@ export class ConfigService {
|
|||||||
|
|
||||||
private emitChange (): void {
|
private emitChange (): void {
|
||||||
this.changed.next()
|
this.changed.next()
|
||||||
|
this.vault.setStore(this.store.vault)
|
||||||
}
|
}
|
||||||
|
|
||||||
private migrate (config) {
|
private migrate (config) {
|
||||||
@ -241,4 +251,67 @@ export class ConfigService {
|
|||||||
config.version = 1
|
config.version = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async maybeDecryptConfig (store) {
|
||||||
|
if (!store.encrypted) {
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/init-declarations
|
||||||
|
let decryptedVault: Vault
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const passphrase = await this.vault.getPassphrase()
|
||||||
|
decryptedVault = await this.vault.decrypt(store.vault, passphrase)
|
||||||
|
break
|
||||||
|
} catch (e) {
|
||||||
|
let result = await this.platform.showMessageBox({
|
||||||
|
type: 'error',
|
||||||
|
message: 'Could not decrypt config',
|
||||||
|
detail: e.toString(),
|
||||||
|
buttons: ['Try again', 'Erase config', 'Quit'],
|
||||||
|
defaultId: 0,
|
||||||
|
})
|
||||||
|
if (result.response === 2) {
|
||||||
|
this.platform.quit()
|
||||||
|
}
|
||||||
|
if (result.response === 1) {
|
||||||
|
result = await this.platform.showMessageBox({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Are you sure?',
|
||||||
|
detail: e.toString(),
|
||||||
|
buttons: ['Erase config', 'Quit'],
|
||||||
|
defaultId: 1,
|
||||||
|
})
|
||||||
|
if (result.response === 1) {
|
||||||
|
this.platform.quit()
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete decryptedVault.config.vault
|
||||||
|
delete decryptedVault.config.encrypted
|
||||||
|
return {
|
||||||
|
...decryptedVault.config,
|
||||||
|
vault: store.vault,
|
||||||
|
encrypted: store.encrypted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async maybeEncryptConfig (store) {
|
||||||
|
if (!store.encrypted) {
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
const vault = await this.vault.load()
|
||||||
|
if (!vault) {
|
||||||
|
throw new Error('Vault not configured')
|
||||||
|
}
|
||||||
|
vault.config = { ...store }
|
||||||
|
delete vault.config.vault
|
||||||
|
delete vault.config.encrypted
|
||||||
|
return {
|
||||||
|
vault: await this.vault.encrypt(vault),
|
||||||
|
encrypted: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ export class ThemesService {
|
|||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
@Inject(Theme) private themes: Theme[],
|
@Inject(Theme) private themes: Theme[],
|
||||||
) {
|
) {
|
||||||
|
this.applyTheme(this.findTheme('Standard')!)
|
||||||
config.ready$.toPromise().then(() => {
|
config.ready$.toPromise().then(() => {
|
||||||
this.applyCurrentTheme()
|
this.applyCurrentTheme()
|
||||||
config.changed$.subscribe(() => {
|
config.changed$.subscribe(() => {
|
||||||
@ -38,7 +39,7 @@ export class ThemesService {
|
|||||||
document.querySelector('head')!.appendChild(this.styleElement)
|
document.querySelector('head')!.appendChild(this.styleElement)
|
||||||
}
|
}
|
||||||
this.styleElement.textContent = theme.css
|
this.styleElement.textContent = theme.css
|
||||||
document.querySelector('style#custom-css')!.innerHTML = this.config.store.appearance.css
|
document.querySelector('style#custom-css')!.innerHTML = this.config.store?.appearance?.css
|
||||||
this.themeChanged.next(theme)
|
this.themeChanged.next(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@ import * as crypto from 'crypto'
|
|||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
import { Injectable, NgZone } from '@angular/core'
|
import { Injectable, NgZone } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { AsyncSubject, Observable } from 'rxjs'
|
import { AsyncSubject, Subject, Observable } from 'rxjs'
|
||||||
import { ConfigService } from '../services/config.service'
|
|
||||||
import { UnlockVaultModalComponent } from '../components/unlockVaultModal.component'
|
import { UnlockVaultModalComponent } from '../components/unlockVaultModal.component'
|
||||||
import { NotificationsService } from '../services/notifications.service'
|
import { NotificationsService } from '../services/notifications.service'
|
||||||
|
|
||||||
@ -28,11 +27,13 @@ export interface VaultSecret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Vault {
|
export interface Vault {
|
||||||
|
config: any
|
||||||
secrets: VaultSecret[]
|
secrets: VaultSecret[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function migrateVaultContent (content: any): Vault {
|
function migrateVaultContent (content: any): Vault {
|
||||||
return {
|
return {
|
||||||
|
config: content.config,
|
||||||
secrets: content.secrets ?? [],
|
secrets: content.secrets ?? [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,34 +87,27 @@ export class VaultService {
|
|||||||
/** Fires once when the config is loaded */
|
/** Fires once when the config is loaded */
|
||||||
get ready$ (): Observable<boolean> { return this.ready }
|
get ready$ (): Observable<boolean> { return this.ready }
|
||||||
|
|
||||||
enabled = false
|
get contentChanged$ (): Observable<void> { return this.contentChanged }
|
||||||
|
|
||||||
|
store: StoredVault|null = null
|
||||||
private ready = new AsyncSubject<boolean>()
|
private ready = new AsyncSubject<boolean>()
|
||||||
|
private contentChanged = new Subject<void>()
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
private constructor (
|
private constructor (
|
||||||
private config: ConfigService,
|
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private notifications: NotificationsService,
|
private notifications: NotificationsService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
) {
|
) { }
|
||||||
config.ready$.toPromise().then(() => {
|
|
||||||
this.onConfigChange()
|
|
||||||
this.ready.next(true)
|
|
||||||
this.ready.complete()
|
|
||||||
config.changed$.subscribe(() => {
|
|
||||||
this.onConfigChange()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async setEnabled (enabled: boolean, passphrase?: string): Promise<void> {
|
async setEnabled (enabled: boolean, passphrase?: string): Promise<void> {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (!this.config.store.vault) {
|
if (!this.store) {
|
||||||
await this.save(migrateVaultContent({}), passphrase)
|
await this.save(migrateVaultContent({}), passphrase)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.config.store.vault = null
|
this.store = null
|
||||||
await this.config.save()
|
this.contentChanged.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,15 +115,12 @@ export class VaultService {
|
|||||||
return !!_rememberedPassphrase
|
return !!_rememberedPassphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
async load (passphrase?: string): Promise<Vault|null> {
|
async decrypt (storage: StoredVault, passphrase?: string): Promise<Vault> {
|
||||||
if (!this.config.store.vault) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (!passphrase) {
|
if (!passphrase) {
|
||||||
passphrase = await this.getPassphrase()
|
passphrase = await this.getPassphrase()
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return await this.wrapPromise(decryptVault(this.config.store.vault, passphrase))
|
return await this.wrapPromise(decryptVault(storage, passphrase))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_rememberedPassphrase = null
|
_rememberedPassphrase = null
|
||||||
if (e.toString().includes('BAD_DECRYPT')) {
|
if (e.toString().includes('BAD_DECRYPT')) {
|
||||||
@ -139,15 +130,27 @@ export class VaultService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async save (vault: Vault, passphrase?: string): Promise<void> {
|
async load (passphrase?: string): Promise<Vault|null> {
|
||||||
|
if (!this.store) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return this.decrypt(this.store, passphrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
async encrypt (vault: Vault, passphrase?: string): Promise<StoredVault|null> {
|
||||||
if (!passphrase) {
|
if (!passphrase) {
|
||||||
passphrase = await this.getPassphrase()
|
passphrase = await this.getPassphrase()
|
||||||
}
|
}
|
||||||
if (_rememberedPassphrase) {
|
if (_rememberedPassphrase) {
|
||||||
_rememberedPassphrase = passphrase
|
_rememberedPassphrase = passphrase
|
||||||
}
|
}
|
||||||
this.config.store.vault = await this.wrapPromise(encryptVault(vault, passphrase))
|
return this.wrapPromise(encryptVault(vault, passphrase))
|
||||||
await this.config.save()
|
}
|
||||||
|
|
||||||
|
async save (vault: Vault, passphrase?: string): Promise<void> {
|
||||||
|
await this.ready$.toPromise()
|
||||||
|
this.store = await this.encrypt(vault, passphrase)
|
||||||
|
this.contentChanged.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPassphrase (): Promise<string> {
|
async getPassphrase (): Promise<string> {
|
||||||
@ -156,7 +159,8 @@ export class VaultService {
|
|||||||
const { passphrase, rememberFor } = await modal.result
|
const { passphrase, rememberFor } = await modal.result
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
_rememberedPassphrase = null
|
_rememberedPassphrase = null
|
||||||
}, rememberFor * 60000)
|
// avoid multiple consequent prompts
|
||||||
|
}, Math.min(1000, rememberFor * 60000))
|
||||||
_rememberedPassphrase = passphrase
|
_rememberedPassphrase = passphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +168,7 @@ export class VaultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getSecret (type: string, key: Record<string, any>): Promise<VaultSecret|null> {
|
async getSecret (type: string, key: Record<string, any>): Promise<VaultSecret|null> {
|
||||||
|
await this.ready$.toPromise()
|
||||||
const vault = await this.load()
|
const vault = await this.load()
|
||||||
if (!vault) {
|
if (!vault) {
|
||||||
return null
|
return null
|
||||||
@ -172,6 +177,7 @@ export class VaultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addSecret (secret: VaultSecret): Promise<void> {
|
async addSecret (secret: VaultSecret): Promise<void> {
|
||||||
|
await this.ready$.toPromise()
|
||||||
const vault = await this.load()
|
const vault = await this.load()
|
||||||
if (!vault) {
|
if (!vault) {
|
||||||
return
|
return
|
||||||
@ -182,6 +188,7 @@ export class VaultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeSecret (type: string, key: Record<string, any>): Promise<void> {
|
async removeSecret (type: string, key: Record<string, any>): Promise<void> {
|
||||||
|
await this.ready$.toPromise()
|
||||||
const vault = await this.load()
|
const vault = await this.load()
|
||||||
if (!vault) {
|
if (!vault) {
|
||||||
return
|
return
|
||||||
@ -194,8 +201,14 @@ export class VaultService {
|
|||||||
return Object.keys(key).every(k => secret.key[k] === key[k])
|
return Object.keys(key).every(k => secret.key[k] === key[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
private onConfigChange () {
|
setStore (store: StoredVault): void {
|
||||||
this.enabled = !!this.config.store.vault
|
this.store = store
|
||||||
|
this.ready.next(true)
|
||||||
|
this.ready.complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnabled (): boolean {
|
||||||
|
return !!this.store
|
||||||
}
|
}
|
||||||
|
|
||||||
private wrapPromise <T> (promise: Promise<T>): Promise<T> {
|
private wrapPromise <T> (promise: Promise<T>): Promise<T> {
|
||||||
|
@ -153,4 +153,8 @@ export class ElectronPlatformService extends PlatformService {
|
|||||||
async showMessageBox (options: MessageBoxOptions): Promise<MessageBoxResult> {
|
async showMessageBox (options: MessageBoxOptions): Promise<MessageBoxResult> {
|
||||||
return this.electron.dialog.showMessageBox(this.hostApp.getWindow(), options)
|
return this.electron.dialog.showMessageBox(this.hostApp.getWindow(), options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quit (): void {
|
||||||
|
this.electron.app.exit(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
.text-center(*ngIf='!vault.enabled')
|
.text-center(*ngIf='!vault.isEnabled()')
|
||||||
i.fas.fa-key.fa-3x.m-3
|
i.fas.fa-key.fa-3x.m-3
|
||||||
h3.m-3 Vault is not configured
|
h3.m-3 Vault is not configured
|
||||||
.m-3 Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
|
.m-3 Vault is an always-encrypted container for secrets such as SSH passwords and private key passphrases.
|
||||||
button.btn.btn-primary.m-2((click)='enableVault()') Set master passphrase
|
button.btn.btn-primary.m-2((click)='enableVault()') Set master passphrase
|
||||||
|
|
||||||
div(*ngIf='vault.enabled')
|
|
||||||
|
div(*ngIf='vault.isEnabled()')
|
||||||
.d-flex.align-items-center.mb-3
|
.d-flex.align-items-center.mb-3
|
||||||
h3.m-0 Vault
|
h3.m-0 Vault
|
||||||
.d-flex.ml-auto(ngbDropdown, *ngIf='vault.enabled')
|
.d-flex.ml-auto(ngbDropdown, *ngIf='vault.isEnabled()')
|
||||||
button.btn.btn-secondary(ngbDropdownToggle) Options
|
button.btn.btn-secondary(ngbDropdownToggle) Options
|
||||||
div(ngbDropdownMenu)
|
div(ngbDropdownMenu)
|
||||||
a(ngbDropdownItem, (click)='changePassphrase()')
|
a(ngbDropdownItem, (click)='changePassphrase()')
|
||||||
@ -29,6 +30,16 @@ div(*ngIf='vault.enabled')
|
|||||||
button.btn.btn-link((click)='removeSecret(secret)')
|
button.btn.btn-link((click)='removeSecret(secret)')
|
||||||
i.fas.fa-trash
|
i.fas.fa-trash
|
||||||
|
|
||||||
|
h3.mt-5 Options
|
||||||
|
.form-line
|
||||||
|
.header
|
||||||
|
.title Encrypt config file
|
||||||
|
.description Puts all of Terminus configuration into the vault
|
||||||
|
toggle(
|
||||||
|
[ngModel]='config.store.encrypted',
|
||||||
|
(click)='toggleConfigEncrypted()',
|
||||||
|
)
|
||||||
|
|
||||||
.text-center(*ngIf='!vaultContents')
|
.text-center(*ngIf='!vaultContents')
|
||||||
i.fas.fa-key.fa-3x
|
i.fas.fa-key.fa-3x
|
||||||
h3.m-3 Vault is locked
|
h3.m-3 Vault is locked
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService } from 'terminus-core'
|
import { BaseComponent, VaultService, VaultSecret, Vault, PlatformService, ConfigService } from 'terminus-core'
|
||||||
import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component'
|
import { SetVaultPassphraseModalComponent } from './setVaultPassphraseModal.component'
|
||||||
|
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
|||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public vault: VaultService,
|
public vault: VaultService,
|
||||||
|
public config: ConfigService,
|
||||||
private platform: PlatformService,
|
private platform: PlatformService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
) {
|
) {
|
||||||
@ -60,6 +61,16 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
|||||||
this.vault.save(this.vaultContents, newPassphrase)
|
this.vault.save(this.vaultContents, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggleConfigEncrypted () {
|
||||||
|
this.config.store.encrypted = !this.config.store.encrypted
|
||||||
|
try {
|
||||||
|
await this.config.save()
|
||||||
|
} catch (e) {
|
||||||
|
this.config.store.encrypted = !this.config.store.encrypted
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getSecretLabel (secret: VaultSecret) {
|
getSecretLabel (secret: VaultSecret) {
|
||||||
if (secret.type === 'ssh:password') {
|
if (secret.type === 'ssh:password') {
|
||||||
return `SSH password for ${secret.key.user}@${secret.key.host}:${secret.key.port}`
|
return `SSH password for ${secret.key.user}@${secret.key.host}:${secret.key.port}`
|
||||||
|
@ -11,7 +11,7 @@ export class PasswordStorageService {
|
|||||||
constructor (private vault: VaultService) { }
|
constructor (private vault: VaultService) { }
|
||||||
|
|
||||||
async savePassword (connection: SSHConnection, password: string): Promise<void> {
|
async savePassword (connection: SSHConnection, password: string): Promise<void> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForConnection(connection)
|
const key = this.getVaultKeyForConnection(connection)
|
||||||
this.vault.addSecret({ type: VAULT_SECRET_TYPE_PASSWORD, key, value: password })
|
this.vault.addSecret({ type: VAULT_SECRET_TYPE_PASSWORD, key, value: password })
|
||||||
} else {
|
} else {
|
||||||
@ -21,7 +21,7 @@ export class PasswordStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deletePassword (connection: SSHConnection): Promise<void> {
|
async deletePassword (connection: SSHConnection): Promise<void> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForConnection(connection)
|
const key = this.getVaultKeyForConnection(connection)
|
||||||
this.vault.removeSecret(VAULT_SECRET_TYPE_PASSWORD, key)
|
this.vault.removeSecret(VAULT_SECRET_TYPE_PASSWORD, key)
|
||||||
} else {
|
} else {
|
||||||
@ -31,7 +31,7 @@ export class PasswordStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadPassword (connection: SSHConnection): Promise<string|null> {
|
async loadPassword (connection: SSHConnection): Promise<string|null> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForConnection(connection)
|
const key = this.getVaultKeyForConnection(connection)
|
||||||
return (await this.vault.getSecret(VAULT_SECRET_TYPE_PASSWORD, key))?.value ?? null
|
return (await this.vault.getSecret(VAULT_SECRET_TYPE_PASSWORD, key))?.value ?? null
|
||||||
} else {
|
} else {
|
||||||
@ -41,7 +41,7 @@ export class PasswordStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async savePrivateKeyPassword (id: string, password: string): Promise<void> {
|
async savePrivateKeyPassword (id: string, password: string): Promise<void> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForPrivateKey(id)
|
const key = this.getVaultKeyForPrivateKey(id)
|
||||||
this.vault.addSecret({ type: VAULT_SECRET_TYPE_PASSPHRASE, key, value: password })
|
this.vault.addSecret({ type: VAULT_SECRET_TYPE_PASSPHRASE, key, value: password })
|
||||||
} else {
|
} else {
|
||||||
@ -51,7 +51,7 @@ export class PasswordStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deletePrivateKeyPassword (id: string): Promise<void> {
|
async deletePrivateKeyPassword (id: string): Promise<void> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForPrivateKey(id)
|
const key = this.getVaultKeyForPrivateKey(id)
|
||||||
this.vault.removeSecret(VAULT_SECRET_TYPE_PASSPHRASE, key)
|
this.vault.removeSecret(VAULT_SECRET_TYPE_PASSPHRASE, key)
|
||||||
} else {
|
} else {
|
||||||
@ -61,7 +61,7 @@ export class PasswordStorageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadPrivateKeyPassword (id: string): Promise<string|null> {
|
async loadPrivateKeyPassword (id: string): Promise<string|null> {
|
||||||
if (this.vault.enabled) {
|
if (this.vault.isEnabled()) {
|
||||||
const key = this.getVaultKeyForPrivateKey(id)
|
const key = this.getVaultKeyForPrivateKey(id)
|
||||||
return (await this.vault.getSecret(VAULT_SECRET_TYPE_PASSPHRASE, key))?.value ?? null
|
return (await this.vault.getSecret(VAULT_SECRET_TYPE_PASSPHRASE, key))?.value ?? null
|
||||||
} else {
|
} else {
|
||||||
|
@ -95,4 +95,8 @@ export class WebPlatformService extends PlatformService {
|
|||||||
return { response: 0 }
|
return { response: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quit (): void {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user