Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3367319
D12120.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D12120.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 26, 2:29 PM (20 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2584008
Default Alt Text
D12120.diff (5 KB)
Attached To
Mode
D12120: [identity] Implement RPC to log out secondary device
Attached
Detach File
Event Timeline
Log In to Comment