token.rs 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. use keyring::Entry;
  2. /// 令牌存取:Windows → Credential Manager,macOS → Keychain
  3. pub struct TokenStore {
  4. service: String,
  5. account: String,
  6. }
  7. impl TokenStore {
  8. pub fn new() -> Self {
  9. Self::with_names("xafpet", "jwt")
  10. }
  11. pub fn with_names(service: &str, account: &str) -> Self {
  12. Self { service: service.into(), account: account.into() }
  13. }
  14. pub fn save(&self, token: &str) -> Result<(), String> {
  15. Entry::new(&self.service, &self.account)
  16. .map_err(|e| e.to_string())?
  17. .set_password(token)
  18. .map_err(|e| e.to_string())
  19. }
  20. pub fn load(&self) -> Result<Option<String>, String> {
  21. let entry = Entry::new(&self.service, &self.account).map_err(|e| e.to_string())?;
  22. match entry.get_password() {
  23. Ok(p) => Ok(Some(p)),
  24. Err(keyring::Error::NoEntry) => Ok(None),
  25. Err(e) => Err(e.to_string()),
  26. }
  27. }
  28. pub fn clear(&self) -> Result<(), String> {
  29. let entry = Entry::new(&self.service, &self.account).map_err(|e| e.to_string())?;
  30. match entry.delete_credential() {
  31. Ok(()) => Ok(()),
  32. Err(keyring::Error::NoEntry) => Ok(()),
  33. Err(e) => Err(e.to_string()),
  34. }
  35. }
  36. }
  37. impl Default for TokenStore {
  38. fn default() -> Self {
  39. Self::new()
  40. }
  41. }
  42. #[cfg(test)]
  43. mod tests {
  44. use super::*;
  45. #[cfg(not(target_os = "macos"))]
  46. #[test]
  47. fn roundtrip_save_load_clear() {
  48. let store = TokenStore::with_names("xafpet-test", "jwt");
  49. let _ = store.clear();
  50. assert_eq!(store.load().unwrap(), None);
  51. store.save("tok-123").unwrap();
  52. assert_eq!(store.load().unwrap(), Some("tok-123".into()));
  53. store.save("tok-456").unwrap();
  54. assert_eq!(store.load().unwrap(), Some("tok-456".into()));
  55. store.clear().unwrap();
  56. assert_eq!(store.load().unwrap(), None);
  57. }
  58. }