重构git凭证托管,改为操作系统托管
This commit is contained in:
@@ -203,6 +203,38 @@ pub async fn update_github_accounts(
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Keyring token management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_account_token(
|
||||
account_id: String,
|
||||
token: String,
|
||||
) -> Result<(), AppCommandError> {
|
||||
crate::keyring_store::set_token(&account_id, &token)
|
||||
.map_err(|e| AppCommandError::io_error("Failed to save token to keyring").with_detail(e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_account_token(
|
||||
account_id: String,
|
||||
) -> Result<Option<String>, AppCommandError> {
|
||||
Ok(crate::keyring_store::get_token(&account_id))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_account_token(
|
||||
account_id: String,
|
||||
) -> Result<(), AppCommandError> {
|
||||
crate::keyring_store::delete_token(&account_id)
|
||||
.map_err(|e| AppCommandError::io_error("Failed to delete token from keyring").with_detail(e))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GitHub token validation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GitHubUserResponse {
|
||||
login: String,
|
||||
|
||||
@@ -123,8 +123,10 @@ pub fn run_credential_helper() {
|
||||
|
||||
let remote_url = format!("https://{}", host);
|
||||
if let Some(account) = find_matching_account(&settings.accounts, &remote_url) {
|
||||
println!("username={}", account.username);
|
||||
println!("password={}", account.token);
|
||||
if let Some(token) = crate::keyring_store::get_token(&account.id) {
|
||||
println!("username={}", account.username);
|
||||
println!("password={}", token);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -387,11 +389,19 @@ pub async fn try_inject_for_repo(
|
||||
}
|
||||
};
|
||||
|
||||
let token = match crate::keyring_store::get_token(&account.id) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
eprintln!("[GIT_CRED] no token in keyring for account {}", account.id);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"[GIT_CRED] injecting credentials for {} (user: {})",
|
||||
remote_url, account.username
|
||||
);
|
||||
inject_credentials(cmd, &account.username, &account.token, &askpass);
|
||||
inject_credentials(cmd, &account.username, &token, &askpass);
|
||||
true
|
||||
}
|
||||
|
||||
@@ -417,6 +427,14 @@ pub async fn try_inject_for_url(
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let token = match crate::keyring_store::get_token(&account.id) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
eprintln!("[GIT_CRED] no token in keyring for account {}", account.id);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let askpass = match ensure_askpass_script(app_data_dir) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
@@ -425,7 +443,7 @@ pub async fn try_inject_for_url(
|
||||
}
|
||||
};
|
||||
|
||||
inject_credentials(cmd, &account.username, &account.token, &askpass);
|
||||
inject_credentials(cmd, &account.username, &token, &askpass);
|
||||
true
|
||||
}
|
||||
|
||||
@@ -464,7 +482,6 @@ mod tests {
|
||||
id: "1".into(),
|
||||
server_url: "https://github.com".into(),
|
||||
username: "user1".into(),
|
||||
token: "tok1".into(),
|
||||
scopes: vec![],
|
||||
avatar_url: None,
|
||||
is_default: false,
|
||||
@@ -474,7 +491,6 @@ mod tests {
|
||||
id: "2".into(),
|
||||
server_url: "https://gitlab.example.com".into(),
|
||||
username: "user2".into(),
|
||||
token: "tok2".into(),
|
||||
scopes: vec![],
|
||||
avatar_url: None,
|
||||
is_default: true,
|
||||
@@ -500,7 +516,6 @@ mod tests {
|
||||
id: "1".into(),
|
||||
server_url: "https://github.com".into(),
|
||||
username: "personal".into(),
|
||||
token: "tok1".into(),
|
||||
scopes: vec![],
|
||||
avatar_url: None,
|
||||
is_default: false,
|
||||
@@ -510,7 +525,6 @@ mod tests {
|
||||
id: "2".into(),
|
||||
server_url: "https://github.com".into(),
|
||||
username: "work".into(),
|
||||
token: "tok2".into(),
|
||||
scopes: vec![],
|
||||
avatar_url: None,
|
||||
is_default: true,
|
||||
|
||||
30
src-tauri/src/keyring_store.rs
Normal file
30
src-tauri/src/keyring_store.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use keyring::Entry;
|
||||
|
||||
const SERVICE_NAME: &str = "codeg";
|
||||
|
||||
fn token_key(account_id: &str) -> String {
|
||||
format!("github-token:{}", account_id)
|
||||
}
|
||||
|
||||
pub fn set_token(account_id: &str, token: &str) -> Result<(), String> {
|
||||
let entry = Entry::new(SERVICE_NAME, &token_key(account_id))
|
||||
.map_err(|e| format!("keyring init error: {e}"))?;
|
||||
entry
|
||||
.set_password(token)
|
||||
.map_err(|e| format!("keyring set error: {e}"))
|
||||
}
|
||||
|
||||
pub fn get_token(account_id: &str) -> Option<String> {
|
||||
let entry = Entry::new(SERVICE_NAME, &token_key(account_id)).ok()?;
|
||||
entry.get_password().ok()
|
||||
}
|
||||
|
||||
pub fn delete_token(account_id: &str) -> Result<(), String> {
|
||||
let entry = Entry::new(SERVICE_NAME, &token_key(account_id))
|
||||
.map_err(|e| format!("keyring init error: {e}"))?;
|
||||
match entry.delete_credential() {
|
||||
Ok(()) => Ok(()),
|
||||
Err(keyring::Error::NoEntry) => Ok(()),
|
||||
Err(e) => Err(format!("keyring delete error: {e}")),
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ mod app_error;
|
||||
mod commands;
|
||||
mod db;
|
||||
pub mod git_credential;
|
||||
pub mod keyring_store;
|
||||
mod models;
|
||||
mod network;
|
||||
mod parsers;
|
||||
@@ -272,6 +273,9 @@ pub fn run() {
|
||||
version_control::get_github_accounts,
|
||||
version_control::validate_github_token,
|
||||
version_control::update_github_accounts,
|
||||
version_control::save_account_token,
|
||||
version_control::get_account_token,
|
||||
version_control::delete_account_token,
|
||||
acp_commands::acp_preflight,
|
||||
acp_commands::acp_connect,
|
||||
acp_commands::acp_prompt,
|
||||
|
||||
@@ -64,7 +64,6 @@ pub struct GitHubAccount {
|
||||
pub id: String,
|
||||
pub server_url: String,
|
||||
pub username: String,
|
||||
pub token: String,
|
||||
pub scopes: Vec<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
pub is_default: bool,
|
||||
|
||||
Reference in New Issue
Block a user