Skip to content

Commit

Permalink
Merge pull request #9 from unbekanntes-pferd/feature/rescue-key
Browse files Browse the repository at this point in the history
add system rescue key handling
  • Loading branch information
unbekanntes-pferd committed Aug 27, 2023
2 parents 36631bf + 088eed2 commit bc85eb4
Show file tree
Hide file tree
Showing 13 changed files with 660 additions and 104 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ reqwest-middleware = "0.2.2"
reqwest-retry = "0.2.2"

# crypto
dco3_crypto = "0.5.0"
dco3_crypto = "0.5.1"

# async runtime and utils
tokio = { version = "1.29.1", features = ["full"] }
Expand Down
24 changes: 7 additions & 17 deletions src/auth/errors.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use async_trait::async_trait;
use dco3_crypto::DracoonCryptoError;
use reqwest_middleware::{Error as ReqError};
use reqwest::{Error as ClientError, Response};
use reqwest_middleware::Error as ReqError;
use thiserror::Error;

use crate::{nodes::models::S3ErrorResponse, utils::FromResponse};

use super::models::{DracoonAuthErrorResponse, DracoonErrorResponse};

#[derive(Debug, Error)]
#[derive(Debug, Error, PartialEq)]
pub enum DracoonClientError {
#[error("Client id required")]
MissingClientId,
Expand Down Expand Up @@ -44,33 +44,25 @@ pub enum DracoonClientError {

impl From<ReqError> for DracoonClientError {
fn from(value: ReqError) -> Self {

match value {
ReqError::Middleware(error) => {
DracoonClientError::ConnectionFailed

},
ReqError::Middleware(error) => DracoonClientError::ConnectionFailed,
ReqError::Reqwest(error) => {
if error.is_timeout() {
return DracoonClientError::ConnectionFailed
return DracoonClientError::ConnectionFailed;
}

if error.is_connect() {
return DracoonClientError::ConnectionFailed
return DracoonClientError::ConnectionFailed;
}


DracoonClientError::Unknown

},
}
}
}
}


impl From<ClientError> for DracoonClientError {
fn from(value: ClientError) -> Self {

if value.is_timeout() {
return DracoonClientError::ConnectionFailed;
}
Expand All @@ -83,8 +75,6 @@ impl From<ClientError> for DracoonClientError {
}
}



#[async_trait]
impl FromResponse for DracoonClientError {
async fn from_response(value: Response) -> Result<Self, DracoonClientError> {
Expand Down Expand Up @@ -145,7 +135,7 @@ impl DracoonClientError {
_ => false,
}
}

/// Check if the error is an 409 Conflict error
pub fn is_conflict(&self) -> bool {
match self {
Expand Down
4 changes: 4 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ pub const GROUPS_LAST_ADMIN_ROOMS: &str = "last_admin_rooms";
pub const USERS_BASE: &str = "users";
pub const USERS_LAST_ADMIN_ROOMS: &str = "last_admin_rooms";

// SETTINGS
pub const SETTINGS_BASE: &str = "settings";
pub const SETTINGS_KEYPAIR: &str = "keypair";

/// user agent header
pub const APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "|", env!("CARGO_PKG_VERSION"));

Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! Currently, the following traits are implemented:
//!
//! * [User] - for user account management
//! * [UserAccountKeypairs] - for user keypair management
//! * [UserAccountKeyPairs] - for user keypair management
//! * [Nodes] - for node operations (folders, rooms, upload and download are excluded)
//! * [Download] - for downloading files
//! * [Upload] - for uploading files
Expand All @@ -26,6 +26,7 @@
//! * [UploadShares] - for upload share operations
//! * [Groups] - for group operations
//! * [Users] - for user management operations
//! * [RescueKeyPair] - for distributing missing keys using the rescue key
//!
//!
//! ### Example
Expand Down Expand Up @@ -341,12 +342,13 @@ use self::{
// re-export traits and base models
pub use self::{
nodes::{Download, Folders, Nodes, Rooms, Upload},
user::{User, UserAccountKeypairs},
user::{User, UserAccountKeyPairs},
auth::errors::DracoonClientError,
auth::OAuth2Flow,
groups::Groups,
shares::{DownloadShares, UploadShares},
users::Users,
settings::RescueKeyPair,
models::*,
};

Expand All @@ -360,6 +362,7 @@ pub mod utils;
pub mod groups;
pub mod shares;
pub mod users;
pub mod settings;
mod tests;


Expand Down
54 changes: 26 additions & 28 deletions src/nodes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
//! This module implements a subset of the nodes DRACOON API.
//! Documentation can be found here: <https://download.dracoon.com/api/swagger-ui/index.html?configUrl=/api/spec_v4/swagger-config#/nodes>
pub use self::{
models::*,
rooms::models::*,
};
pub use self::{models::*, rooms::models::*};
use super::{auth::errors::DracoonClientError, models::ListAllParams};
use async_trait::async_trait;

use std::io::Write;
use tokio::io::{AsyncRead, BufReader};

Expand All @@ -16,11 +14,10 @@ pub mod nodes;
pub mod rooms;
pub mod upload;


/// This trait provides methods to manage nodes.
/// Specifically, there's a method to obtain a node for a given path and
/// Specifically, there's a method to obtain a node for a given path and
/// all relevant methods to list nodes (get, search), move, copy and deleted nodes.
///
///
/// To download a node, use the [Download] trait.
/// To upload a node, use the [Upload] trait.
/// To manage rooms, use the [Rooms] trait.
Expand All @@ -42,20 +39,20 @@ pub trait Nodes {
/// # .await
/// # .unwrap();
/// let nodes = dracoon.get_nodes(None, None, None).await.unwrap();
///
///
/// // get all nodes for a parent
/// let nodes = dracoon.get_nodes(Some(123), None, None).await.unwrap();
///
///
/// // get all nodes visible as room manager / admin
/// let nodes = dracoon.get_nodes(None, Some(true), None).await.unwrap();
///
/// // use filtering and sorting
///
/// // use filtering and sorting
/// let params = ListAllParams::builder()
/// .with_filter(NodesFilter::is_file())
/// .with_filter(NodesFilter::name_contains("foo"))
/// .with_sort(NodesSortBy::name(SortOrder::Desc))
/// .build();
///
///
/// let nodes = dracoon.get_nodes(None, None, Some(params)).await.unwrap();
/// # }
/// ```
Expand Down Expand Up @@ -103,13 +100,13 @@ pub trait Nodes {
/// # .unwrap();
/// // search for nodes ("*" is wildcard)
/// let nodes = dracoon.search_nodes("foo", None, None, None).await.unwrap();
///
///
/// // search for nodes in a parent
/// let nodes = dracoon.search_nodes("foo", Some(123), None, None).await.unwrap();
///
///
/// // search for nodes in a parent with a depth level (-1 is full tree)
/// let nodes = dracoon.search_nodes("foo", Some(123), Some(1), None).await.unwrap();
///
///
/// // use filtering and sorting
/// let params = ListAllParams::builder()
/// .with_filter(NodesSearchFilter::is_file())
Expand Down Expand Up @@ -230,6 +227,7 @@ pub trait Nodes {
target_parent_id: u64,
) -> Result<Node, DracoonClientError>;
}

#[async_trait]
pub trait Folders {
/// Creates a folder in the provided parent room.
Expand Down Expand Up @@ -282,7 +280,7 @@ pub trait Folders {
) -> Result<Node, DracoonClientError>;
}
/// This trait provides methods to manage rooms.
///
///
/// - Create a room
/// - Update a room
/// - Configure a room
Expand Down Expand Up @@ -437,7 +435,7 @@ pub trait Rooms {
/// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into()))
/// # .await
/// # .unwrap();
///
///
/// // add a a list of updates
/// let group_updates = vec![RoomGroupsAddBatchRequestItem::new(123, NodePermissions::new_with_read_permissions(), None)];
/// dracoon.update_room_groups(123, group_updates.into()).await.unwrap();
Expand All @@ -464,7 +462,7 @@ pub trait Rooms {
/// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into()))
/// # .await
/// # .unwrap();
/// // You can use a vec
/// // You can use a vec
/// let group_ids = vec![1, 2, 3];
/// dracoon.delete_room_groups(123, group_ids.into()).await.unwrap();
/// # }
Expand Down Expand Up @@ -515,7 +513,7 @@ pub trait Rooms {
/// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into()))
/// # .await
/// # .unwrap();
///
///
/// // add a a list of updates
/// let user_updates = vec![RoomUsersAddBatchRequestItem::new(123, NodePermissions::new_with_read_permissions())];
/// dracoon.update_room_users(123, user_updates.into()).await.unwrap();
Expand All @@ -541,7 +539,7 @@ pub trait Rooms {
/// # .connect(OAuth2Flow::PasswordFlow("username".into(), "password".into()))
/// # .await
/// # .unwrap();
/// // You can use a vec
/// // You can use a vec
/// let user_ids = vec![1, 2, 3];
/// dracoon.delete_room_users(123, user_ids.into()).await.unwrap();
/// # }
Expand Down Expand Up @@ -576,7 +574,7 @@ pub trait Download {
/// .connect(OAuth2Flow::password_flow("username", "password"))
/// .await
/// .unwrap();
///
///
/// let node_id = 123u64;
///
/// let node = client.get_node(node_id).await.unwrap();
Expand Down Expand Up @@ -626,7 +624,7 @@ pub trait Upload<R: AsyncRead> {
/// .connect(OAuth2Flow::password_flow("username", "password"))
/// .await
/// .unwrap();
///
///
/// let file = tokio::fs::File::open("test.txt").await.unwrap();
/// let file_meta = FileMeta::builder()
/// .with_name("test.txt".into())
Expand All @@ -636,19 +634,19 @@ pub trait Upload<R: AsyncRead> {
///
///
/// let parent_node_id = 123u64;
///
///
/// let parent_node = client.get_node(parent_node_id).await.unwrap();
///
///
/// let reader = tokio::io::BufReader::new(file);
///
///
/// let options = UploadOptions::builder()
/// .with_resolution_strategy(ResolutionStrategy::AutoRename)
/// .build();
///
///
/// let chunk_size = 1024 * 1024 * 10; // 10 MB - DEFAULT is 32 MB
///
///
/// client.upload(file_meta, &parent_node, options, reader, None, Some(chunk_size)).await.unwrap();
///
///
/// // or with progress callback (boxed closure)
/// let file = tokio::fs::File::open("test.txt").await.unwrap();
/// let file_meta = FileMeta::builder()
Expand Down
22 changes: 22 additions & 0 deletions src/nodes/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,3 +1087,25 @@ impl UserFileKeySetRequest {
}
}
}


#[derive(Debug, Clone)]
pub enum UseKey {
RoomRescueKey,
SystemRescueKey,
PreviousUserKey,
PreviousRoomRescueKey,
PreviousSystemRescueKey,
}

impl From<UseKey> for String {
fn from(use_key: UseKey) -> Self {
match use_key {
UseKey::RoomRescueKey => "room_rescue_key".to_string(),
UseKey::SystemRescueKey => "system_rescue_key".to_string(),
UseKey::PreviousUserKey => "previous_user_key".to_string(),
UseKey::PreviousRoomRescueKey => "previous_room_rescue_key".to_string(),
UseKey::PreviousSystemRescueKey => "previous_system_rescue_key".to_string(),
}
}
}
21 changes: 9 additions & 12 deletions src/nodes/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Nodes for Dracoon<Connected> {
.extend_pairs(room_manager.map(|v| ("room_manager", v.to_string())))
.extend_pairs(parent_id.map(|v| ("parent_id", v.to_string())))
.finish();

let response = self
.client
.http
Expand All @@ -59,17 +59,11 @@ impl Nodes for Dracoon<Connected> {
// TODO: refactor and make use of search_nodes
let url_part = format!("/{DRACOON_API_PREFIX}/{NODES_BASE}/{NODES_SEARCH}");

debug!("Looking up node - path: {}", path);

let (parent_path, name, depth) = parse_node_path(path).map_err(|_| {
error!("Failed to parse path: {}", path);
DracoonClientError::InvalidPath(path.to_string())
})?;

debug!("Looking up node - parent_path: {}", parent_path);
debug!("Parsed name: {}", name);
debug!("Calculated depth: {}", depth);

let mut api_url = self.build_api_url(&url_part);

api_url
Expand Down Expand Up @@ -252,16 +246,19 @@ pub fn parse_node_path(path: &str) -> Result<ParsedPath, DracoonClientError> {
if path == "/" {
return Ok((String::from("/"), String::new(), 0));
}

let path_parts: Vec<&str> = path.trim_end_matches('/').split('/').collect();
let name = String::from(*path_parts.last().ok_or(DracoonClientError::InvalidPath(path.to_string()))?);
let name = String::from(
*path_parts
.last()
.ok_or(DracoonClientError::InvalidPath(path.to_string()))?,
);
let parent_path = format!("{}/", path_parts[..path_parts.len() - 1].join("/"));
let depth = path_parts.len().saturating_sub(2) as u64;

Ok((parent_path, name, depth))
}


#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -319,4 +316,4 @@ mod tests {
assert_eq!("", name);
assert_eq!(0, depth);
}
}
}
Loading

0 comments on commit bc85eb4

Please sign in to comment.