mirror of
https://github.com/narrrl/proton-sdk-rs.git
synced 2026-11-03 16:41:26 +01:00
feature (public link retrievel)
This commit is contained in:
1 parent
64f977edb3
commit
2fa9b90265
2 files changed
+48
-19
No files matched your search
@@ -54,10 +54,10 @@ use crate::dtos::{
|
|||||||
NodeNameAvailabilityRequest, NodeNameAvailabilityResponse, PhotosAttributesDto,
|
NodeNameAvailabilityRequest, NodeNameAvailabilityResponse, PhotosAttributesDto,
|
||||||
RenameLinkRequest, RevisionCreationRequest, RevisionCreationResponse, RevisionDto,
|
RenameLinkRequest, RevisionCreationRequest, RevisionCreationResponse, RevisionDto,
|
||||||
RevisionResponse, RevisionUpdateRequest, ShareInvitationDto, ShareInvitationsResponse,
|
RevisionResponse, RevisionUpdateRequest, ShareInvitationDto, ShareInvitationsResponse,
|
||||||
ShareMembersResponse, ShareResponse, ShareTargetType, ShareUrlsResponse, SharedWithMeResponse,
|
ShareMembersResponse, ShareResponse, ShareTargetType, ShareUrlDto, ShareUrlsResponse,
|
||||||
ThumbnailBlockListRequest, ThumbnailBlockListResponse, ThumbnailCreationRequest, ThumbnailDto,
|
SharedWithMeResponse, ThumbnailBlockListRequest, ThumbnailBlockListResponse,
|
||||||
TimelinePhotoListResponse, UpdatePermissionsRequest, VolumeCreationRequest, VolumeEventDto,
|
ThumbnailCreationRequest, ThumbnailDto, TimelinePhotoListResponse, UpdatePermissionsRequest,
|
||||||
VolumeEventListResponse, VolumeTrashResponse,
|
VolumeCreationRequest, VolumeEventDto, VolumeEventListResponse, VolumeTrashResponse,
|
||||||
};
|
};
|
||||||
use crate::events::{DriveEvent, DriveEventScopeId};
|
use crate::events::{DriveEvent, DriveEventScopeId};
|
||||||
use crate::node::{FileThumbnail, Node, NodeKind, RevisionState, Thumbnail, ThumbnailType};
|
use crate::node::{FileThumbnail, Node, NodeKind, RevisionState, Thumbnail, ThumbnailType};
|
||||||
@@ -1889,27 +1889,31 @@ impl ProtonDriveClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The public link on a node, if one exists (metadata only — the secret URL
|
/// The public link on a node, if one exists. The secret URL fragment is
|
||||||
/// fragment is not reconstructed).
|
/// recovered by decrypting the stored link password with the owner's address
|
||||||
|
/// key and taking the generated (non-custom) portion — the same recovery the
|
||||||
|
/// web client does when re-displaying an existing link.
|
||||||
pub async fn get_public_link(&self, uid: &NodeUid) -> Result<Option<PublicLink>> {
|
pub async fn get_public_link(&self, uid: &NodeUid) -> Result<Option<PublicLink>> {
|
||||||
let mut timer = self.telemetry.start("get_public_link");
|
let mut timer = self.telemetry.start("get_public_link");
|
||||||
let link = match self.node_share_id(uid).await? {
|
let link = match self.node_share_id(uid).await? {
|
||||||
Some(share_id) => {
|
Some(share_id) => {
|
||||||
let path = format!("shares/{share_id}/urls");
|
let path = format!("shares/{share_id}/urls");
|
||||||
let response: ShareUrlsResponse = self.http.get(&path).await?;
|
let response: ShareUrlsResponse = self.http.get(&path).await?;
|
||||||
response
|
match response.share_urls.into_iter().next() {
|
||||||
.share_urls
|
Some(dto) => {
|
||||||
.into_iter()
|
let url = self.recover_public_link_url(&dto).await;
|
||||||
.next()
|
Some(PublicLink {
|
||||||
.map(|dto| PublicLink {
|
share_id,
|
||||||
share_id,
|
public_link_id: dto.share_url_id,
|
||||||
public_link_id: dto.share_url_id,
|
url,
|
||||||
url: None,
|
role: MemberRole::from_permissions(dto.permissions),
|
||||||
role: MemberRole::from_permissions(dto.permissions),
|
creation_time: dto.create_time,
|
||||||
creation_time: dto.create_time,
|
expiration_time: dto.expiration_time,
|
||||||
expiration_time: dto.expiration_time,
|
has_custom_password: dto.flags == 3,
|
||||||
has_custom_password: dto.flags == 3,
|
})
|
||||||
})
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
@@ -1917,6 +1921,26 @@ impl ProtonDriveClient {
|
|||||||
Ok(link)
|
Ok(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rebuild the full public URL (`{public_url}#{generated}`) for an existing
|
||||||
|
/// link by decrypting its stored `Password` with the owner's address key. The
|
||||||
|
/// stored secret is the generated password with any custom password appended;
|
||||||
|
/// only the leading generated portion belongs in the URL fragment (the
|
||||||
|
/// recipient supplies the custom password separately). Returns `None` if the
|
||||||
|
/// link carries no password or it can't be decrypted, so the caller still gets
|
||||||
|
/// the link's metadata.
|
||||||
|
async fn recover_public_link_url(&self, dto: &ShareUrlDto) -> Option<String> {
|
||||||
|
if dto.public_url.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let enc = dto.password.as_deref()?;
|
||||||
|
let (_, email, _) = self.membership_address().await.ok()?;
|
||||||
|
let (address_keys, _) = self.own_address_keys(&email).await.ok()?;
|
||||||
|
let bytes = decrypt_armored_with_keys(enc, &address_keys).ok()?;
|
||||||
|
let full = String::from_utf8_lossy(&bytes);
|
||||||
|
let generated: String = full.chars().take(PUBLIC_LINK_PASSWORD_LEN).collect();
|
||||||
|
Some(format!("{}#{generated}", dto.public_url))
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a public link, revoking access via the URL
|
/// Remove a public link, revoking access via the URL
|
||||||
/// (`DELETE shares/{sid}/urls/{urlID}`).
|
/// (`DELETE shares/{sid}/urls/{urlID}`).
|
||||||
pub async fn remove_public_link(&self, link: &PublicLink) -> Result<()> {
|
pub async fn remove_public_link(&self, link: &PublicLink) -> Result<()> {
|
||||||
|
|||||||
@@ -1522,6 +1522,11 @@ pub struct ShareUrlDto {
|
|||||||
pub share_id: Option<ShareId>,
|
pub share_id: Option<ShareId>,
|
||||||
#[serde(rename = "PublicUrl", default)]
|
#[serde(rename = "PublicUrl", default)]
|
||||||
pub public_url: String,
|
pub public_url: String,
|
||||||
|
/// The link password (generated portion + any custom password), PGP-encrypted
|
||||||
|
/// to the share creator's address key. Decrypting it recovers the secret URL
|
||||||
|
/// fragment. Absent on the create response, present when listing.
|
||||||
|
#[serde(rename = "Password", default)]
|
||||||
|
pub password: Option<String>,
|
||||||
#[serde(rename = "CreateTime", default)]
|
#[serde(rename = "CreateTime", default)]
|
||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
#[serde(rename = "ExpirationTime", default)]
|
#[serde(rename = "ExpirationTime", default)]
|
||||||
|
|||||||
Reference in new issue
Block a user