Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3348547
D13226.id43848.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D13226.id43848.diff
View Options
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
@@ -5,6 +5,7 @@
use comm_lib::aws::DynamoDBError;
use comm_lib::shared::reserved_users::RESERVED_USERNAME_SET;
use comm_opaque2::grpc::protocol_error_to_grpc_status;
+use grpc_clients::identity::PlatformMetadata;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use siwe::eip55;
@@ -15,7 +16,7 @@
use crate::config::CONFIG;
use crate::constants::{error_types, tonic_status_messages};
use crate::database::{
- DBDeviceTypeInt, DatabaseClient, DeviceType, KeyPayload, UserInfoAndPasswordFile
+ DBDeviceTypeInt, DatabaseClient, DeviceType, KeyPayload, UserInfoAndPasswordFile, DeviceListRow
};
use crate::device_list::SignedDeviceList;
use crate::error::{DeviceListError, Error as DBError};
@@ -692,14 +693,93 @@
&self,
request: tonic::Request<RestorePasswordUserRequest>,
) -> Result<tonic::Response<AuthResponse>, tonic::Status> {
- unimplemented!();
+ let platform_metadata = get_platform_metadata(&request)?;
+ let message = request.into_inner();
+ debug!("Attempting to restore user: {:?}", &message.username);
+
+ let user_id = self
+ .client
+ .get_user_id_from_user_info(message.username.clone(), &AuthType::Password)
+ .await
+ .map_err(handle_db_error)?
+ .ok_or_else(|| {
+ tonic::Status::not_found(tonic_status_messages::USER_NOT_FOUND)
+ })?;
+
+ let flattened_device_key_upload =
+ construct_flattened_device_key_upload(&message)?;
+
+ // verify nonce
+ let challenge_response = SignedNonce::try_from(&message)?;
+ let nonce = challenge_response
+ .verify_and_get_nonce(&flattened_device_key_upload.device_id_key)?;
+ self.verify_and_remove_nonce(&nonce).await?;
+
+ let access_token = self
+ .restore_primary_device_and_get_csat(
+ user_id.clone(),
+ message.device_list,
+ platform_metadata,
+ flattened_device_key_upload,
+ )
+ .await?;
+
+ let response = AuthResponse {
+ user_id,
+ access_token,
+ username: message.username,
+ };
+ Ok(Response::new(response))
}
async fn restore_wallet_user(
&self,
request: tonic::Request<RestoreWalletUserRequest>,
) -> Result<tonic::Response<AuthResponse>, tonic::Status> {
- unimplemented!();
+ let platform_metadata = get_platform_metadata(&request)?;
+ let message = request.into_inner();
+
+ let parsed_message = parse_and_verify_siwe_message(
+ &message.siwe_message,
+ &message.siwe_signature,
+ )
+ .await?;
+ self.verify_and_remove_nonce(&parsed_message.nonce).await?;
+ let wallet_address = eip55(&parsed_message.address);
+
+ let user_id = self
+ .client
+ .get_user_id_from_user_info(wallet_address.clone(), &AuthType::Wallet)
+ .await
+ .map_err(handle_db_error)?
+ .ok_or_else(|| {
+ tonic::Status::not_found(tonic_status_messages::USER_NOT_FOUND)
+ })?;
+ let flattened_device_key_upload =
+ construct_flattened_device_key_upload(&message)?;
+
+ let social_proof =
+ SocialProof::new(message.siwe_message, message.siwe_signature);
+ self
+ .client
+ .update_wallet_user_social_proof(&user_id, social_proof)
+ .await?;
+
+ let access_token = self
+ .restore_primary_device_and_get_csat(
+ user_id.clone(),
+ message.device_list,
+ platform_metadata,
+ flattened_device_key_upload,
+ )
+ .await?;
+
+ let response = AuthResponse {
+ user_id,
+ access_token,
+ username: wallet_address,
+ };
+ Ok(Response::new(response))
}
#[tracing::instrument(skip_all)]
@@ -1166,6 +1246,91 @@
))
}
}
+
+ /// This function is used in Backup Restore protocol. It:
+ /// - Verifies singleton device list with both old and new primary signature
+ /// - Performs device list update (updates with singleton payload)
+ /// - Removes all CSATs, OTKs, Devices data for all devices
+ /// - Closes Tunnelbroker connections with these devices
+ /// - Registers the new primary device (Device Key Upload)
+ /// - Issues a new CSAT for the new primary device and returns it
+ async fn restore_primary_device_and_get_csat(
+ &self,
+ user_id: String,
+ device_list_payload: String,
+ platform_metadata: PlatformMetadata,
+ device_key_upload: FlattenedDeviceKeyUpload,
+ ) -> Result<String, tonic::Status> {
+ let new_primary_device_id = device_key_upload.device_id_key.clone();
+ let previous_primary_device_id = self
+ .client
+ .get_current_device_list(&user_id)
+ .await?
+ .and_then(|device_list| device_list.primary_device_id().cloned())
+ .ok_or_else(|| {
+ error!(
+ user_id = redact_sensitive_data(&user_id),
+ errorType = error_types::GRPC_SERVICES_LOG,
+ "User had missing or empty device list (before backup restore)!"
+ );
+ tonic::Status::failed_precondition(
+ tonic_status_messages::NO_DEVICE_LIST,
+ )
+ })?;
+
+ // Verify device list payload
+ let signed_list: SignedDeviceList = device_list_payload.parse()?;
+ let device_list_payload =
+ crate::database::DeviceListUpdate::try_from(signed_list)?;
+ crate::device_list::verify_singleton_device_list(
+ &device_list_payload,
+ &new_primary_device_id,
+ Some(&previous_primary_device_id),
+ )?;
+
+ debug!(user_id, "Attempting to revoke user's old access tokens");
+ self.client.delete_all_tokens_for_user(&user_id).await?;
+ // We must delete the one-time keys first because doing so requires device
+ // IDs from the devices table
+ debug!(user_id, "Attempting to delete user's old one-time keys");
+ self
+ .client
+ .delete_otks_table_rows_for_user(&user_id)
+ .await?;
+ debug!(user_id, "Attempting to delete user's old devices");
+ let _old_device_ids =
+ self.client.delete_devices_data_for_user(&user_id).await?;
+
+ // TODO: Revoke TB sessions with previous devices
+
+ // Reset device list (perform update)
+ let login_time = chrono::Utc::now();
+ self
+ .client
+ .register_primary_device(
+ &user_id,
+ device_key_upload,
+ platform_metadata,
+ login_time,
+ device_list_payload,
+ )
+ .await
+ .map_err(handle_db_error)?;
+
+ // Create new access token
+ let token_data = AccessTokenData::with_created_time(
+ user_id.clone(),
+ new_primary_device_id,
+ login_time,
+ crate::token::AuthType::Password,
+ &mut OsRng,
+ );
+
+ let access_token = token_data.access_token.clone();
+ self.client.put_access_token_data(token_data).await?;
+
+ Ok(access_token)
+ }
}
#[tracing::instrument(skip_all)]
@@ -1200,6 +1365,12 @@
}
}
+impl From<DBError> for tonic::Status {
+ fn from(err: DBError) -> Self {
+ handle_db_error(err)
+ }
+}
+
fn construct_user_registration_info(
message: &(impl DeviceKeyUploadActions + RegistrationActions),
user_id: Option<String>,
diff --git a/services/identity/src/database/device_list.rs b/services/identity/src/database/device_list.rs
--- a/services/identity/src/database/device_list.rs
+++ b/services/identity/src/database/device_list.rs
@@ -197,6 +197,10 @@
pub fn has_secondary_device(&self, device_id: &String) -> bool {
self.has_device(device_id) && !self.is_primary_device(device_id)
}
+
+ pub fn primary_device_id(&self) -> Option<&String> {
+ self.device_ids.first()
+ }
}
impl PlatformDetails {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 23, 3:07 PM (19 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2571309
Default Alt Text
D13226.id43848.diff (7 KB)
Attached To
Mode
D13226: [identity] Implement RestoreUser RPCs
Attached
Detach File
Event Timeline
Log In to Comment