Create file/API URL builder
This commit is contained in:
parent
b47d2941d0
commit
6e913428c2
12 changed files with 101 additions and 99 deletions
1
IDEAS.md
1
IDEAS.md
|
@ -39,3 +39,4 @@
|
|||
- Ask to add MIME extension to downloaded files without one on Windows
|
||||
- Fetch max file size from `server/jsconfig.js`
|
||||
- Define a redirect policy (allow setting max redirects)
|
||||
- Support servers that are hosted on a sub path (URL builder resets path)
|
||||
|
|
|
@ -5,6 +5,7 @@ use api::data::{
|
|||
OwnedData,
|
||||
};
|
||||
use api::nonce::{NonceError, request_nonce};
|
||||
use api::url::UrlBuilder;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
|
||||
|
@ -48,7 +49,7 @@ impl<'a> Delete<'a> {
|
|||
{
|
||||
request_nonce(
|
||||
client,
|
||||
self.file.download_url(false),
|
||||
UrlBuilder::download(self.file, false),
|
||||
).map_err(|err| PrepareError::Auth(err))
|
||||
}
|
||||
|
||||
|
@ -59,7 +60,7 @@ impl<'a> Delete<'a> {
|
|||
data: OwnedData<DeleteData>,
|
||||
) -> Result<(), DeleteError> {
|
||||
// Get the delete URL, and send the request
|
||||
let url = self.file.api_delete_url();
|
||||
let url = UrlBuilder::api_delete(self.file);
|
||||
let response = client.post(url)
|
||||
.json(&data)
|
||||
.send()
|
||||
|
|
|
@ -11,6 +11,7 @@ use reqwest::{Client, Response, StatusCode};
|
|||
use reqwest::header::Authorization;
|
||||
use reqwest::header::ContentLength;
|
||||
|
||||
use api::url::UrlBuilder;
|
||||
use crypto::key_set::KeySet;
|
||||
use crypto::sig::signature_encoded;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
|
@ -172,7 +173,7 @@ impl<'a> Download<'a> {
|
|||
.map_err(|_| DownloadError::ComputeSignature)?;
|
||||
|
||||
// Build and send the download request
|
||||
let response = client.get(self.file.api_download_url())
|
||||
let response = client.get(UrlBuilder::api_download(self.file))
|
||||
.header(Authorization(
|
||||
format!("send-v1 {}", sig)
|
||||
))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use reqwest::{Client, StatusCode};
|
||||
|
||||
use api::url::UrlBuilder;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
|
||||
|
@ -30,7 +31,7 @@ impl<'a> Exists<'a> {
|
|||
/// Send a request to check whether the file exists
|
||||
fn check_exists(&self, client: &Client) -> Result<ExistsResponse, Error> {
|
||||
// Get the download url, and parse the nonce
|
||||
let exists_url = self.file.api_exists_url();
|
||||
let exists_url = UrlBuilder::api_exists(self.file);
|
||||
let mut response = client.get(exists_url)
|
||||
.send()
|
||||
.map_err(|_| Error::Request)?;
|
||||
|
|
|
@ -11,6 +11,7 @@ use api::data::{
|
|||
OwnedData,
|
||||
};
|
||||
use api::nonce::{NonceError, request_nonce};
|
||||
use api::url::UrlBuilder;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
|
||||
|
@ -54,7 +55,7 @@ impl<'a> Info<'a> {
|
|||
{
|
||||
request_nonce(
|
||||
client,
|
||||
self.file.download_url(false),
|
||||
UrlBuilder::download(self.file, false),
|
||||
).map_err(|err| PrepareError::Auth(err))
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ impl<'a> Info<'a> {
|
|||
data: OwnedData<InfoData>,
|
||||
) -> Result<InfoResponse, InfoError> {
|
||||
// Get the info URL, and send the request
|
||||
let url = self.file.api_info_url();
|
||||
let url = UrlBuilder::api_info(self.file);
|
||||
let mut response = client.post(url)
|
||||
.json(&data)
|
||||
.send()
|
||||
|
|
|
@ -5,6 +5,7 @@ use reqwest::header::Authorization;
|
|||
use serde_json;
|
||||
|
||||
use api::nonce::{header_nonce, NonceError, request_nonce};
|
||||
use api::url::UrlBuilder;
|
||||
use crypto::b64;
|
||||
use crypto::key_set::KeySet;
|
||||
use crypto::sig::signature_encoded;
|
||||
|
@ -52,7 +53,7 @@ impl<'a> Metadata<'a> {
|
|||
{
|
||||
request_nonce(
|
||||
client,
|
||||
self.file.download_url(false),
|
||||
UrlBuilder::download(self.file, false),
|
||||
).map_err(|err| RequestError::Auth(err))
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,7 @@ impl<'a> Metadata<'a> {
|
|||
.map_err(|_| MetaError::ComputeSignature)?;
|
||||
|
||||
// Build the request, fetch the encrypted metadata
|
||||
let mut response = client.get(self.file.api_meta_url())
|
||||
let mut response = client.get(UrlBuilder::api_metadata(self.file))
|
||||
.header(Authorization(
|
||||
format!("send-v1 {}", sig)
|
||||
))
|
||||
|
|
|
@ -5,6 +5,7 @@ use api::data::{
|
|||
OwnedData,
|
||||
};
|
||||
use api::nonce::{NonceError, request_nonce};
|
||||
use api::url::UrlBuilder;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
|
||||
|
@ -70,7 +71,7 @@ impl<'a> Params<'a> {
|
|||
{
|
||||
request_nonce(
|
||||
client,
|
||||
self.file.download_url(false),
|
||||
UrlBuilder::download(self.file, false),
|
||||
).map_err(|err| PrepareError::Auth(err))
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ impl<'a> Params<'a> {
|
|||
data: OwnedData<ParamsData>,
|
||||
) -> Result<(), ChangeError> {
|
||||
// Get the params URL, and send the change
|
||||
let url = self.file.api_params_url();
|
||||
let url = UrlBuilder::api_params(self.file);
|
||||
let response = client.post(url)
|
||||
.json(&data)
|
||||
.send()
|
||||
|
|
|
@ -5,6 +5,7 @@ use api::data::{
|
|||
OwnedData,
|
||||
};
|
||||
use api::nonce::{NonceError, request_nonce};
|
||||
use api::url::UrlBuilder;
|
||||
use crypto::key_set::KeySet;
|
||||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
|
@ -47,7 +48,7 @@ impl<'a> Password<'a> {
|
|||
}
|
||||
|
||||
// Derive a new authentication key
|
||||
key.derive_auth_password(self.password, &self.file.download_url(true));
|
||||
key.derive_auth_password(self.password, &UrlBuilder::download(self.file, true));
|
||||
|
||||
// Build the password data, wrap it as owned
|
||||
let data = OwnedData::from(PasswordData::from(&key), &self.file)
|
||||
|
@ -64,7 +65,7 @@ impl<'a> Password<'a> {
|
|||
{
|
||||
request_nonce(
|
||||
client,
|
||||
self.file.download_url(false),
|
||||
UrlBuilder::download(self.file, false),
|
||||
).map_err(|err| PrepareError::Auth(err))
|
||||
}
|
||||
|
||||
|
@ -75,7 +76,7 @@ impl<'a> Password<'a> {
|
|||
data: OwnedData<PasswordData>,
|
||||
) -> Result<(), ChangeError> {
|
||||
// Get the password URL, and send the change
|
||||
let url = self.file.api_password_url();
|
||||
let url = UrlBuilder::api_password(self.file);
|
||||
let response = client.post(url)
|
||||
.json(&data)
|
||||
.send()
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod data;
|
||||
pub mod nonce;
|
||||
pub mod url;
|
||||
|
|
72
api/src/api/url.rs
Normal file
72
api/src/api/url.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use url::Url;
|
||||
|
||||
use file::remote_file::RemoteFile;
|
||||
|
||||
/// A struct, that helps building URLs for communicating with a remote host.
|
||||
pub struct UrlBuilder;
|
||||
|
||||
impl UrlBuilder {
|
||||
/// Get the download URL of the given file.
|
||||
/// This URL is identical to the share URL, a term used in this API.
|
||||
/// Set `secret` to `true`, to include it in the URL if known.
|
||||
pub fn download(file: &RemoteFile, secret: bool) -> Url {
|
||||
// Get the share URL, and update the secret fragment
|
||||
let mut url = file.url().clone();
|
||||
if secret && file.has_secret() {
|
||||
url.set_fragment(Some(&file.secret()));
|
||||
} else {
|
||||
url.set_fragment(None);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Generate an API file URL, with the given endpoint.
|
||||
/// The endpoint should not contain any slashes.
|
||||
///
|
||||
/// Valid endpoints may be 'metadata', 'download' or for example
|
||||
/// 'password'.
|
||||
fn api(endpoint: &str, file: &RemoteFile) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = file.url().clone();
|
||||
url.set_path(format!("/api/{}/{}", endpoint, file.id()).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API metadata URL for the given file.
|
||||
pub fn api_metadata(file: &RemoteFile) -> Url {
|
||||
Self::api("metadata", file)
|
||||
}
|
||||
|
||||
/// Get the API download URL for the given file.
|
||||
pub fn api_download(file: &RemoteFile) -> Url {
|
||||
Self::api("download", file)
|
||||
}
|
||||
|
||||
/// Get the API password URL for the given file.
|
||||
pub fn api_password(file: &RemoteFile) -> Url {
|
||||
Self::api("password", file)
|
||||
}
|
||||
|
||||
/// Get the API params URL for the given file.
|
||||
pub fn api_params(file: &RemoteFile) -> Url {
|
||||
Self::api("params", file)
|
||||
}
|
||||
|
||||
/// Get the API info URL for the given file.
|
||||
pub fn api_info(file: &RemoteFile) -> Url {
|
||||
Self::api("info", file)
|
||||
}
|
||||
|
||||
/// Get the API exists URL for the given file.
|
||||
pub fn api_exists(file: &RemoteFile) -> Url {
|
||||
Self::api("exists", file)
|
||||
}
|
||||
|
||||
/// Get the API delete URL for the given file.
|
||||
pub fn api_delete(file: &RemoteFile) -> Url {
|
||||
Self::api("delete", file)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use openssl::symm::Cipher;
|
||||
use url::Url;
|
||||
|
||||
use api::url::UrlBuilder;
|
||||
use file::remote_file::RemoteFile;
|
||||
use super::{b64, rand_bytes};
|
||||
use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key};
|
||||
|
@ -54,7 +55,7 @@ impl KeySet {
|
|||
|
||||
// Derive a pasworded key
|
||||
if let Some(password) = password {
|
||||
set.derive_auth_password(password, &file.download_url(true));
|
||||
set.derive_auth_password(password, &UrlBuilder::download(&file, true));
|
||||
}
|
||||
|
||||
set
|
||||
|
|
|
@ -138,6 +138,11 @@ impl RemoteFile {
|
|||
&self.id
|
||||
}
|
||||
|
||||
/// Get the file URL, provided by the server.
|
||||
pub fn url(&self) -> &Url {
|
||||
&self.url
|
||||
}
|
||||
|
||||
/// Get the raw secret.
|
||||
pub fn secret_raw(&self) -> &Vec<u8> {
|
||||
// A secret must have been set
|
||||
|
@ -169,91 +174,6 @@ impl RemoteFile {
|
|||
pub fn set_owner_token(&mut self, token: Option<String>) {
|
||||
self.owner_token = token;
|
||||
}
|
||||
|
||||
/// Get the download URL of the file
|
||||
/// This URL is identical to the share URL, a term used in this API.
|
||||
/// Set `secret` to `true`, to include it in the URL if known.
|
||||
pub fn download_url(&self, secret: bool) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
if secret && self.has_secret() {
|
||||
url.set_fragment(Some(&self.secret()));
|
||||
} else {
|
||||
url.set_fragment(None);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API metadata URL of the file.
|
||||
pub fn api_meta_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/metadata/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API download URL of the file.
|
||||
pub fn api_download_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/download/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API password URL of the file.
|
||||
pub fn api_password_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/password/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API params URL of the file.
|
||||
pub fn api_params_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/params/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API info URL of the file.
|
||||
pub fn api_info_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/info/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API exists URL of the file.
|
||||
pub fn api_exists_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/exists/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
/// Get the API delete URL of the file.
|
||||
pub fn api_delete_url(&self) -> Url {
|
||||
// Get the share URL, and add the secret fragment
|
||||
let mut url = self.url.clone();
|
||||
url.set_path(format!("/api/delete/{}", self.id).as_str());
|
||||
url.set_fragment(None);
|
||||
|
||||
url
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
|
|
Loading…
Reference in a new issue