fix: recover macOS OAuth data from app update backups
This commit is contained in:
1 parent
cf97e8ed24
commit
5bc3142249
8 files changed
+89
-7
No files matched your search
@@ -1127,7 +1127,33 @@ pub(crate) fn migrate_auth_dir_from_macos_app_bundle(
|
||||
));
|
||||
}
|
||||
|
||||
copy_auth_directory(&source, &destination)?;
|
||||
let migration_source = match fs::symlink_metadata(&source) {
|
||||
Ok(_) => source.clone(),
|
||||
Err(error) if error.kind() == io::ErrorKind::NotFound => source
|
||||
.ancestors()
|
||||
.find_map(|directory| {
|
||||
if directory.file_name()? != std::ffi::OsStr::new("Contents") {
|
||||
return None;
|
||||
}
|
||||
let app = directory.parent()?;
|
||||
if !app.extension()?.to_string_lossy().eq_ignore_ascii_case("app") {
|
||||
return None;
|
||||
}
|
||||
Some(
|
||||
app.parent()?
|
||||
.join(".EasyCLIProxyAPI.app.update-backup")
|
||||
.join(source.strip_prefix(app).ok()?),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| source.clone()),
|
||||
Err(error) => {
|
||||
return Err(format!(
|
||||
"Failed to check old OAuth directory {}: {error}",
|
||||
path_to_string(&source)
|
||||
));
|
||||
}
|
||||
};
|
||||
copy_auth_directory(&migration_source, &destination)?;
|
||||
config.auth_dir = DEFAULT_AUTH_DIR.to_string();
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@@ -2380,6 +2380,9 @@ fn main() {
|
||||
Ok(config) => config,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
if portable_update_ack.is_some() {
|
||||
return;
|
||||
}
|
||||
let mut config = GuiConfigFile::default();
|
||||
if let Err(secret_error) = ensure_strong_management_secret(&mut config) {
|
||||
eprintln!("Failed to initialize WebUI security key: {secret_error}");
|
||||
|
||||
@@ -627,10 +627,13 @@ fn portable_update_zip_accepts_the_complete_release_package() {
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, target_os = "linux"))]
|
||||
#[test]
|
||||
fn portable_update_replacement_preserves_user_data_and_can_roll_back() {
|
||||
let root = agent_test_home("portable-replace");
|
||||
let platform = current_core_platform().unwrap();
|
||||
let old_archive = core_release_asset_name("v7.2.100", &platform);
|
||||
let new_archive = core_release_asset_name("v7.2.109", &platform);
|
||||
let app_dir = root.join("app");
|
||||
let work_dir = root.join("work");
|
||||
let staging = work_dir.join("staging");
|
||||
@@ -644,7 +647,7 @@ fn portable_update_replacement_preserves_user_data_and_can_roll_back() {
|
||||
fs::write(
|
||||
app_dir
|
||||
.join("cpa-core")
|
||||
.join("CLIProxyAPI_7.2.100_windows_amd64.zip"),
|
||||
.join(&old_archive),
|
||||
b"old core archive",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -670,7 +673,7 @@ fn portable_update_replacement_preserves_user_data_and_can_roll_back() {
|
||||
fs::write(
|
||||
staging
|
||||
.join("cpa-core")
|
||||
.join("CLIProxyAPI_7.2.109_windows_amd64.zip"),
|
||||
.join(&new_archive),
|
||||
b"new core archive",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -680,17 +683,17 @@ fn portable_update_replacement_preserves_user_data_and_can_roll_back() {
|
||||
staged_exe: staging.join(PORTABLE_APP_BINARY),
|
||||
current_manifest: app_dir.join(PORTABLE_APP_MANIFEST_FILE),
|
||||
staged_manifest: staging.join(PORTABLE_APP_MANIFEST_FILE),
|
||||
backup_exe: app_dir.join(".EasyCLIProxyAPI.exe.update-backup"),
|
||||
backup_exe: app_dir.join(format!(".{PORTABLE_APP_BINARY}.update-backup")),
|
||||
backup_manifest: app_dir.join(".portable-app.json.update-backup"),
|
||||
current_core_version: app_dir.join(CORE_VERSION_FILE),
|
||||
staged_core_version: staging.join(CORE_VERSION_FILE),
|
||||
backup_core_version: app_dir.join(".core-version.txt.update-backup"),
|
||||
staged_core_archive: staging
|
||||
.join("cpa-core")
|
||||
.join("CLIProxyAPI_7.2.109_windows_amd64.zip"),
|
||||
.join(&new_archive),
|
||||
target_core_archive: app_dir
|
||||
.join("cpa-core")
|
||||
.join("CLIProxyAPI_7.2.109_windows_amd64.zip"),
|
||||
.join(&new_archive),
|
||||
install_core_archive: true,
|
||||
ack_path: work_dir.join("update-started.ack"),
|
||||
work_dir,
|
||||
|
||||
@@ -756,6 +756,52 @@ fn packaged_macos_auth_directory_is_copied_before_config_is_repointed() {
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_macos_auth_migration_recovers_the_replaced_app_backup() {
|
||||
let root = agent_test_home("macos-auth-update-backup");
|
||||
let app = root.join("Applications/EasyCLIProxyAPI.app");
|
||||
let relative = Path::new("Contents/MacOS/oauth");
|
||||
let source = app.join(relative);
|
||||
let backup = root.join("Applications/.EasyCLIProxyAPI.app.update-backup");
|
||||
let old_auth = backup.join(relative);
|
||||
let persistent = root.join("Library/Application Support/com.cpa.gui");
|
||||
let install_dir = persistent.join("cpa-core");
|
||||
let destination = persistent.join(OAUTH_DIR_NAME);
|
||||
fs::create_dir_all(app.join("Contents/MacOS")).unwrap();
|
||||
fs::create_dir_all(old_auth.join("logs/archive")).unwrap();
|
||||
fs::write(old_auth.join("account.json"), b"existing-credential").unwrap();
|
||||
fs::write(old_auth.join("logs/archive/core.log"), b"historical-log").unwrap();
|
||||
let mut config = GuiConfigFile {
|
||||
auth_dir: path_to_string(&source),
|
||||
..GuiConfigFile::default()
|
||||
};
|
||||
|
||||
assert!(
|
||||
migrate_auth_dir_from_macos_app_bundle(&mut config, &install_dir, &destination).unwrap()
|
||||
);
|
||||
assert_eq!(config.auth_dir, DEFAULT_AUTH_DIR);
|
||||
assert_eq!(fs::read(destination.join("account.json")).unwrap(), b"existing-credential");
|
||||
assert_eq!(fs::read(destination.join("logs/archive/core.log")).unwrap(), b"historical-log");
|
||||
assert_eq!(fs::read(old_auth.join("account.json")).unwrap(), b"existing-credential");
|
||||
|
||||
config.auth_dir = path_to_string(&source);
|
||||
fs::write(destination.join("account.json"), b"different-credential").unwrap();
|
||||
let error = migrate_auth_dir_from_macos_app_bundle(&mut config, &install_dir, &destination)
|
||||
.unwrap_err();
|
||||
assert!(error.contains("not overwritten"), "{error}");
|
||||
assert_eq!(config.auth_dir, path_to_string(&source));
|
||||
assert_eq!(fs::read(destination.join("account.json")).unwrap(), b"different-credential");
|
||||
assert_eq!(fs::read(old_auth.join("account.json")).unwrap(), b"existing-credential");
|
||||
|
||||
fs::create_dir_all(&source).unwrap();
|
||||
fs::write(source.join("account.json"), b"different-credential").unwrap();
|
||||
assert!(
|
||||
migrate_auth_dir_from_macos_app_bundle(&mut config, &install_dir, &destination).unwrap()
|
||||
);
|
||||
assert_eq!(fs::read(destination.join("account.json")).unwrap(), b"different-credential");
|
||||
fs::remove_dir_all(root).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_macos_auth_migration_does_not_overwrite_conflicting_credentials() {
|
||||
let root = agent_test_home("packaged-macos-auth-conflict");
|
||||
|
||||
Reference in new issue
Block a user