fix manifest migrations not working for encrypted maFiles (#201)

fixes #200
This commit is contained in:
Carson McManus 2023-06-23 08:40:23 -04:00 committed by GitHub
parent 7950e43f70
commit 8defefb11e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 7 deletions

View file

@ -4,7 +4,9 @@
use std::{fs::File, io::Read, path::Path};
use log::debug;
use serde::de::Error;
use steamguard::SteamGuardAccount;
use thiserror::Error;
use super::{
legacy::{SdaAccount, SdaManifest},
@ -12,10 +14,10 @@ use super::{
EntryEncryptionParams, EntryLoader, Manifest,
};
pub fn load_and_migrate(
pub(crate) fn load_and_migrate(
manifest_path: &Path,
passkey: Option<&String>,
) -> anyhow::Result<(Manifest, Vec<SteamGuardAccount>)> {
) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
backup_file(manifest_path)?;
let parent = manifest_path.parent().unwrap();
parent.read_dir()?.for_each(|e| {
@ -34,11 +36,19 @@ pub fn load_and_migrate(
fn do_migrate(
manifest_path: &Path,
passkey: Option<&String>,
) -> anyhow::Result<(Manifest, Vec<SteamGuardAccount>)> {
) -> Result<(Manifest, Vec<SteamGuardAccount>), MigrationError> {
let mut file = File::open(manifest_path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
let mut manifest: MigratingManifest = deserialize_manifest(buffer)?;
let mut manifest: MigratingManifest = deserialize_manifest(buffer)
.map_err(|err| MigrationError::ManifestDeserializeFailed(err))?;
if manifest.is_encrypted() && passkey.is_none() {
return Err(MigrationError::MissingPasskey);
} else if !manifest.is_encrypted() && passkey.is_some() {
// no custom error because this is an edge case, mostly user error
return Err(MigrationError::UnexpectedError(anyhow::anyhow!("A passkey was provided but the manifest is not encrypted. Aborting migration because it would encrypt the maFiles, and you probably didn't mean to do that.")));
}
let folder = manifest_path.parent().unwrap();
let mut accounts = manifest.load_all_accounts(folder, passkey)?;
@ -70,6 +80,18 @@ fn backup_file(path: &Path) -> anyhow::Result<()> {
Ok(())
}
#[derive(Debug, Error)]
pub(crate) enum MigrationError {
#[error("Passkey is required to decrypt manifest")]
MissingPasskey,
#[error("Failed to deserialize manifest: {0}")]
ManifestDeserializeFailed(serde_json::Error),
#[error("IO error when upgrading manifest: {0}")]
IoError(#[from] std::io::Error),
#[error("An unexpected error occurred during manifest migration: {0}")]
UnexpectedError(#[from] anyhow::Error),
}
#[derive(Debug)]
enum MigratingManifest {
SDA(SdaManifest),
@ -98,6 +120,13 @@ impl MigratingManifest {
}
}
pub fn is_encrypted(&self) -> bool {
match self {
Self::SDA(manifest) => manifest.entries.iter().any(|e| e.encryption.is_some()),
Self::ManifestV1(manifest) => manifest.entries.iter().any(|e| e.encryption.is_some()),
}
}
pub fn load_all_accounts(
&self,
folder: &Path,
@ -164,15 +193,20 @@ impl From<MigratingManifest> for Manifest {
}
}
fn deserialize_manifest(text: String) -> anyhow::Result<MigratingManifest> {
fn deserialize_manifest(text: String) -> Result<MigratingManifest, serde_json::Error> {
let json: serde_json::Value = serde_json::from_str(&text)?;
debug!("deserializing manifest: version {}", json["version"]);
if json["version"] == 1 {
let manifest: ManifestV1 = serde_json::from_str(&text)?;
Ok(MigratingManifest::ManifestV1(manifest))
} else {
} else if json["version"] == serde_json::Value::Null {
let manifest: SdaManifest = serde_json::from_str(&text)?;
Ok(MigratingManifest::SDA(manifest))
} else {
Err(serde_json::Error::custom(format!(
"Unknown manifest version: {}",
json["version"]
)))
}
}

View file

@ -104,6 +104,7 @@ fn run() -> anyhow::Result<()> {
let (manifest, accounts) = load_and_migrate(path.as_path(), args.passkey.as_ref())?;
let mut manager = AccountManager::from_manifest(manifest, mafiles_dir);
manager.register_accounts(accounts);
manager.submit_passkey(args.passkey.clone());
manager.save()?;
manager
}
@ -114,7 +115,7 @@ fn run() -> anyhow::Result<()> {
}
}
let mut passkey: Option<String> = args.passkey.clone();
let mut passkey = args.passkey.clone();
manager.submit_passkey(passkey);
loop {