diff --git a/Cargo.lock b/Cargo.lock index 6490472d2..73cafad60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2528,7 +2528,7 @@ dependencies = [ [[package]] name = "sargon" -version = "0.6.52" +version = "0.6.55" dependencies = [ "actix-rt", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 97e19e4cb..f61200980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon" -version = "0.6.52" +version = "0.6.55" edition = "2021" build = "build.rs" diff --git a/apple/Tests/TestCases/Profile/DeviceInfoTests.swift b/apple/Tests/TestCases/Profile/DeviceInfoTests.swift index bedfa3c80..589362390 100644 --- a/apple/Tests/TestCases/Profile/DeviceInfoTests.swift +++ b/apple/Tests/TestCases/Profile/DeviceInfoTests.swift @@ -19,21 +19,52 @@ final class DeviceInfoTests: Test { ) } - func test_codable() throws { - let raw = """ - { - "id": "66f07ca2-a9d9-49e5-8152-77aca3d1dd74", - "date": "2023-09-11T16:05:56.000Z", - "description": "iPhone" - } - """.data(using: .utf8)! - - // test decoding - let sut = try JSONDecoder().decode(SUT.self, from: raw) - XCTAssertEqual(sut, SUT.sample) - - // test encoding - let encoded = try JSONEncoder().encode(sut) - try XCTAssertEqual(JSONDecoder().decode(SUT.self, from: encoded), sut) + func test_codable_lowercase_rust_styled_uuid() throws { + func doTest(_ jsonString: String, expected: SUT? = .sample) throws { + let raw = Data(jsonString.utf8) + + // test decoding + let sut = try JSONDecoder().decode(SUT.self, from: raw) + + if let expected { + XCTAssertEqual(sut, expected) + } + + // test encoding + let encoded = try JSONEncoder().encode(sut) + try XCTAssertEqual(JSONDecoder().decode(SUT.self, from: encoded), sut) + } + + // Rust style: + // * lower UUID + // * date with milliseconds + try doTest(""" + { + "id": "66f07ca2-a9d9-49e5-8152-77aca3d1dd74", + "date": "2023-09-11T16:05:56.000Z", + "description": "iPhone" + } + """) + + // Swift style: + // * uppercase UUID + // * date without milliseconds + try doTest(""" + { + "id": "66F07CA2-A9D9-49E5-8152-77ACA3D1DD74", + "date": "2023-09-11T16:05:56Z", + "description": "iPhone" + } + """) + + // Swift style - new. + try doTest(""" + { + "id": "\(UUID().uuidString)", + "date": "\(Date.now.ISO8601Format())", + "description": "iPhone" + } + """, expected: nil) } + } diff --git a/src/profile/logic/profile_network/profile_network_details.rs b/src/profile/logic/profile_network/profile_network_details.rs index a8b2c2d50..6ac3a04f7 100644 --- a/src/profile/logic/profile_network/profile_network_details.rs +++ b/src/profile/logic/profile_network/profile_network_details.rs @@ -1,5 +1,138 @@ use crate::prelude::*; +impl AuthorizedPersonaSimple { + fn accounts_for_display( + &self, + non_hidden_accounts: &Accounts, + ) -> Result> { + let shared_accounts = self + .shared_accounts + .as_ref().map(|s| s.ids.clone()) + .unwrap_or_default() + .iter() + .map(|account_address| { + let Some(account) = non_hidden_accounts + .iter().find(|x| x.address == *account_address) + else { + // This is a sign that Profile is in a bad state somehow... + warn!("Discrepancy! AuthorizedDapp references account which does not exist {}", account_address); + return Err(CommonError::DiscrepancyAuthorizedDappReferencedAccountWhichDoesNotExist { + address: account_address.to_owned() + }) + }; + Ok(AccountForDisplay::new( + account.address, + account.display_name.clone(), + account.appearance_id + )) + }).collect::>()?; + + if shared_accounts.is_empty() { + Ok(None) + } else { + Ok(Some(shared_accounts)) + } + } + + fn pick_persona_data_from_full( + &self, + full: &PersonaData, + ) -> Result { + let full_ids = &full.ids_of_entries(); + let shared = self.shared_persona_data.clone(); + let shared_ids = shared.ids_of_entries(); + + if !full_ids.is_superset(&shared_ids) { + error!("Profile discrepancy - most likely caused by incorrect implementation of DappInteractionFlow and updating of shared persona data. \n\nDetails [persona.personaData.ids] {:?} != {:?} [simple.sharedPersonaData]\n\npersona.personaData: {full}\n\nsimple.sharedPersonaData: {shared}", full_ids, shared_ids); + return Err( + CommonError::AuthorizedDappReferencesFieldIDThatDoesNotExist, + ); + }; + + let mut name: Option = None; + if let Some(saved_name) = &full.name { + if let Some(shared) = shared.name { + if shared.id() == saved_name.id { + name = Some(saved_name.clone()); + } + }; + }; + + let phone_numbers = full + .phone_numbers + .collection + .clone() + .into_iter() + .filter(|x| { + shared + .phone_numbers + .clone() + .map(|s| s.ids.clone()) + .unwrap_or_default() + .into_iter() + .contains(&x.id) + }) + .collect::(); + + let email_addresses = full + .email_addresses + .collection + .clone() + .into_iter() + .filter(|x| { + shared + .email_addresses + .clone() + .map(|s| s.ids.clone()) + .unwrap_or_default() + .into_iter() + .contains(&x.id) + }) + .collect::(); + + Ok(PersonaData::new(name, phone_numbers, email_addresses)) + } + + fn persona_from(&self, non_hidden_personas: &Personas) -> Result { + let Some(persona) = non_hidden_personas + .iter() + .find(|x| x.address == self.identity_address) + else { + // This is a sign that Profile is in a bad state somehow... + warn!("Discrepancy! AuthorizedDapp references persona which does not exist {}", self.identity_address); + return Err(CommonError::DiscrepancyAuthorizedDappReferencedPersonaWhichDoesNotExist { + address: self.identity_address + }); + }; + Ok(persona.clone()) + } + + fn detailed( + &self, + non_hidden_personas: &Personas, + non_hidden_accounts: &Accounts, + ) -> Result { + let persona = self.persona_from(non_hidden_personas)?; + let persona_data = + self.pick_persona_data_from_full(&persona.persona_data)?; + + let shared_accounts = self.accounts_for_display(non_hidden_accounts)?; + + let has_auth_signing_key = match &persona.security_state { + EntitySecurityState::Unsecured { value: uec } => { + uec.authentication_signing.is_some() + } + }; + Ok(AuthorizedPersonaDetailed::new( + persona.address, + persona.display_name.clone(), + shared_accounts.clone(), + persona_data, + has_auth_signing_key, + )) + } +} + impl ProfileNetwork { pub fn details_for_authorized_dapp( &self, @@ -7,88 +140,15 @@ impl ProfileNetwork { ) -> Result { self.is_on_same_network_as(dapp)?; + let non_hidden_personas = &self.personas_non_hidden(); + let non_hidden_accounts = &self.accounts_non_hidden(); + let detailed_authorized_personas = dapp .references_to_authorized_personas - .clone() + .clone() .into_iter() .map(|simple| { - let Some(persona) = self - .personas_non_hidden() - .into_iter().find(|x| x.address == simple.identity_address) - else { - // This is a sign that Profile is in a bad state somehow... - warn!("Discrepancy! AuthorizedDapp references persona which does not exist {}", simple.identity_address); - return Err(CommonError::DiscrepancyAuthorizedDappReferencedPersonaWhichDoesNotExist { - address: simple.identity_address - }) - }; - - let display_name = persona.display_name.clone(); - let shared_accounts = simple - .shared_accounts - .map(|s| s.ids.clone()) - .unwrap_or_default() - .into_iter() - .map(|account_address| { - let Some(account) = self - .accounts_non_hidden() - .into_iter().find(|x| x.address == account_address) - else { - // This is a sign that Profile is in a bad state somehow... - warn!("Discrepancy! AuthorizedDapp references account which does not exist {}", account_address); - return Err(CommonError::DiscrepancyAuthorizedDappReferencedAccountWhichDoesNotExist { - address: account_address - }) - }; - Ok(AccountForDisplay::new( - account.address, - account.display_name, - account.appearance_id - )) - }).collect::>()?; - let shared_accounts = if shared_accounts.is_empty() { None } else { Some(shared_accounts) }; - - let full = persona.persona_data.clone(); - let full_ids = full.ids_of_entries(); - let shared = simple.shared_persona_data.clone(); - let shared_ids = shared.ids_of_entries(); - - if !full_ids.is_superset(&shared_ids) { - error!("Profile discrepancy - most likely caused by incorrect implementation of DappInteractionFlow and updating of shared persona data. \n\nDetails [persona.personaData.ids] {:?} != {:?} [simple.sharedPersonaData]\n\npersona.personaData: {full}\n\nsimple.sharedPersonaData: {shared}", full_ids, shared_ids); - return Err(CommonError::AuthorizedDappReferencesFieldIDThatDoesNotExist) - }; - - let mut name: Option = None; - if let Some(saved_name) = full.name { - if let Some(shared) = shared.name { - if shared.id() == saved_name.id { - name = Some(saved_name.clone()); - } - }; - }; - - let phone_numbers = full.phone_numbers.collection.clone().into_iter().filter(|x| { - shared.phone_numbers.clone().map(|s| s.ids.clone()).unwrap_or_default().into_iter().contains(&x.id) - }).collect::(); - - let email_addresses = full.email_addresses.collection.clone().into_iter().filter(|x| { - shared.email_addresses.clone().map(|s| s.ids.clone()).unwrap_or_default().into_iter().contains(&x.id) - }).collect::(); - - let persona_data = PersonaData::new(name, phone_numbers, email_addresses); - - let has_auth_signing_key = match &persona.security_state { - EntitySecurityState::Unsecured { value: uec } => uec.authentication_signing.is_some() - }; - Ok( - AuthorizedPersonaDetailed::new( - persona.address, - display_name, - shared_accounts, - persona_data, - has_auth_signing_key - ) - ) + simple.detailed(non_hidden_personas, non_hidden_accounts) }) .collect::>()?; diff --git a/src/radix_connect/mobile/relay_service/models/request/request.rs b/src/radix_connect/mobile/relay_service/models/request/request.rs index 59c0ad774..8fca256e0 100644 --- a/src/radix_connect/mobile/relay_service/models/request/request.rs +++ b/src/radix_connect/mobile/relay_service/models/request/request.rs @@ -161,7 +161,7 @@ mod tests { let public_key = PublicKey::sample(); let request = SUT::new_send_handshake_response_with_public_key( session_id.clone(), - public_key.clone(), + public_key, ); assert_eq!(request.method, Method::SendHandshakeResponse); @@ -185,7 +185,7 @@ mod tests { "data": "deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -199,7 +199,7 @@ mod tests { "sessionId": "ffffffff-ffff-ffff-ffff-ffffffffffff" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -215,7 +215,7 @@ mod tests { "data": "deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -229,7 +229,7 @@ mod tests { "sessionId": "ffffffff-ffff-ffff-ffff-ffffffffffff" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -243,7 +243,7 @@ mod tests { "sessionId": "ffffffff-ffff-ffff-ffff-ffffffffffff" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -260,7 +260,7 @@ mod tests { "data": "deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } #[test] @@ -269,7 +269,7 @@ mod tests { let public_key = PublicKey::sample(); let request = SUT::new_send_handshake_response_with_public_key( session_id.clone(), - public_key.clone(), + public_key, ); let expected_json = r#" @@ -279,6 +279,6 @@ mod tests { "data": "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf" } "#; - assert_eq_after_json_roundtrip(&request, &expected_json); + assert_eq_after_json_roundtrip(&request, expected_json); } } diff --git a/src/radix_connect/mobile/relay_service/models/response/wallet_interaction_requests.rs b/src/radix_connect/mobile/relay_service/models/response/wallet_interaction_requests.rs index d3ebf122c..5d2127464 100644 --- a/src/radix_connect/mobile/relay_service/models/response/wallet_interaction_requests.rs +++ b/src/radix_connect/mobile/relay_service/models/response/wallet_interaction_requests.rs @@ -42,6 +42,6 @@ mod tests { let original = SUT::sample(); let json = r#"["deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead"]"#; - assert_eq_after_json_roundtrip(&original, &json); + assert_eq_after_json_roundtrip(&original, json); } } diff --git a/src/radix_connect/mobile/relay_service/service.rs b/src/radix_connect/mobile/relay_service/service.rs index 2bce1d077..e13606229 100644 --- a/src/radix_connect/mobile/relay_service/service.rs +++ b/src/radix_connect/mobile/relay_service/service.rs @@ -192,7 +192,7 @@ mod tests { async fn test_get_wallet_interaction_requests() { let session = Session::sample(); // Prepare encryption keys - let mut encryption_key = session.encryption_key.clone(); + let mut encryption_key = session.encryption_key; // Serialize the request let dapp_to_wallet_interaction_unvalidated = @@ -239,7 +239,7 @@ mod tests { async fn test_send_wallet_interaction_response() { let mock_antenna = MockAntenna::with_spy(200, (), |request| { // Prepare encryption keys - let mut encryption_key = Session::sample().encryption_key.clone(); + let mut encryption_key = Session::sample().encryption_key; let mut decryption_key = encryption_key; // Serialize the response