Page MenuHomePhabricator

D9016.diff
No OneTemporary

D9016.diff

diff --git a/services/backup/src/constants.rs b/services/backup/src/constants.rs
--- a/services/backup/src/constants.rs
+++ b/services/backup/src/constants.rs
@@ -20,7 +20,7 @@
pub const BACKUP_TABLE_FIELD_CREATED: &str = "created";
pub const BACKUP_TABLE_FIELD_USER_DATA: &str = "userData";
pub const BACKUP_TABLE_FIELD_USER_KEYS: &str = "userKeys";
-pub const BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS: &str = "attachmentHolders";
+pub const BACKUP_TABLE_FIELD_ATTACHMENTS: &str = "attachments";
pub const BACKUP_TABLE_INDEX_USERID_CREATED: &str = "userID-created-index";
pub const LOG_TABLE_NAME: &str = "backup-service-log";
diff --git a/services/backup/src/database/backup_item.rs b/services/backup/src/database/backup_item.rs
--- a/services/backup/src/database/backup_item.rs
+++ b/services/backup/src/database/backup_item.rs
@@ -1,13 +1,13 @@
use aws_sdk_dynamodb::types::AttributeValue;
use chrono::{DateTime, Utc};
use comm_services_lib::{
- blob::types::BlobInfo,
- database::{DBItemError, TryFromAttribute},
+ blob::{client::BlobServiceClient, types::BlobInfo},
+ database::{AttributeTryInto, DBItemError, TryFromAttribute},
};
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use crate::constants::{
- BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS, BACKUP_TABLE_FIELD_BACKUP_ID,
+ BACKUP_TABLE_FIELD_ATTACHMENTS, BACKUP_TABLE_FIELD_BACKUP_ID,
BACKUP_TABLE_FIELD_CREATED, BACKUP_TABLE_FIELD_USER_DATA,
BACKUP_TABLE_FIELD_USER_ID, BACKUP_TABLE_FIELD_USER_KEYS,
};
@@ -19,7 +19,7 @@
pub created: DateTime<Utc>,
pub user_keys: BlobInfo,
pub user_data: BlobInfo,
- pub attachment_holders: HashSet<String>,
+ pub attachments: Vec<BlobInfo>,
}
impl BackupItem {
@@ -28,7 +28,7 @@
backup_id: String,
user_keys: BlobInfo,
user_data: BlobInfo,
- attachment_holders: HashSet<String>,
+ attachments: Vec<BlobInfo>,
) -> Self {
BackupItem {
user_id,
@@ -36,7 +36,22 @@
created: chrono::Utc::now(),
user_keys,
user_data,
- attachment_holders,
+ attachments,
+ }
+ }
+
+ pub async fn revoke_holders(self, blob_client: &BlobServiceClient) {
+ blob_client
+ .schedule_revoke_holder(self.user_keys.blob_hash, self.user_keys.holder);
+
+ blob_client
+ .schedule_revoke_holder(self.user_data.blob_hash, self.user_data.holder);
+
+ for attachment_info in self.attachments {
+ blob_client.schedule_revoke_holder(
+ attachment_info.blob_hash,
+ attachment_info.holder,
+ );
}
}
}
@@ -66,10 +81,16 @@
),
]);
- if !value.attachment_holders.is_empty() {
+ if !value.attachments.is_empty() {
attrs.insert(
- BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS.to_string(),
- AttributeValue::Ss(value.attachment_holders.into_iter().collect()),
+ BACKUP_TABLE_FIELD_ATTACHMENTS.to_string(),
+ AttributeValue::L(
+ value
+ .attachments
+ .into_iter()
+ .map(AttributeValue::from)
+ .collect(),
+ ),
);
}
@@ -105,14 +126,11 @@
value.remove(BACKUP_TABLE_FIELD_USER_DATA),
)?;
- let attachments = value.remove(BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS);
- let attachment_holders = if attachments.is_some() {
- HashSet::<String>::try_from_attr(
- BACKUP_TABLE_FIELD_ATTACHMENT_HOLDERS,
- attachments,
- )?
+ let attachments = value.remove(BACKUP_TABLE_FIELD_ATTACHMENTS);
+ let attachments = if attachments.is_some() {
+ attachments.attr_try_into(BACKUP_TABLE_FIELD_ATTACHMENTS)?
} else {
- HashSet::new()
+ Vec::new()
};
Ok(BackupItem {
@@ -121,7 +139,7 @@
created,
user_keys,
user_data,
- attachment_holders,
+ attachments,
})
}
}
diff --git a/services/backup/src/http/handlers/backup.rs b/services/backup/src/http/handlers/backup.rs
--- a/services/backup/src/http/handlers/backup.rs
+++ b/services/backup/src/http/handlers/backup.rs
@@ -1,5 +1,3 @@
-use std::{collections::HashSet, convert::Infallible};
-
use actix_web::{
error::ErrorBadRequest,
web::{self, Bytes},
@@ -12,6 +10,7 @@
http::multipart::{get_named_text_field, get_text_field},
tools::Defer,
};
+use std::convert::Infallible;
use tokio_stream::{wrappers::ReceiverStream, StreamExt};
use tracing::{info, instrument, trace, warn};
@@ -49,7 +48,7 @@
)
.await?;
- let attachments_holders: HashSet<String> =
+ let attachments_hashes: Vec<String> =
match get_text_field(&mut multipart).await? {
Some((name, attachments)) => {
if name != "attachments" {
@@ -62,15 +61,28 @@
attachments.lines().map(ToString::to_string).collect()
}
- None => HashSet::new(),
+ None => Vec::new(),
};
+ let mut attachments = Vec::new();
+ let mut attachments_revokes = Vec::new();
+ for attachment_hash in attachments_hashes {
+ let (holder, revoke) =
+ create_attachment_holder(&attachment_hash, &blob_client).await?;
+
+ attachments.push(BlobInfo {
+ blob_hash: attachment_hash,
+ holder,
+ });
+ attachments_revokes.push(revoke);
+ }
+
let item = BackupItem::new(
user.user_id.clone(),
backup_id,
user_keys_blob_info,
user_data_blob_info,
- attachments_holders,
+ attachments,
);
db_client
@@ -80,21 +92,16 @@
user_keys_revoke.cancel();
user_data_revoke.cancel();
+ for attachment_revoke in attachments_revokes {
+ attachment_revoke.cancel();
+ }
for backup in db_client
.remove_old_backups(&user.user_id)
.await
.map_err(BackupError::from)?
{
- blob_client.schedule_revoke_holder(
- backup.user_keys.blob_hash,
- backup.user_keys.holder,
- );
-
- blob_client.schedule_revoke_holder(
- backup.user_data.blob_hash,
- backup.user_data.holder,
- );
+ backup.revoke_holders(&blob_client).await;
}
Ok(HttpResponse::Ok().finish())
@@ -173,6 +180,30 @@
Ok((blob_info, revoke_holder))
}
+#[instrument(skip_all, name = "create_attachment_holder")]
+async fn create_attachment_holder<'revoke, 'blob: 'revoke>(
+ attachment: &str,
+ blob_client: &'blob web::Data<BlobServiceClient>,
+) -> Result<(String, Defer<'revoke>), BackupError> {
+ let holder = uuid::Uuid::new_v4().to_string();
+
+ if !blob_client
+ .assign_holder(attachment, &holder)
+ .await
+ .map_err(BackupError::from)?
+ {
+ warn!("Blob attachment with hash {attachment:?} doesn't exist");
+ }
+
+ let revoke_hash = attachment.to_string();
+ let revoke_holder = holder.clone();
+ let revoke_holder = Defer::new(|| {
+ blob_client.schedule_revoke_holder(revoke_hash, revoke_holder)
+ });
+
+ Ok((holder, revoke_holder))
+}
+
#[instrument(name = "download_user_keys", skip_all, fields(backup_id = %path.as_str()))]
pub async fn download_user_keys(
user: UserIdentity,
diff --git a/services/comm-services-lib/src/database.rs b/services/comm-services-lib/src/database.rs
--- a/services/comm-services-lib/src/database.rs
+++ b/services/comm-services-lib/src/database.rs
@@ -282,6 +282,35 @@
}
}
+impl<T: TryFromAttribute> TryFromAttribute for Vec<T> {
+ fn try_from_attr(
+ attribute_name: impl Into<String>,
+ attribute: Option<AttributeValue>,
+ ) -> Result<Self, DBItemError> {
+ let attribute_name = attribute_name.into();
+ match attribute {
+ Some(AttributeValue::L(list)) => Ok(
+ list
+ .into_iter()
+ .map(|attribute| {
+ T::try_from_attr(format!("{attribute_name}[i]"), Some(attribute))
+ })
+ .collect::<Result<Vec<T>, _>>()?,
+ ),
+ Some(_) => Err(DBItemError::new(
+ attribute_name.into(),
+ Value::AttributeValue(attribute),
+ DBItemAttributeError::IncorrectType,
+ )),
+ None => Err(DBItemError::new(
+ attribute_name.into(),
+ Value::AttributeValue(attribute),
+ DBItemAttributeError::Missing,
+ )),
+ }
+ }
+}
+
#[deprecated = "Use `String::try_from_attr()` instead"]
pub fn parse_string_attribute(
attribute_name: impl Into<String>,

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 9:52 PM (18 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2565443
Default Alt Text
D9016.diff (7 KB)

Event Timeline