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 @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use crate::config::CONFIG; +use crate::grpc_utils::DeviceInfoWithAuth; use crate::{ client_service::{ handle_db_error, CacheExt, UpdateState, WorkflowInProgress, @@ -15,9 +18,8 @@ // This must be named client, because generated code from the authenticated // protobuf file references message structs from the client protobuf file // with the client:: namespace -pub mod client { - tonic::include_proto!("identity.client"); -} +use crate::client_service::client_proto as client; + pub mod auth_proto { tonic::include_proto!("identity.authenticated"); } @@ -28,7 +30,7 @@ UploadOneTimeKeysRequest, }; use client::{Empty, IdentityKeyInfo}; -use tracing::debug; +use tracing::{debug, error}; #[derive(derive_more::Constructor)] pub struct AuthenticatedService { @@ -123,6 +125,56 @@ Ok(response) } + async fn get_outbound_keys_for_user( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> + { + let message = request.into_inner(); + + use client::outbound_keys_for_user_request::Identifier; + let (user_ident, auth_type) = match message.identifier { + None => { + return Err(tonic::Status::invalid_argument("no identifier provided")) + } + Some(Identifier::Username(username)) => (username, AuthType::Password), + Some(Identifier::WalletAddress(address)) => (address, AuthType::Wallet), + }; + + let devices_map = self + .db_client + .get_keys_for_user(user_ident, &auth_type, true) + .await + .map_err(handle_db_error)? + .ok_or_else(|| match auth_type { + AuthType::Password => tonic::Status::not_found("username not found"), + AuthType::Wallet => { + tonic::Status::not_found("wallet address not found") + } + })?; + + let transformed_devices = devices_map + .into_iter() + .filter_map(|(key, device_info)| { + let device_info_with_auth = DeviceInfoWithAuth { + device_info, + auth_type: &auth_type, + }; + match client::OutboundKeyInfo::try_from(device_info_with_auth) { + Ok(key_info) => Some((key, key_info)), + Err(_) => { + error!("Failed to transform device info for key {}", key); + None + } + } + }) + .collect::>(); + + Ok(tonic::Response::new(client::OutboundKeysForUserResponse { + devices: transformed_devices, + })) + } + async fn get_keyserver_keys( &self, request: Request, diff --git a/shared/protos/identity_authenticated.proto b/shared/protos/identity_authenticated.proto --- a/shared/protos/identity_authenticated.proto +++ b/shared/protos/identity_authenticated.proto @@ -19,6 +19,15 @@ rpc RefreshUserPreKeys(RefreshUserPreKeysRequest) returns (identity.client.Empty) {} + // Called by clients to get all device keys associated with a user in order + // to open a new channel of communication on any of their devices. + // Specially, this will return the following per device: + // - Identity keys (both Content and Notif Keys) + // - PreKey (including preKey signature) + // - One-time PreKey + rpc GetOutboundKeysForUser(identity.client.OutboundKeysForUserRequest) + returns (identity.client.OutboundKeysForUserResponse) {} + // Called by user to update password and receive new access token rpc UpdateUserPasswordStart(UpdateUserPasswordStartRequest) returns (UpdateUserPasswordStartResponse) {}