Create file/API URL builder

This commit is contained in:
timvisee 2018-04-08 18:01:32 +02:00
parent b47d2941d0
commit 6e913428c2
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
12 changed files with 101 additions and 99 deletions

View file

@ -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)

View file

@ -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()

View file

@ -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)
))

View file

@ -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)?;

View file

@ -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()

View file

@ -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)
))

View file

@ -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()

View file

@ -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()

View file

@ -1,2 +1,3 @@
pub mod data;
pub mod nonce;
pub mod url;

72
api/src/api/url.rs Normal file
View 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)
}
}

View 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

View file

@ -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)]