mirror of
https://github.com/narrrl/proton-sdk-rs.git
synced 2026-11-03 16:41:26 +01:00
fmt + Clippy
This commit is contained in:
1 parent
ec590fd52b
commit
e2f5ab7ad6
17 files changed
+263
-158
No files matched your search
@@ -54,13 +54,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let root = client.get_my_files_folder().await?;
|
||||
eprintln!(
|
||||
"[+] root: name={:?} kind={:?} uid={}",
|
||||
root.name,
|
||||
root.kind,
|
||||
root.uid,
|
||||
root.name, root.kind, root.uid,
|
||||
);
|
||||
|
||||
eprintln!("[*] enumerating children ...");
|
||||
let child_uids = client.enumerate_folder_children_node_uids(&root.uid).await?;
|
||||
let child_uids = client
|
||||
.enumerate_folder_children_node_uids(&root.uid)
|
||||
.await?;
|
||||
let children = client.enumerate_nodes(&child_uids).await?;
|
||||
eprintln!("[+] {} children:", children.len());
|
||||
for c in &children {
|
||||
|
||||
@@ -194,14 +194,23 @@ mod tests {
|
||||
assert!(cache.main_volume_id().await.unwrap().is_none());
|
||||
|
||||
cache.set_client_uid("client-xyz").await.unwrap();
|
||||
cache.set_main_volume_id(&VolumeId::from("vol-1")).await.unwrap();
|
||||
cache
|
||||
.set_main_volume_id(&VolumeId::from("vol-1"))
|
||||
.await
|
||||
.unwrap();
|
||||
cache
|
||||
.set_my_files_share_id(&ShareId::from("mf-share"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(cache.client_uid().await.unwrap().as_deref(), Some("client-xyz"));
|
||||
assert_eq!(cache.main_volume_id().await.unwrap(), Some(VolumeId::from("vol-1")));
|
||||
assert_eq!(
|
||||
cache.client_uid().await.unwrap().as_deref(),
|
||||
Some("client-xyz")
|
||||
);
|
||||
assert_eq!(
|
||||
cache.main_volume_id().await.unwrap(),
|
||||
Some(VolumeId::from("vol-1"))
|
||||
);
|
||||
assert_eq!(
|
||||
cache.my_files_share_id().await.unwrap(),
|
||||
Some(ShareId::from("mf-share"))
|
||||
@@ -211,7 +220,9 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn malformed_entry_is_evicted_as_miss() {
|
||||
let repo = InMemoryCacheRepository::shared();
|
||||
repo.set(&node_key(&uid("v", "l")), "not json", &[]).await.unwrap();
|
||||
repo.set(&node_key(&uid("v", "l")), "not json", &[])
|
||||
.await
|
||||
.unwrap();
|
||||
let cache = DriveEntityCache::new(repo.clone());
|
||||
assert!(cache.try_get_node(&uid("v", "l")).await.unwrap().is_none());
|
||||
// The bad entry was removed.
|
||||
|
||||
@@ -12,12 +12,12 @@ use tokio::sync::Mutex;
|
||||
use proton_sdk::account::AccountClient;
|
||||
use proton_sdk::cache::{CacheRepository, InMemoryCacheRepository};
|
||||
use proton_sdk::crypto::PrivateKey;
|
||||
use proton_sdk::error::{ProtonError, Result};
|
||||
use proton_sdk::http::ApiHttpClient;
|
||||
use proton_sdk::crypto::{
|
||||
build_volume_creation_material, generate_node_hash_key, generate_node_key, verify_detached,
|
||||
ContentKey, VerificationKeyRing, VerificationStatus,
|
||||
};
|
||||
use proton_sdk::error::{ProtonError, Result};
|
||||
use proton_sdk::http::ApiHttpClient;
|
||||
use proton_sdk::ids::{AddressId, DriveEventId, LinkId, NodeUid, ShareId, VolumeId};
|
||||
use proton_sdk::session::ProtonApiSession;
|
||||
use proton_sdk::telemetry::{NoopTelemetry, Telemetry, TelemetryExt};
|
||||
@@ -25,6 +25,7 @@ use proton_sdk::telemetry::{NoopTelemetry, Telemetry, TelemetryExt};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha1::Sha1;
|
||||
|
||||
use crate::cache::DriveEntityCache;
|
||||
use crate::crypto::{
|
||||
decrypt_content_key_verified, decrypt_extended_attributes_verified, decrypt_link,
|
||||
decrypt_link_verified, decrypt_share_key,
|
||||
@@ -34,19 +35,18 @@ use crate::dtos::{
|
||||
BlockUploadPreparationResponse, BlockVerificationInputResponse, BlockVerifier,
|
||||
CommonExtendedAttributes, ExtendedAttributes, FileContentDigests, FileCreationRequest,
|
||||
FileCreationResponse, FolderChildrenResponse, FolderCreationRequest, FolderCreationResponse,
|
||||
LinkDetailsDto, LinkDetailsRequest, LinkDetailsResponse, LinkDto, LinkType, MoveLinkRequest,
|
||||
MoveMultipleLinksItem, MoveMultipleLinksRequest, MultipleLinksRequest, MyFilesShareResponse,
|
||||
NodeNameAvailabilityRequest,
|
||||
NodeNameAvailabilityResponse, RenameLinkRequest, RevisionCreationRequest,
|
||||
LatestVolumeEventResponse, LinkDetailsDto, LinkDetailsRequest, LinkDetailsResponse, LinkDto,
|
||||
LinkType, MoveLinkRequest, MoveMultipleLinksItem, MoveMultipleLinksRequest,
|
||||
MultipleLinksRequest, MyFilesShareResponse, NodeNameAvailabilityRequest,
|
||||
NodeNameAvailabilityResponse, PhotosAttributesDto, RenameLinkRequest, RevisionCreationRequest,
|
||||
RevisionCreationResponse, RevisionDto, RevisionResponse, RevisionUpdateRequest,
|
||||
LatestVolumeEventResponse, PhotosAttributesDto, ThumbnailBlockListRequest,
|
||||
ThumbnailBlockListResponse, ThumbnailCreationRequest, ThumbnailDto, TimelinePhotoListResponse,
|
||||
VolumeCreationRequest, VolumeEventDto, VolumeEventListResponse, VolumeTrashResponse,
|
||||
ThumbnailBlockListRequest, ThumbnailBlockListResponse, ThumbnailCreationRequest, ThumbnailDto,
|
||||
TimelinePhotoListResponse, VolumeCreationRequest, VolumeEventDto, VolumeEventListResponse,
|
||||
VolumeTrashResponse,
|
||||
};
|
||||
use crate::photos::{PhotoUploadMetadata, PhotosTimelineItem};
|
||||
use crate::cache::DriveEntityCache;
|
||||
use crate::events::{DriveEvent, DriveEventScopeId};
|
||||
use crate::node::{FileThumbnail, Node, NodeKind, Thumbnail, ThumbnailType};
|
||||
use crate::photos::{PhotoUploadMetadata, PhotosTimelineItem};
|
||||
|
||||
/// Content blocks are 4 MiB of plaintext each (C# `RevisionWriter.DefaultBlockSize`).
|
||||
const DEFAULT_BLOCK_SIZE: usize = 1 << 22;
|
||||
@@ -223,7 +223,9 @@ impl ProtonDriveClient {
|
||||
let parent_key = self
|
||||
.resolve_parent_key(&uid.volume_id, &details.link)
|
||||
.await?;
|
||||
let node = self.build_node(&uid.volume_id, &details, &parent_key).await?;
|
||||
let node = self
|
||||
.build_node(&uid.volume_id, &details, &parent_key)
|
||||
.await?;
|
||||
timer.success();
|
||||
Ok(Some(node))
|
||||
}
|
||||
@@ -326,8 +328,7 @@ impl ProtonDriveClient {
|
||||
let mut page = 0_usize;
|
||||
|
||||
loop {
|
||||
let path =
|
||||
format!("volumes/{volume_id}/trash?pageSize={TRASH_PAGE_SIZE}&page={page}");
|
||||
let path = format!("volumes/{volume_id}/trash?pageSize={TRASH_PAGE_SIZE}&page={page}");
|
||||
let response: VolumeTrashResponse = self.http.get(&path).await?;
|
||||
|
||||
let mut count = 0_usize;
|
||||
@@ -474,9 +475,9 @@ impl ProtonDriveClient {
|
||||
let content_key_packet_b64 = file.content_key_packet.ok_or_else(|| {
|
||||
ProtonError::invalid_operation("file is missing its content key packet")
|
||||
})?;
|
||||
let content_key_packet = BASE64
|
||||
.decode(content_key_packet_b64.trim())
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("decode content key packet: {e}")))?;
|
||||
let content_key_packet = BASE64.decode(content_key_packet_b64.trim()).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("decode content key packet: {e}"))
|
||||
})?;
|
||||
let revision_id = file
|
||||
.active_revision
|
||||
.map(|r| r.id)
|
||||
@@ -536,7 +537,8 @@ impl ProtonDriveClient {
|
||||
uid: &NodeUid,
|
||||
thumbnail_type: ThumbnailType,
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
self.download_thumbnail_ctx(uid, thumbnail_type, false).await
|
||||
self.download_thumbnail_ctx(uid, thumbnail_type, false)
|
||||
.await
|
||||
}
|
||||
|
||||
/// As [`download_thumbnail`](Self::download_thumbnail), but routes node and
|
||||
@@ -646,10 +648,7 @@ impl ProtonDriveClient {
|
||||
}
|
||||
Ok((_, None)) => {
|
||||
let msg = format!("node {uid} has no thumbnail of the requested type");
|
||||
results.push(FileThumbnail::err(
|
||||
uid,
|
||||
ProtonError::invalid_operation(msg),
|
||||
));
|
||||
results.push(FileThumbnail::err(uid, ProtonError::invalid_operation(msg)));
|
||||
}
|
||||
Err(e) => results.push(FileThumbnail::err(uid, e)),
|
||||
}
|
||||
@@ -695,9 +694,9 @@ impl ProtonDriveClient {
|
||||
.get_storage_blob(&block.bare_url, &block.token)
|
||||
.await
|
||||
{
|
||||
Ok(ciphertext) => {
|
||||
content_key.decrypt_block(&ciphertext).map_err(ProtonError::from)
|
||||
}
|
||||
Ok(ciphertext) => content_key
|
||||
.decrypt_block(&ciphertext)
|
||||
.map_err(ProtonError::from),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
results.push(match downloaded {
|
||||
@@ -743,7 +742,11 @@ impl ProtonDriveClient {
|
||||
for_photos: bool,
|
||||
) -> Result<(ContentKey, Option<String>)> {
|
||||
let details = self
|
||||
.get_link_details_ctx(&uid.volume_id, std::slice::from_ref(&uid.link_id), for_photos)
|
||||
.get_link_details_ctx(
|
||||
&uid.volume_id,
|
||||
std::slice::from_ref(&uid.link_id),
|
||||
for_photos,
|
||||
)
|
||||
.await?;
|
||||
let detail = details
|
||||
.links
|
||||
@@ -758,9 +761,9 @@ impl ProtonDriveClient {
|
||||
let content_key_packet_b64 = file.content_key_packet.ok_or_else(|| {
|
||||
ProtonError::invalid_operation("file is missing its content key packet")
|
||||
})?;
|
||||
let content_key_packet = BASE64
|
||||
.decode(content_key_packet_b64.trim())
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("decode content key packet: {e}")))?;
|
||||
let content_key_packet = BASE64.decode(content_key_packet_b64.trim()).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("decode content key packet: {e}"))
|
||||
})?;
|
||||
let revision_id = file
|
||||
.active_revision
|
||||
.map(|r| r.id)
|
||||
@@ -920,7 +923,9 @@ impl ProtonDriveClient {
|
||||
let mut timer = self.telemetry.start("upload_photo");
|
||||
timer.attr("aead", aead);
|
||||
if !self.ensure_photos().await? {
|
||||
return Err(ProtonError::invalid_operation("account has no photos volume"));
|
||||
return Err(ProtonError::invalid_operation(
|
||||
"account has no photos volume",
|
||||
));
|
||||
}
|
||||
let parent_uid = self
|
||||
.cache
|
||||
@@ -938,8 +943,13 @@ impl ProtonDriveClient {
|
||||
let written = self.write_blocks(&draft, reader, thumbnails).await?;
|
||||
timer.attr("size", written.total_size);
|
||||
let photos_attributes = build_photos_attributes(&draft.parent_hash_key, &written, metadata);
|
||||
self.seal_revision(&draft, &written, metadata.capture_time, Some(photos_attributes))
|
||||
.await?;
|
||||
self.seal_revision(
|
||||
&draft,
|
||||
&written,
|
||||
metadata.capture_time,
|
||||
Some(photos_attributes),
|
||||
)
|
||||
.await?;
|
||||
|
||||
timer.success();
|
||||
Ok(file_uid)
|
||||
@@ -971,7 +981,8 @@ impl ProtonDriveClient {
|
||||
let node = generate_node_key()?;
|
||||
let node_hash_key = generate_node_hash_key(&node.key)?;
|
||||
|
||||
let encrypted_name = parent_key.encrypt_and_sign(&signing_key, name.as_bytes(), true, false)?;
|
||||
let encrypted_name =
|
||||
parent_key.encrypt_and_sign(&signing_key, name.as_bytes(), true, false)?;
|
||||
let name_hash = hex::encode(hmac_sha256(&parent_hash_key, name.as_bytes()));
|
||||
let encrypted_passphrase = parent_key.encrypt(&node.passphrase)?;
|
||||
let passphrase_signature = signing_key.sign_detached(&node.passphrase)?;
|
||||
@@ -992,7 +1003,10 @@ impl ProtonDriveClient {
|
||||
let xattr_json = serde_json::to_vec(&xattr).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("serialize folder xattr: {e}"))
|
||||
})?;
|
||||
Some(node.key.encrypt_and_sign(&signing_key, &xattr_json, false, true)?)
|
||||
Some(
|
||||
node.key
|
||||
.encrypt_and_sign(&signing_key, &xattr_json, false, true)?,
|
||||
)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
@@ -1230,9 +1244,9 @@ impl ProtonDriveClient {
|
||||
for chunk in uids.chunks(MAX_BATCH_COUNT) {
|
||||
let mut items = Vec::with_capacity(chunk.len());
|
||||
for uid in chunk {
|
||||
let link = links
|
||||
.get(&uid.link_id)
|
||||
.ok_or_else(|| ProtonError::invalid_operation(format!("node {uid} not found")))?;
|
||||
let link = links.get(&uid.link_id).ok_or_else(|| {
|
||||
ProtonError::invalid_operation(format!("node {uid} not found"))
|
||||
})?;
|
||||
let parts = self
|
||||
.build_move_parts(uid, link, &dest_parent_key, &dest_hash_key, &signing_key)
|
||||
.await?;
|
||||
@@ -1403,7 +1417,8 @@ impl ProtonDriveClient {
|
||||
ContentKey::generate()
|
||||
};
|
||||
|
||||
let encrypted_name = parent_key.encrypt_and_sign(&signing_key, name.as_bytes(), true, false)?;
|
||||
let encrypted_name =
|
||||
parent_key.encrypt_and_sign(&signing_key, name.as_bytes(), true, false)?;
|
||||
let name_hash = hex::encode(hmac_sha256(&parent_hash_key, name.as_bytes()));
|
||||
let encrypted_passphrase = parent_key.encrypt(&node.passphrase)?;
|
||||
let passphrase_signature = signing_key.sign_detached(&node.passphrase)?;
|
||||
@@ -1455,22 +1470,21 @@ impl ProtonDriveClient {
|
||||
let details = self
|
||||
.get_link_details(&volume_id, std::slice::from_ref(&file_uid.link_id))
|
||||
.await?;
|
||||
let detail = details
|
||||
.links
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| ProtonError::invalid_operation(format!("file {file_uid} not found")))?;
|
||||
let detail =
|
||||
details.links.into_iter().next().ok_or_else(|| {
|
||||
ProtonError::invalid_operation(format!("file {file_uid} not found"))
|
||||
})?;
|
||||
let link = detail.link;
|
||||
let file = detail
|
||||
.file
|
||||
.ok_or_else(|| ProtonError::invalid_operation(format!("node {file_uid} is not a file")))?;
|
||||
let file = detail.file.ok_or_else(|| {
|
||||
ProtonError::invalid_operation(format!("node {file_uid} is not a file"))
|
||||
})?;
|
||||
|
||||
let content_key_packet_b64 = file.content_key_packet.ok_or_else(|| {
|
||||
ProtonError::invalid_operation("file is missing its content key packet")
|
||||
})?;
|
||||
let content_key_packet = BASE64
|
||||
.decode(content_key_packet_b64.trim())
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("decode content key packet: {e}")))?;
|
||||
let content_key_packet = BASE64.decode(content_key_packet_b64.trim()).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("decode content key packet: {e}"))
|
||||
})?;
|
||||
let active_revision_id = file
|
||||
.active_revision
|
||||
.map(|r| r.id)
|
||||
@@ -1488,7 +1502,10 @@ impl ProtonDriveClient {
|
||||
client_uid: Some(self.http.session_id().to_string()),
|
||||
intended_upload_size,
|
||||
};
|
||||
let path = format!("v2/volumes/{volume_id}/files/{}/revisions", file_uid.link_id);
|
||||
let path = format!(
|
||||
"v2/volumes/{volume_id}/files/{}/revisions",
|
||||
file_uid.link_id
|
||||
);
|
||||
let created: RevisionCreationResponse = self.http.post(&path, &request).await?;
|
||||
|
||||
Ok(RevisionDraft {
|
||||
@@ -1674,7 +1691,9 @@ impl ProtonDriveClient {
|
||||
let xattr_json = serde_json::to_vec(&extended_attributes)
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("serialize xattr: {e}")))?;
|
||||
let encrypted_xattr =
|
||||
draft.node_key.encrypt_and_sign(&draft.signing_key, &xattr_json, false, true)?;
|
||||
draft
|
||||
.node_key
|
||||
.encrypt_and_sign(&draft.signing_key, &xattr_json, false, true)?;
|
||||
|
||||
let seal_request = RevisionUpdateRequest {
|
||||
manifest_signature,
|
||||
@@ -1712,7 +1731,9 @@ impl ProtonDriveClient {
|
||||
/// photos share's `MembershipAddressId` is cached by [`ensure_photos`].
|
||||
async fn photos_membership_address(&self) -> Result<(AddressId, String, PrivateKey)> {
|
||||
if !self.ensure_photos().await? {
|
||||
return Err(ProtonError::invalid_operation("account has no photos volume"));
|
||||
return Err(ProtonError::invalid_operation(
|
||||
"account has no photos volume",
|
||||
));
|
||||
}
|
||||
let address_id = self
|
||||
.cache
|
||||
@@ -1743,7 +1764,9 @@ impl ProtonDriveClient {
|
||||
let signing_key = keys
|
||||
.get(address.primary_key_index)
|
||||
.cloned()
|
||||
.ok_or_else(|| ProtonError::invalid_operation("membership address has no primary key"))?;
|
||||
.ok_or_else(|| {
|
||||
ProtonError::invalid_operation("membership address has no primary key")
|
||||
})?;
|
||||
|
||||
Ok((address_id, address.email, signing_key))
|
||||
}
|
||||
@@ -1754,7 +1777,8 @@ impl ProtonDriveClient {
|
||||
parent_uid: &NodeUid,
|
||||
parent_key: &PrivateKey,
|
||||
) -> Result<Vec<u8>> {
|
||||
self.parent_hash_key_ctx(parent_uid, parent_key, false).await
|
||||
self.parent_hash_key_ctx(parent_uid, parent_key, false)
|
||||
.await
|
||||
}
|
||||
|
||||
/// As [`parent_hash_key`](Self::parent_hash_key), but routes the link-details
|
||||
@@ -1796,7 +1820,9 @@ impl ProtonDriveClient {
|
||||
|
||||
let packet = BASE64
|
||||
.decode(response.content_key_packet.trim())
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("decode verification packet: {e}")))?;
|
||||
.map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("decode verification packet: {e}"))
|
||||
})?;
|
||||
node_key.decrypt_content_key(&packet).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("verification content key mismatch: {e}"))
|
||||
})?;
|
||||
@@ -1883,9 +1909,7 @@ impl ProtonDriveClient {
|
||||
let mut cache = self.cache.lock().await;
|
||||
cache.main_volume_id = Some(volume_id);
|
||||
cache.my_files_root = Some(root_uid.clone());
|
||||
cache
|
||||
.folder_keys
|
||||
.insert(root_uid, decrypted_root.node_key);
|
||||
cache.folder_keys.insert(root_uid, decrypted_root.node_key);
|
||||
cache.my_files_share = Some(ShareKey {
|
||||
share_id,
|
||||
address_id: response.share.address_id.clone(),
|
||||
@@ -2156,9 +2180,9 @@ impl ProtonDriveClient {
|
||||
let content_key_packet_b64 = file.content_key_packet.ok_or_else(|| {
|
||||
ProtonError::invalid_operation("photo is missing its content key packet")
|
||||
})?;
|
||||
let content_key_packet = BASE64
|
||||
.decode(content_key_packet_b64.trim())
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("decode content key packet: {e}")))?;
|
||||
let content_key_packet = BASE64.decode(content_key_packet_b64.trim()).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("decode content key packet: {e}"))
|
||||
})?;
|
||||
let revision_id = file
|
||||
.active_revision
|
||||
.map(|r| r.id)
|
||||
@@ -2635,7 +2659,7 @@ fn to_drive_event(volume_id: &VolumeId, event: &VolumeEventDto) -> Result<DriveE
|
||||
|
||||
// VolumeEventType: 0 Delete, 1 Create, 2 Update, 3 UpdateMetadata.
|
||||
match event.event_type {
|
||||
1 | 2 | 3 => Ok(DriveEvent::NodeUpdated {
|
||||
1..=3 => Ok(DriveEvent::NodeUpdated {
|
||||
id: event.id.clone(),
|
||||
node_uid,
|
||||
parent_node_uid,
|
||||
@@ -2848,7 +2872,10 @@ mod tests {
|
||||
};
|
||||
let metadata = PhotoUploadMetadata {
|
||||
capture_time: Some(1_700_000_000),
|
||||
main_photo_uid: Some(NodeUid::new(VolumeId::new("vol-1"), LinkId::new("main-link"))),
|
||||
main_photo_uid: Some(NodeUid::new(
|
||||
VolumeId::new("vol-1"),
|
||||
LinkId::new("main-link"),
|
||||
)),
|
||||
tags: vec![PhotoTag::Video, PhotoTag::Selfie],
|
||||
};
|
||||
|
||||
@@ -2860,7 +2887,10 @@ mod tests {
|
||||
assert_eq!(attrs.capture_time, 1_700_000_000);
|
||||
assert_eq!(attrs.main_photo_link_id, Some(LinkId::new("main-link")));
|
||||
// Tags carry their `PhotoTag` discriminants.
|
||||
assert_eq!(attrs.tags, vec![PhotoTag::Video as i32, PhotoTag::Selfie as i32]);
|
||||
assert_eq!(
|
||||
attrs.tags,
|
||||
vec![PhotoTag::Video as i32, PhotoTag::Selfie as i32]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -44,8 +44,9 @@ pub fn decrypt_link(parent_key: &PrivateKey, link: &LinkDto) -> Result<Decrypted
|
||||
let node_key = PrivateKey::from_armored(&link.key, &passphrase)?;
|
||||
|
||||
let name_bytes = parent_key.decrypt_armored_message(&link.name)?;
|
||||
let name = String::from_utf8(name_bytes)
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("node name is not valid UTF-8: {e}")))?;
|
||||
let name = String::from_utf8(name_bytes).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("node name is not valid UTF-8: {e}"))
|
||||
})?;
|
||||
|
||||
Ok(DecryptedLink { node_key, name })
|
||||
}
|
||||
@@ -125,8 +126,9 @@ pub async fn decrypt_link_verified(
|
||||
// Name: an inline-signed message addressed to the parent key.
|
||||
let (name_bytes, name_status) =
|
||||
parent_key.decrypt_armored_verify(&link.name, &name_claim.ring(parent_key))?;
|
||||
let name = String::from_utf8(name_bytes)
|
||||
.map_err(|e| ProtonError::invalid_operation(format!("node name is not valid UTF-8: {e}")))?;
|
||||
let name = String::from_utf8(name_bytes).map_err(|e| {
|
||||
ProtonError::invalid_operation(format!("node name is not valid UTF-8: {e}"))
|
||||
})?;
|
||||
|
||||
let verification = NodeVerification {
|
||||
name: name_status,
|
||||
|
||||
@@ -507,7 +507,10 @@ pub struct MoveLinkRequest {
|
||||
pub parent_link_id: LinkId,
|
||||
#[serde(rename = "NodePassphrase")]
|
||||
pub passphrase: String,
|
||||
#[serde(rename = "NodePassphraseSignature", skip_serializing_if = "Option::is_none")]
|
||||
#[serde(
|
||||
rename = "NodePassphraseSignature",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub passphrase_signature: Option<String>,
|
||||
#[serde(rename = "Name")]
|
||||
pub name: String,
|
||||
@@ -550,7 +553,10 @@ pub struct MoveMultipleLinksItem {
|
||||
pub name_hash: String,
|
||||
#[serde(rename = "OriginalHash")]
|
||||
pub original_hash: String,
|
||||
#[serde(rename = "NodePassphraseSignature", skip_serializing_if = "Option::is_none")]
|
||||
#[serde(
|
||||
rename = "NodePassphraseSignature",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub passphrase_signature: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -111,13 +111,7 @@ mod tests {
|
||||
#[test]
|
||||
fn id_covers_parity_variants() {
|
||||
let id = DriveEventId::new("evt-1");
|
||||
assert_eq!(
|
||||
DriveEvent::ScopeAccessLost { id: id.clone() }.id(),
|
||||
&id
|
||||
);
|
||||
assert_eq!(
|
||||
DriveEvent::SharedWithMeUpdated { id: id.clone() }.id(),
|
||||
&id
|
||||
);
|
||||
assert_eq!(DriveEvent::ScopeAccessLost { id: id.clone() }.id(), &id);
|
||||
assert_eq!(DriveEvent::SharedWithMeUpdated { id: id.clone() }.id(), &id);
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,8 @@ mod photos;
|
||||
pub use cache::{CachedNodeInfo, DriveEntityCache};
|
||||
pub use client::ProtonDriveClient;
|
||||
pub use events::{DriveEvent, DriveEventScopeId};
|
||||
pub use photos::{PhotoTag, PhotosTimelineItem, ProtonPhotosClient};
|
||||
pub use proton_sdk::cache::{
|
||||
CacheRepository, EncryptedCacheRepository, InMemoryCacheRepository,
|
||||
};
|
||||
pub use node::{Node, NodeKind, Thumbnail, ThumbnailType};
|
||||
pub use photos::{PhotoTag, PhotosTimelineItem, ProtonPhotosClient};
|
||||
pub use proton_sdk::cache::{CacheRepository, EncryptedCacheRepository, InMemoryCacheRepository};
|
||||
|
||||
pub use proton_sdk;
|
||||
@@ -60,8 +60,9 @@ impl NodeVerification {
|
||||
/// `NotSigned` is treated as acceptable (Proton metadata is not always
|
||||
/// signed); only `NoVerifier`/`Failed` count against trust.
|
||||
pub fn is_fully_verified(&self) -> bool {
|
||||
let ok =
|
||||
|s: VerificationStatus| matches!(s, VerificationStatus::Ok | VerificationStatus::NotSigned);
|
||||
let ok = |s: VerificationStatus| {
|
||||
matches!(s, VerificationStatus::Ok | VerificationStatus::NotSigned)
|
||||
};
|
||||
ok(self.name)
|
||||
&& ok(self.passphrase)
|
||||
&& self.content_key.map_or(true, ok)
|
||||
|
||||
Reference in new issue
Block a user