дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
feat(git): remember HTTPS credentials in the app secret store (#321)
Git no longer asks for the HTTPS user name and password on every fetch, pull and push. The password prompt offers "Remember for this host"; the credential is saved in the app's secret store (next to the saved SSH passwords, never in git's global configuration) only after the git command that used it succeeds, and a stored credential the server rejects is forgotten so the next command asks again. Settings › Version Control › Git lists saved credentials and forgets them. Each git child gets its own id, passed to the askpass helper, so concurrent commands never save or forget each other's credentials. ide-vcs reports command completion through an observer installed at start-up instead of depending on ide-ssh. Credentials are keyed by scheme, host and port, and only git's authentication errors (Authentication failed, HTTP 401/403 responses, invalid username or password) forget a stored credential. Fixes #272 Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
1 джерело
2c10b63f76
коміт
e85ffa2ae8
17 files changed
+666
-34
No files matched your search
@@ -424,6 +424,8 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
ssh::ssh_connected,
|
||||
ssh::ssh_disconnect,
|
||||
ssh::ssh_askpass_answer,
|
||||
ssh::git_credentials,
|
||||
ssh::git_credential_forget,
|
||||
ssh::ssh_terminal_command,
|
||||
deploy::deploy_upload,
|
||||
deploy::deploy_download,
|
||||
|
||||
@@ -61,13 +61,29 @@ pub fn ssh_disconnect(state: State<'_, AppState>, id: String) {
|
||||
/// Answers an `IdeEvent::Askpass` prompt (`None`: cancelled); every window drops it.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn ssh_askpass_answer(state: State<'_, AppState>, id: u64, answer: Option<String>) {
|
||||
pub fn ssh_askpass_answer(state: State<'_, AppState>, id: u64, answer: Option<String>, remember: bool) {
|
||||
if let Some(server) = &state.askpass {
|
||||
server.answer(id, answer);
|
||||
server.answer(id, answer, remember);
|
||||
}
|
||||
state.sink.emit(IdeEvent::AskpassDone { id });
|
||||
}
|
||||
|
||||
/// Settings › Git › Saved credentials: `scheme://host` of every saved entry.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn git_credentials(_state: State<'_, AppState>) -> Vec<String> {
|
||||
ide_ssh::git_creds::get().map(|s| s.list()).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Forgets the credential of `key` (a `git_credentials` entry).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn git_credential_forget(_state: State<'_, AppState>, key: String) {
|
||||
if let Some(store) = ide_ssh::git_creds::get() {
|
||||
store.forget(&key);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SshTerminalCommand {
|
||||
|
||||
@@ -66,6 +66,8 @@ impl AppState {
|
||||
let data_dir = app.path().app_data_dir()?;
|
||||
std::fs::create_dir_all(&config_dir)?;
|
||||
std::fs::create_dir_all(&data_dir)?;
|
||||
// Saved git HTTPS credentials (askpass answers them silently).
|
||||
ide_ssh::git_creds::init(data_dir.join("git-credentials.json"));
|
||||
// File › Invalidate Caches… requested at the last run.
|
||||
let removed = ide_workspace::caches::apply_pending(&data_dir);
|
||||
if !removed.is_empty() {
|
||||
@@ -158,10 +160,24 @@ fn start_askpass(app: &AppHandle) -> Option<ide_ssh::askpass::AskpassServer> {
|
||||
let on_prompt = Arc::new(move |p: ide_ssh::askpass::AskpassPrompt| {
|
||||
// A focused Settings window hands the prompt to its project window.
|
||||
let window = crate::windows::target_window(&handle).map(|w| AppState::owner_window(w.label()).to_owned());
|
||||
TauriSink { app: handle.clone(), window }.emit(IdeEvent::Askpass { id: p.id, prompt: p.prompt, kind: p.kind });
|
||||
let remember = ide_ssh::askpass::remembers(&p.prompt);
|
||||
TauriSink { app: handle.clone(), window }.emit(IdeEvent::Askpass {
|
||||
id: p.id,
|
||||
prompt: p.prompt,
|
||||
kind: p.kind,
|
||||
remember,
|
||||
});
|
||||
});
|
||||
match ide_ssh::askpass::AskpassServer::start(exe, on_prompt) {
|
||||
Ok(server) => {
|
||||
ide_vcs::set_git_command_observer(
|
||||
ide_ssh::askpass::COMMAND_ENV,
|
||||
Arc::new(|id, success, stderr| {
|
||||
if let Some(store) = ide_ssh::git_creds::get() {
|
||||
store.command_finished(id, success, stderr);
|
||||
}
|
||||
}),
|
||||
);
|
||||
ide_vcs::set_askpass_env(server.env());
|
||||
Some(server)
|
||||
}
|
||||
|
||||
Reference in new issue
Заблокувати користувача