Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3714118
D14088.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D14088.diff
View Options
diff --git a/services/commtest/tests/identity_device_list_tests.rs b/services/commtest/tests/identity_device_list_tests.rs
--- a/services/commtest/tests/identity_device_list_tests.rs
+++ b/services/commtest/tests/identity_device_list_tests.rs
@@ -97,7 +97,10 @@
#[tokio::test]
async fn test_update_device_list_rpc() {
// Register user with primary device
- let primary_device = register_user_device(None, None).await;
+ let mut primary_account = MockOlmAccount::new();
+ let primary_device_keys = primary_account.public_keys();
+ let primary_device =
+ register_user_device(Some(&primary_device_keys), None).await;
let mut auth_client = get_auth_client(
&service_addr::IDENTITY_GRPC.to_string(),
primary_device.user_id.clone(),
@@ -120,6 +123,14 @@
assert!(initial_device_list.len() == 1, "Expected single device");
let primary_device_id = initial_device_list[0].clone();
+ // migrate to signed device lists
+ migrate_device_list(
+ &mut auth_client,
+ &initial_device_list,
+ &mut primary_account,
+ )
+ .await;
+
// perform update by adding a new device
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let devices_payload = vec![primary_device_id, "device2".to_string()];
@@ -170,13 +181,32 @@
.await
.expect("Couldn't connect to identity service");
- // Perform unsigned update (add a new device)
+ // perform a migration to signed device list
+ let migration_update: DeviceListHistoryItem = {
+ let latest_device_list = &[primary_device_id.to_string()];
+ let updated_device_list = migrate_device_list(
+ &mut auth_client,
+ latest_device_list,
+ &mut primary_account,
+ )
+ .await;
+
+ (
+ updated_device_list.cur_primary_signature.clone(),
+ updated_device_list.into_raw().devices,
+ )
+ };
+
+ // Perform first update (add a new device)
let first_update: DeviceListHistoryItem = {
- let update_payload =
- SignedDeviceList::from_raw_unsigned(&RawDeviceList::new(vec![
+ let update_payload = SignedDeviceList::create_signed(
+ &RawDeviceList::new(vec![
primary_device_id.to_string(),
"device2".to_string(),
- ]));
+ ]),
+ &mut primary_account,
+ None,
+ );
let update_request = UpdateDeviceListRequest::from(&update_payload);
auth_client
.update_device_list(update_request)
@@ -189,7 +219,7 @@
)
};
- // now perform a update (remove a device), but sign the device list
+ // now perform another update (remove a device)
let second_update: DeviceListHistoryItem = {
let update_payload = SignedDeviceList::create_signed(
&RawDeviceList::new(vec![primary_device_id.to_string()]),
@@ -238,6 +268,7 @@
let expected_devices_lists: Vec<DeviceListHistoryItem> = vec![
(None, vec![primary_device_id.to_string()]), // auto-generated during registration
+ migration_update,
first_update,
second_update,
];
@@ -555,3 +586,19 @@
.map(|signed| signed.into_raw())
.collect()
}
+
+async fn migrate_device_list(
+ client: &mut ChainedInterceptedAuthClient,
+ last_device_list: &[String],
+ signing_account: &mut MockOlmAccount,
+) -> SignedDeviceList {
+ let raw_list = RawDeviceList::new(Vec::from(last_device_list));
+ let signed_list =
+ SignedDeviceList::create_signed(&raw_list, signing_account, None);
+ client
+ .update_device_list(UpdateDeviceListRequest::from(&signed_list))
+ .await
+ .expect("Failed to perform signed device list migration");
+
+ signed_list
+}
diff --git a/services/identity/src/device_list.rs b/services/identity/src/device_list.rs
--- a/services/identity/src/device_list.rs
+++ b/services/identity/src/device_list.rs
@@ -382,6 +382,22 @@
is_added != is_removed
}
+ pub fn new_flow_migration_validator(
+ previous_device_list: &[&str],
+ new_device_list: &[&str],
+ calling_device_id: &str,
+ ) -> bool {
+ // new primary must be the calling device
+ if new_device_list.first() != Some(&calling_device_id) {
+ return false;
+ }
+
+ // no device added or removed, only reorder allowed
+ let previous_set: HashSet<_> = previous_device_list.iter().collect();
+ let new_set: HashSet<_> = new_device_list.iter().collect();
+ previous_set == new_set
+ }
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/services/identity/src/grpc_services/authenticated.rs b/services/identity/src/grpc_services/authenticated.rs
--- a/services/identity/src/grpc_services/authenticated.rs
+++ b/services/identity/src/grpc_services/authenticated.rs
@@ -920,13 +920,58 @@
)
.await?;
+ let is_new_flow_user = self
+ .db_client
+ .get_user_login_flow(&user_id)
+ .await?
+ .is_signed_device_list_flow();
+
let new_list = SignedDeviceList::try_from(request.into_inner())?;
let update = DeviceListUpdate::try_from(new_list)?;
- let validator =
- crate::device_list::validation::update_device_list_rpc_validator;
+
+ let validator = if is_new_flow_user {
+ // regular device list update
+ Some(crate::device_list::validation::update_device_list_rpc_validator)
+ } else {
+ // new flow migration
+ let Some(current_device_list) =
+ self.db_client.get_current_device_list(&user_id).await?
+ else {
+ tracing::warn!("User {} does not have valid device list. New flow migration impossible.", redact_sensitive_data(&user_id));
+ return Err(tonic::Status::aborted(
+ tonic_status_messages::DEVICE_LIST_ERROR,
+ ));
+ };
+
+ let calling_device_id = &device_id;
+ let previous_device_ids: Vec<&str> = current_device_list
+ .device_ids
+ .iter()
+ .map(AsRef::as_ref)
+ .collect();
+ let new_device_ids: Vec<&str> =
+ update.devices.iter().map(AsRef::as_ref).collect();
+
+ let is_valid =
+ crate::device_list::validation::new_flow_migration_validator(
+ &previous_device_ids,
+ &new_device_ids,
+ calling_device_id,
+ );
+ if !is_valid {
+ return Err(
+ crate::error::Error::DeviceList(
+ crate::error::DeviceListError::InvalidDeviceListUpdate,
+ )
+ .into(),
+ );
+ }
+ // we've already validated it, no further validator required
+ None
+ };
self
.db_client
- .apply_devicelist_update(&user_id, update, Some(validator), true)
+ .apply_devicelist_update(&user_id, update, validator, true)
.await?;
Ok(Response::new(Empty {}))
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 9, 7:49 AM (1 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2823224
Default Alt Text
D14088.diff (6 KB)
Attached To
Mode
D14088: [identity] Verify new flow migration device list update
Attached
Detach File
Event Timeline
Log In to Comment