mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-07 19:07:45 +01:00
[PM-14863] Force unlock when keys are cleared / on first unlock and fix account switching behavior (#11994)
* Force unlock when keys are cleared / on first unlock and fix account switching behavior * Make comment a doc comment * Pin russh commit * Cleanup * Make list messaging explicit * Add account switching error handling for ssh agent * Add account switching error handling for ssh agent * Cleanup
This commit is contained in:
parent
36750b374e
commit
050417a92e
2
apps/desktop/desktop_native/Cargo.lock
generated
2
apps/desktop/desktop_native/Cargo.lock
generated
@ -307,7 +307,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "bitwarden-russh"
|
name = "bitwarden-russh"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/bitwarden/bitwarden-russh.git?branch=km/pm-10098/clean-russh-implementation#86ff1bf2f4620a3ae5684adee31abdbee33c6f07"
|
source = "git+https://github.com/bitwarden/bitwarden-russh.git?rev=b4e7f2fedbe3df8c35545feb000176d3e7b2bc32#b4e7f2fedbe3df8c35545feb000176d3e7b2bc32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -10,22 +10,22 @@ default = ["sys"]
|
|||||||
manual_test = []
|
manual_test = []
|
||||||
|
|
||||||
sys = [
|
sys = [
|
||||||
"dep:widestring",
|
"dep:widestring",
|
||||||
"dep:windows",
|
"dep:windows",
|
||||||
"dep:core-foundation",
|
"dep:core-foundation",
|
||||||
"dep:security-framework",
|
"dep:security-framework",
|
||||||
"dep:security-framework-sys",
|
"dep:security-framework-sys",
|
||||||
"dep:gio",
|
"dep:gio",
|
||||||
"dep:libsecret",
|
"dep:libsecret",
|
||||||
"dep:zbus",
|
"dep:zbus",
|
||||||
"dep:zbus_polkit",
|
"dep:zbus_polkit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = "=0.8.4"
|
aes = "=0.8.4"
|
||||||
anyhow = "=1.0.93"
|
anyhow = "=1.0.93"
|
||||||
arboard = { version = "=3.4.1", default-features = false, features = [
|
arboard = { version = "=3.4.1", default-features = false, features = [
|
||||||
"wayland-data-control",
|
"wayland-data-control",
|
||||||
] }
|
] }
|
||||||
async-stream = "=0.3.6"
|
async-stream = "=0.3.6"
|
||||||
base64 = "=0.22.1"
|
base64 = "=0.22.1"
|
||||||
@ -50,7 +50,7 @@ ssh-key = { version = "=0.6.7", default-features = false, features = [
|
|||||||
"rsa",
|
"rsa",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
] }
|
] }
|
||||||
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", branch = "km/pm-10098/clean-russh-implementation" }
|
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "b4e7f2fedbe3df8c35545feb000176d3e7b2bc32" }
|
||||||
tokio = { version = "=1.41.1", features = ["io-util", "sync", "macros", "net"] }
|
tokio = { version = "=1.41.1", features = ["io-util", "sync", "macros", "net"] }
|
||||||
tokio-stream = { version = "=0.1.15", features = ["net"] }
|
tokio-stream = { version = "=0.1.15", features = ["net"] }
|
||||||
tokio-util = { version = "=0.7.12", features = ["codec"] }
|
tokio-util = { version = "=0.7.12", features = ["codec"] }
|
||||||
|
@ -17,9 +17,11 @@ pub mod importer;
|
|||||||
pub struct BitwardenDesktopAgent {
|
pub struct BitwardenDesktopAgent {
|
||||||
keystore: ssh_agent::KeyStore,
|
keystore: ssh_agent::KeyStore,
|
||||||
cancellation_token: CancellationToken,
|
cancellation_token: CancellationToken,
|
||||||
show_ui_request_tx: tokio::sync::mpsc::Sender<(u32, String)>,
|
show_ui_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>,
|
||||||
get_ui_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
get_ui_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
||||||
request_id: Arc<Mutex<u32>>,
|
request_id: Arc<Mutex<u32>>,
|
||||||
|
/// before first unlock, or after account switching, listing keys should require an unlock to get a list of public keys
|
||||||
|
needs_unlock: Arc<Mutex<bool>>,
|
||||||
is_running: Arc<tokio::sync::Mutex<bool>>,
|
is_running: Arc<tokio::sync::Mutex<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,8 +35,30 @@ impl ssh_agent::Agent for BitwardenDesktopAgent {
|
|||||||
let request_id = self.get_request_id().await;
|
let request_id = self.get_request_id().await;
|
||||||
|
|
||||||
let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe();
|
let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe();
|
||||||
|
let message = (request_id, (ssh_key.cipher_uuid.clone(), false));
|
||||||
self.show_ui_request_tx
|
self.show_ui_request_tx
|
||||||
.send((request_id, ssh_key.cipher_uuid.clone()))
|
.send(message)
|
||||||
|
.await
|
||||||
|
.expect("Should send request to ui");
|
||||||
|
while let Ok((id, response)) = rx_channel.recv().await {
|
||||||
|
if id == request_id {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn can_list(&self) -> bool {
|
||||||
|
if !*self.needs_unlock.lock().await{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let request_id = self.get_request_id().await;
|
||||||
|
|
||||||
|
let mut rx_channel = self.get_ui_response_rx.lock().await.resubscribe();
|
||||||
|
let message = (request_id, ("".to_string(), true));
|
||||||
|
self.show_ui_request_tx
|
||||||
|
.send(message)
|
||||||
.await
|
.await
|
||||||
.expect("Should send request to ui");
|
.expect("Should send request to ui");
|
||||||
while let Ok((id, response)) = rx_channel.recv().await {
|
while let Ok((id, response)) = rx_channel.recv().await {
|
||||||
@ -75,6 +99,8 @@ impl BitwardenDesktopAgent {
|
|||||||
let keystore = &mut self.keystore;
|
let keystore = &mut self.keystore;
|
||||||
keystore.0.write().expect("RwLock is not poisoned").clear();
|
keystore.0.write().expect("RwLock is not poisoned").clear();
|
||||||
|
|
||||||
|
*self.needs_unlock.blocking_lock() = false;
|
||||||
|
|
||||||
for (key, name, cipher_id) in new_keys.iter() {
|
for (key, name, cipher_id) in new_keys.iter() {
|
||||||
match parse_key_safe(&key) {
|
match parse_key_safe(&key) {
|
||||||
Ok(private_key) => {
|
Ok(private_key) => {
|
||||||
@ -119,6 +145,14 @@ impl BitwardenDesktopAgent {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_keys(&mut self) -> Result<(), anyhow::Error> {
|
||||||
|
let keystore = &mut self.keystore;
|
||||||
|
keystore.0.write().expect("RwLock is not poisoned").clear();
|
||||||
|
*self.needs_unlock.blocking_lock() = true;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_request_id(&self) -> u32 {
|
async fn get_request_id(&self) -> u32 {
|
||||||
if !*self.is_running.lock().await {
|
if !*self.is_running.lock().await {
|
||||||
println!("[BitwardenDesktopAgent] Agent is not running, but tried to get request id");
|
println!("[BitwardenDesktopAgent] Agent is not running, but tried to get request id");
|
||||||
|
@ -14,7 +14,7 @@ use super::BitwardenDesktopAgent;
|
|||||||
|
|
||||||
impl BitwardenDesktopAgent {
|
impl BitwardenDesktopAgent {
|
||||||
pub async fn start_server(
|
pub async fn start_server(
|
||||||
auth_request_tx: tokio::sync::mpsc::Sender<(u32, String)>,
|
auth_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>,
|
||||||
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
||||||
) -> Result<Self, anyhow::Error> {
|
) -> Result<Self, anyhow::Error> {
|
||||||
let agent = BitwardenDesktopAgent {
|
let agent = BitwardenDesktopAgent {
|
||||||
@ -23,6 +23,7 @@ impl BitwardenDesktopAgent {
|
|||||||
show_ui_request_tx: auth_request_tx,
|
show_ui_request_tx: auth_request_tx,
|
||||||
get_ui_response_rx: auth_response_rx,
|
get_ui_response_rx: auth_response_rx,
|
||||||
request_id: Arc::new(tokio::sync::Mutex::new(0)),
|
request_id: Arc::new(tokio::sync::Mutex::new(0)),
|
||||||
|
needs_unlock: Arc::new(tokio::sync::Mutex::new(true)),
|
||||||
is_running: Arc::new(tokio::sync::Mutex::new(false)),
|
is_running: Arc::new(tokio::sync::Mutex::new(false)),
|
||||||
};
|
};
|
||||||
let cloned_agent_state = agent.clone();
|
let cloned_agent_state = agent.clone();
|
||||||
|
@ -12,7 +12,7 @@ use super::BitwardenDesktopAgent;
|
|||||||
|
|
||||||
impl BitwardenDesktopAgent {
|
impl BitwardenDesktopAgent {
|
||||||
pub async fn start_server(
|
pub async fn start_server(
|
||||||
auth_request_tx: tokio::sync::mpsc::Sender<(u32, String)>,
|
auth_request_tx: tokio::sync::mpsc::Sender<(u32, (String, bool))>,
|
||||||
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
auth_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>,
|
||||||
) -> Result<Self, anyhow::Error> {
|
) -> Result<Self, anyhow::Error> {
|
||||||
let agent_state = BitwardenDesktopAgent {
|
let agent_state = BitwardenDesktopAgent {
|
||||||
@ -21,6 +21,7 @@ impl BitwardenDesktopAgent {
|
|||||||
get_ui_response_rx: auth_response_rx,
|
get_ui_response_rx: auth_response_rx,
|
||||||
cancellation_token: CancellationToken::new(),
|
cancellation_token: CancellationToken::new(),
|
||||||
request_id: Arc::new(tokio::sync::Mutex::new(0)),
|
request_id: Arc::new(tokio::sync::Mutex::new(0)),
|
||||||
|
needs_unlock: Arc::new(tokio::sync::Mutex::new(true)),
|
||||||
is_running: Arc::new(tokio::sync::Mutex::new(true)),
|
is_running: Arc::new(tokio::sync::Mutex::new(true)),
|
||||||
};
|
};
|
||||||
let stream = named_pipe_listener_stream::NamedPipeServerStream::new(
|
let stream = named_pipe_listener_stream::NamedPipeServerStream::new(
|
||||||
|
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
3
apps/desktop/desktop_native/napi/index.d.ts
vendored
@ -69,12 +69,13 @@ export declare namespace sshagent {
|
|||||||
status: SshKeyImportStatus
|
status: SshKeyImportStatus
|
||||||
sshKey?: SshKey
|
sshKey?: SshKey
|
||||||
}
|
}
|
||||||
export function serve(callback: (err: Error | null, arg: string) => any): Promise<SshAgentState>
|
export function serve(callback: (err: Error | null, arg0: string, arg1: boolean) => any): Promise<SshAgentState>
|
||||||
export function stop(agentState: SshAgentState): void
|
export function stop(agentState: SshAgentState): void
|
||||||
export function isRunning(agentState: SshAgentState): boolean
|
export function isRunning(agentState: SshAgentState): boolean
|
||||||
export function setKeys(agentState: SshAgentState, newKeys: Array<PrivateKey>): void
|
export function setKeys(agentState: SshAgentState, newKeys: Array<PrivateKey>): void
|
||||||
export function lock(agentState: SshAgentState): void
|
export function lock(agentState: SshAgentState): void
|
||||||
export function importKey(encodedKey: string, password: string): SshKeyImportResult
|
export function importKey(encodedKey: string, password: string): SshKeyImportResult
|
||||||
|
export function clearKeys(agentState: SshAgentState): void
|
||||||
export function generateKeypair(keyAlgorithm: string): Promise<SshKey>
|
export function generateKeypair(keyAlgorithm: string): Promise<SshKey>
|
||||||
export class SshAgentState { }
|
export class SshAgentState { }
|
||||||
}
|
}
|
||||||
|
@ -247,15 +247,15 @@ pub mod sshagent {
|
|||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub async fn serve(
|
pub async fn serve(
|
||||||
callback: ThreadsafeFunction<String, CalleeHandled>,
|
callback: ThreadsafeFunction<(String, bool), CalleeHandled>,
|
||||||
) -> napi::Result<SshAgentState> {
|
) -> napi::Result<SshAgentState> {
|
||||||
let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::<(u32, String)>(32);
|
let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::<(u32, (String, bool))>(32);
|
||||||
let (auth_response_tx, auth_response_rx) = tokio::sync::broadcast::channel::<(u32, bool)>(32);
|
let (auth_response_tx, auth_response_rx) = tokio::sync::broadcast::channel::<(u32, bool)>(32);
|
||||||
let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx));
|
let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx));
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = auth_response_rx;
|
let _ = auth_response_rx;
|
||||||
|
|
||||||
while let Some((request_id, cipher_uuid)) = auth_request_rx.recv().await {
|
while let Some((request_id, (cipher_uuid, is_list_request))) = auth_request_rx.recv().await {
|
||||||
let cloned_request_id = request_id.clone();
|
let cloned_request_id = request_id.clone();
|
||||||
let cloned_cipher_uuid = cipher_uuid.clone();
|
let cloned_cipher_uuid = cipher_uuid.clone();
|
||||||
let cloned_response_tx_arc = auth_response_tx_arc.clone();
|
let cloned_response_tx_arc = auth_response_tx_arc.clone();
|
||||||
@ -266,7 +266,7 @@ pub mod sshagent {
|
|||||||
let auth_response_tx_arc = cloned_response_tx_arc;
|
let auth_response_tx_arc = cloned_response_tx_arc;
|
||||||
let callback = cloned_callback;
|
let callback = cloned_callback;
|
||||||
let promise_result: Result<Promise<bool>, napi::Error> =
|
let promise_result: Result<Promise<bool>, napi::Error> =
|
||||||
callback.call_async(Ok(cipher_uuid)).await;
|
callback.call_async(Ok((cipher_uuid, is_list_request))).await;
|
||||||
match promise_result {
|
match promise_result {
|
||||||
Ok(promise_result) => match promise_result.await {
|
Ok(promise_result) => match promise_result.await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
@ -345,6 +345,12 @@ pub mod sshagent {
|
|||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn clear_keys(agent_state: &mut SshAgentState) -> napi::Result<()> {
|
||||||
|
let bitwarden_agent_state = &mut agent_state.state;
|
||||||
|
bitwarden_agent_state.clear_keys().map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub async fn generate_keypair(key_algorithm: String) -> napi::Result<SshKey> {
|
pub async fn generate_keypair(key_algorithm: String) -> napi::Result<SshKey> {
|
||||||
desktop_core::ssh_agent::generator::generate_keypair(key_algorithm)
|
desktop_core::ssh_agent::generator::generate_keypair(key_algorithm)
|
||||||
|
@ -27,7 +27,7 @@ export class MainSshAgentService {
|
|||||||
init() {
|
init() {
|
||||||
// handle sign request passing to UI
|
// handle sign request passing to UI
|
||||||
sshagent
|
sshagent
|
||||||
.serve(async (err: Error, cipherId: string) => {
|
.serve(async (err: Error, cipherId: string, isListRequest: boolean) => {
|
||||||
// clear all old (> SIGN_TIMEOUT) requests
|
// clear all old (> SIGN_TIMEOUT) requests
|
||||||
this.requestResponses = this.requestResponses.filter(
|
this.requestResponses = this.requestResponses.filter(
|
||||||
(response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT),
|
(response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT),
|
||||||
@ -37,6 +37,7 @@ export class MainSshAgentService {
|
|||||||
const id_for_this_request = this.request_id;
|
const id_for_this_request = this.request_id;
|
||||||
this.messagingService.send("sshagent.signrequest", {
|
this.messagingService.send("sshagent.signrequest", {
|
||||||
cipherId,
|
cipherId,
|
||||||
|
isListRequest,
|
||||||
requestId: id_for_this_request,
|
requestId: id_for_this_request,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,5 +112,11 @@ export class MainSshAgentService {
|
|||||||
sshagent.lock(this.agentState);
|
sshagent.lock(this.agentState);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("sshagent.clearkeys", async (event: any) => {
|
||||||
|
if (this.agentState != null) {
|
||||||
|
sshagent.clearKeys(this.agentState);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,9 @@ const sshAgent = {
|
|||||||
lock: async () => {
|
lock: async () => {
|
||||||
return await ipcRenderer.invoke("sshagent.lock");
|
return await ipcRenderer.invoke("sshagent.lock");
|
||||||
},
|
},
|
||||||
|
clearKeys: async () => {
|
||||||
|
return await ipcRenderer.invoke("sshagent.clearkeys");
|
||||||
|
},
|
||||||
importKey: async (key: string, password: string): Promise<ssh.SshKeyImportResult> => {
|
importKey: async (key: string, password: string): Promise<ssh.SshKeyImportResult> => {
|
||||||
const res = await ipcRenderer.invoke("sshagent.importkey", {
|
const res = await ipcRenderer.invoke("sshagent.importkey", {
|
||||||
privateKey: key,
|
privateKey: key,
|
||||||
|
@ -5,9 +5,11 @@ import {
|
|||||||
concatMap,
|
concatMap,
|
||||||
EMPTY,
|
EMPTY,
|
||||||
filter,
|
filter,
|
||||||
|
firstValueFrom,
|
||||||
from,
|
from,
|
||||||
map,
|
map,
|
||||||
of,
|
of,
|
||||||
|
skip,
|
||||||
Subject,
|
Subject,
|
||||||
switchMap,
|
switchMap,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
@ -17,6 +19,7 @@ import {
|
|||||||
withLatestFrom,
|
withLatestFrom,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
@ -52,6 +55,7 @@ export class SshAgentService implements OnDestroy {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private desktopSettingsService: DesktopSettingsService,
|
private desktopSettingsService: DesktopSettingsService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@ -112,19 +116,34 @@ export class SshAgentService implements OnDestroy {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// This concatMap handles showing the dialog to approve the request.
|
// This concatMap handles showing the dialog to approve the request.
|
||||||
concatMap(([message, decryptedCiphers]) => {
|
concatMap(async ([message, ciphers]) => {
|
||||||
const cipherId = message.cipherId as string;
|
const cipherId = message.cipherId as string;
|
||||||
|
const isListRequest = message.isListRequest as boolean;
|
||||||
const requestId = message.requestId as number;
|
const requestId = message.requestId as number;
|
||||||
|
|
||||||
if (decryptedCiphers === undefined) {
|
if (isListRequest) {
|
||||||
return of(false).pipe(
|
const sshCiphers = ciphers.filter(
|
||||||
switchMap((result) =>
|
(cipher) => cipher.type === CipherType.SshKey && !cipher.isDeleted,
|
||||||
ipc.platform.sshAgent.signRequestResponse(requestId, Boolean(result)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
const keys = sshCiphers.map((cipher) => {
|
||||||
|
return {
|
||||||
|
name: cipher.name,
|
||||||
|
privateKey: cipher.sshKey.privateKey,
|
||||||
|
cipherId: cipher.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
await ipc.platform.sshAgent.setKeys(keys);
|
||||||
|
await ipc.platform.sshAgent.signRequestResponse(requestId, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cipher = decryptedCiphers.find((cipher) => cipher.id == cipherId);
|
if (ciphers === undefined) {
|
||||||
|
ipc.platform.sshAgent
|
||||||
|
.signRequestResponse(requestId, false)
|
||||||
|
.catch((e) => this.logService.error("Failed to respond to SSH request", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
const cipher = ciphers.find((cipher) => cipher.id == cipherId);
|
||||||
|
|
||||||
ipc.platform.focusWindow();
|
ipc.platform.focusWindow();
|
||||||
const dialogRef = ApproveSshRequestComponent.open(
|
const dialogRef = ApproveSshRequestComponent.open(
|
||||||
@ -133,16 +152,34 @@ export class SshAgentService implements OnDestroy {
|
|||||||
this.i18nService.t("unknownApplication"),
|
this.i18nService.t("unknownApplication"),
|
||||||
);
|
);
|
||||||
|
|
||||||
return dialogRef.closed.pipe(
|
const result = await firstValueFrom(dialogRef.closed);
|
||||||
switchMap((result) => {
|
return ipc.platform.sshAgent.signRequestResponse(requestId, result);
|
||||||
return ipc.platform.sshAgent.signRequestResponse(requestId, Boolean(result));
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
this.accountService.activeAccount$.pipe(skip(1), takeUntil(this.destroy$)).subscribe({
|
||||||
|
next: (account) => {
|
||||||
|
this.logService.info("Active account changed, clearing SSH keys");
|
||||||
|
ipc.platform.sshAgent
|
||||||
|
.clearKeys()
|
||||||
|
.catch((e) => this.logService.error("Failed to clear SSH keys", e));
|
||||||
|
},
|
||||||
|
error: (e: unknown) => {
|
||||||
|
this.logService.error("Error in active account observable", e);
|
||||||
|
ipc.platform.sshAgent
|
||||||
|
.clearKeys()
|
||||||
|
.catch((e) => this.logService.error("Failed to clear SSH keys", e));
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.logService.info("Active account observable completed, clearing SSH keys");
|
||||||
|
ipc.platform.sshAgent
|
||||||
|
.clearKeys()
|
||||||
|
.catch((e) => this.logService.error("Failed to clear SSH keys", e));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
combineLatest([
|
combineLatest([
|
||||||
timer(0, this.SSH_REFRESH_INTERVAL),
|
timer(0, this.SSH_REFRESH_INTERVAL),
|
||||||
this.desktopSettingsService.sshAgentEnabled$,
|
this.desktopSettingsService.sshAgentEnabled$,
|
||||||
@ -150,7 +187,7 @@ export class SshAgentService implements OnDestroy {
|
|||||||
.pipe(
|
.pipe(
|
||||||
concatMap(async ([, enabled]) => {
|
concatMap(async ([, enabled]) => {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
await ipc.platform.sshAgent.setKeys([]);
|
await ipc.platform.sshAgent.clearKeys();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user