Page MenuHomePhabricator

D12120.id40680.diff
No OneTemporary

D12120.id40680.diff

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
@@ -144,6 +144,22 @@
last_primary_signature: update_info.last_signature.clone(),
}
}
+
+ pub fn has_device(&self, device_id: &String) -> bool {
+ self.device_ids.contains(device_id)
+ }
+
+ pub fn is_primary_device(&self, device_id: &String) -> bool {
+ self
+ .device_ids
+ .first()
+ .filter(|it| *it == device_id)
+ .is_some()
+ }
+
+ pub fn has_secondary_device(&self, device_id: &String) -> bool {
+ self.has_device(device_id) && !self.is_primary_device(device_id)
+ }
}
// helper structs for converting to/from attribute values for sort key (a.k.a itemID)
@@ -816,6 +832,37 @@
Ok(())
}
+ /// Removes device data from devices table. If the device doesn't exist,
+ /// it is a no-op. This does not update the device list; the device ID
+ /// should be removed from the device list separately.
+ #[tracing::instrument(skip_all)]
+ pub async fn remove_device_data(
+ &self,
+ user_id: impl Into<String>,
+ device_id: impl Into<String>,
+ ) -> Result<(), Error> {
+ let user_id = user_id.into();
+ let device_id = device_id.into();
+
+ self
+ .client
+ .delete_item()
+ .table_name(devices_table::NAME)
+ .key(ATTR_USER_ID, AttributeValue::S(user_id))
+ .key(ATTR_ITEM_ID, DeviceIDAttribute(device_id).into())
+ .send()
+ .await
+ .map_err(|e| {
+ error!(
+ errorType = error_types::DEVICE_LIST_DB_LOG,
+ "Failed to delete device data: {:?}", e
+ );
+ Error::AwsSdk(e.into())
+ })?;
+
+ Ok(())
+ }
+
/// Registers primary device for user, stores its signed device list
pub async fn register_primary_device(
&self,
diff --git a/services/identity/src/database/token.rs b/services/identity/src/database/token.rs
--- a/services/identity/src/database/token.rs
+++ b/services/identity/src/database/token.rs
@@ -148,17 +148,19 @@
pub async fn delete_access_token_data(
&self,
- user_id: String,
- device_id_key: String,
+ user_id: impl Into<String>,
+ device_id_key: impl Into<String>,
) -> Result<(), Error> {
use crate::constants::token_table::*;
+ let user_id = user_id.into();
+ let device_id = device_id_key.into();
self
.client
.delete_item()
.table_name(NAME)
.key(PARTITION_KEY.to_string(), AttributeValue::S(user_id))
- .key(SORT_KEY.to_string(), AttributeValue::S(device_id_key))
+ .key(SORT_KEY.to_string(), AttributeValue::S(device_id))
.send()
.await
.map_err(|e| Error::AwsSdk(e.into()))?;
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
@@ -337,7 +337,40 @@
&self,
request: tonic::Request<Empty>,
) -> Result<tonic::Response<Empty>, tonic::Status> {
- Err(tonic::Status::unimplemented(""))
+ let (user_id, device_id) = get_user_and_device_id(&request)?;
+
+ debug!(
+ "Secondary device logout request for user_id={}, device_id={}",
+ user_id, device_id
+ );
+ self
+ .verify_device_on_device_list(
+ &user_id,
+ &device_id,
+ DeviceListItemKind::Secondary,
+ )
+ .await?;
+
+ self
+ .db_client
+ .delete_access_token_data(&user_id, &device_id)
+ .await
+ .map_err(handle_db_error)?;
+
+ self
+ .db_client
+ .remove_device_data(&user_id, &device_id)
+ .await
+ .map_err(handle_db_error)?;
+
+ self
+ .db_client
+ .delete_otks_table_rows_for_user_device(&user_id, &device_id)
+ .await
+ .map_err(handle_db_error)?;
+
+ let response = Empty {};
+ Ok(Response::new(response))
}
#[tracing::instrument(skip_all)]
@@ -645,6 +678,60 @@
}
}
+enum DeviceListItemKind {
+ Any,
+ Primary,
+ Secondary,
+}
+
+impl AuthenticatedService {
+ async fn verify_device_on_device_list(
+ &self,
+ user_id: &String,
+ device_id: &String,
+ device_kind: DeviceListItemKind,
+ ) -> Result<(), tonic::Status> {
+ let device_list = self
+ .db_client
+ .get_current_device_list(user_id)
+ .await
+ .map_err(|err| {
+ error!(
+ user_id,
+ errorType = error_types::GRPC_SERVICES_LOG,
+ "Failed fetching device list: {err}"
+ );
+ handle_db_error(err)
+ })?;
+
+ let Some(device_list) = device_list else {
+ error!(
+ user_id,
+ errorType = error_types::GRPC_SERVICES_LOG,
+ "User has no device list!"
+ );
+ return Err(Status::failed_precondition("no device list"));
+ };
+
+ use DeviceListItemKind as DeviceKind;
+ let device_on_list = match device_kind {
+ DeviceKind::Any => device_list.has_device(device_id),
+ DeviceKind::Primary => device_list.is_primary_device(device_id),
+ DeviceKind::Secondary => device_list.has_secondary_device(device_id),
+ };
+
+ if !device_on_list {
+ debug!(
+ "Device {} not on device list for user {}",
+ device_id, user_id
+ );
+ return Err(Status::permission_denied("device not on device list"));
+ }
+
+ Ok(())
+ }
+}
+
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct DeletePasswordUserInfo {
pub opaque_server_login: comm_opaque2::server::Login,

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 2:04 AM (5 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2559358
Default Alt Text
D12120.id40680.diff (5 KB)

Event Timeline