diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs --- a/services/identity/src/client_service.rs +++ b/services/identity/src/client_service.rs @@ -639,7 +639,7 @@ let user_id = match self .client - .get_user_id_from_user_info(wallet_address.clone(), AuthType::Wallet) + .get_user_id_from_user_info(wallet_address.clone(), &AuthType::Wallet) .await .map_err(handle_db_error)? { diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs --- a/services/identity/src/database.rs +++ b/services/identity/src/database.rs @@ -29,11 +29,13 @@ NONCE_TABLE_EXPIRATION_TIME_UNIX_ATTRIBUTE, NONCE_TABLE_PARTITION_KEY, RESERVED_USERNAMES_TABLE, RESERVED_USERNAMES_TABLE_PARTITION_KEY, USERS_TABLE, USERS_TABLE_DEVICES_ATTRIBUTE, + USERS_TABLE_DEVICES_MAP_CONTENT_ONE_TIME_KEYS_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_SIGNATURE_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME, + USERS_TABLE_DEVICES_MAP_NOTIF_ONE_TIME_KEYS_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME, USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME, @@ -857,7 +859,7 @@ pub async fn username_taken(&self, username: String) -> Result { let result = self - .get_user_id_from_user_info(username, AuthType::Password) + .get_user_id_from_user_info(username, &AuthType::Password) .await?; Ok(result.is_some()) @@ -883,7 +885,7 @@ async fn get_user_from_user_info( &self, user_info: String, - auth_type: AuthType, + auth_type: &AuthType, ) -> Result>, Error> { let (index, attribute_name) = match auth_type { AuthType::Password => { @@ -951,10 +953,53 @@ } } + pub async fn get_keys_for_user( + &self, + user_info: String, + auth_type: &AuthType, + ) -> Result, Error> { + let Some(mut user) = + self.get_user_from_user_info(user_info, auth_type).await? + else { + return Ok(None); + }; + + let devices = parse_map_attribute( + USERS_TABLE_DEVICES_ATTRIBUTE, + user.remove(USERS_TABLE_DEVICES_ATTRIBUTE), + )?; + + let mut devices_response = HashMap::with_capacity(devices.len()); + for (device_id_key, device_info) in devices { + let device_info_map = + parse_map_attribute(&device_id_key, Some(device_info))?; + + let mut device_info_string_map = HashMap::new(); + for (attribute_name, attribute_value) in device_info_map { + // Excluding one-time keys since we're moving them to a separate table + if attribute_name + == USERS_TABLE_DEVICES_MAP_NOTIF_ONE_TIME_KEYS_ATTRIBUTE_NAME + || attribute_name + == USERS_TABLE_DEVICES_MAP_CONTENT_ONE_TIME_KEYS_ATTRIBUTE_NAME + { + continue; + } + + let attribute_value_str = + parse_string_attribute(&attribute_name, Some(attribute_value))?; + device_info_string_map.insert(attribute_name, attribute_value_str); + } + + devices_response.insert(device_id_key, device_info_string_map); + } + + Ok(Some(devices_response)) + } + pub async fn get_user_id_from_user_info( &self, user_info: String, - auth_type: AuthType, + auth_type: &AuthType, ) -> Result, Error> { match self .get_user_from_user_info(user_info.clone(), auth_type) @@ -976,7 +1021,7 @@ username: &str, ) -> Result)>, Error> { match self - .get_user_from_user_info(username.to_string(), AuthType::Password) + .get_user_from_user_info(username.to_string(), &AuthType::Password) .await { Ok(Some(mut user)) => { @@ -1099,8 +1144,8 @@ .map_err(|e| Error::AwsSdk(e.into()))?; let Some(mut item) = get_response.item else { - return Ok(None); - }; + return Ok(None); + }; let nonce = parse_string_attribute( NONCE_TABLE_PARTITION_KEY, @@ -1232,6 +1277,8 @@ } type AttributeName = String; +type DeviceKeys = HashMap; +type Devices = HashMap; fn create_simple_primary_key( partition_key: (AttributeName, String), @@ -1349,40 +1396,70 @@ #[allow(dead_code)] fn parse_map_attribute( - attribute_name: &'static str, + attribute_name: &str, attribute_value: Option, ) -> Result, DBItemError> { match attribute_value { Some(AttributeValue::M(map)) => Ok(map), - Some(_) => Err(DBItemError::new( - attribute_name.to_string(), - attribute_value, - DBItemAttributeError::IncorrectType, - )), - None => Err(DBItemError::new( - attribute_name.to_string(), - attribute_value, - DBItemAttributeError::Missing, - )), + Some(_) => { + error!( + attribute = attribute_name, + value = ?attribute_value, + error_type = "IncorrectType", + "Unexpected attribute type when parsing map attribute" + ); + Err(DBItemError::new( + attribute_name.to_string(), + attribute_value, + DBItemAttributeError::IncorrectType, + )) + } + None => { + error!( + attribute = attribute_name, + error_type = "Missing", + "Attribute is missing" + ); + Err(DBItemError::new( + attribute_name.to_string(), + attribute_value, + DBItemAttributeError::Missing, + )) + } } } fn parse_string_attribute( - attribute_name: &'static str, + attribute_name: &str, attribute_value: Option, ) -> Result { match attribute_value { Some(AttributeValue::S(value)) => Ok(value), - Some(_) => Err(DBItemError::new( - attribute_name.to_string(), - attribute_value, - DBItemAttributeError::IncorrectType, - )), - None => Err(DBItemError::new( - attribute_name.to_string(), - attribute_value, - DBItemAttributeError::Missing, - )), + Some(_) => { + error!( + attribute = attribute_name, + value = ?attribute_value, + error_type = "IncorrectType", + "Unexpected attribute type when parsing string attribute" + ); + Err(DBItemError::new( + attribute_name.to_string(), + attribute_value, + DBItemAttributeError::IncorrectType, + )) + } + None => { + error!( + attribute = attribute_name, + error_type = "Missing", + "Attribute is missing" + ); + Err(DBItemError::new( + attribute_name.to_string(), + attribute_value, + DBItemAttributeError::Missing, + )) + } } }