Extract nonce parsing logic to a module

This commit is contained in:
timvisee 2018-04-06 14:30:02 +02:00
parent 7ced1f4278
commit 718238b35e
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
11 changed files with 142 additions and 397 deletions

View file

@ -1,18 +1,13 @@
// TODO: define redirect policy
use reqwest::{Client, StatusCode};
use api::data::{
Error as DataError,
OwnedData,
};
use crypto::b64;
use api::nonce::{NonceError, request_auth_nonce};
use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// An action to delete a remote file.
pub struct Delete<'a> {
/// The remote file to delete.
@ -48,40 +43,13 @@ impl<'a> Delete<'a> {
}
/// Fetch the authentication nonce for the file from the remote server.
fn fetch_auth_nonce(&self, client: &Client) -> Result<Vec<u8>, AuthError> {
// Get the download URL, and parse the nonce
let download_url = self.file.download_url(false);
let response = client.get(download_url)
.send()
.map_err(|_| AuthError::NonceReq)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// TODO: should we check here whether a 404 is returned?
// // Handle expired files
// if status == FILE_EXPIRED_STATUS {
// return Err(Error::Expired);
// } else {
return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
// }
}
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthError::NoNonceHeader)?
.one()
.ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthError::MalformedNonce.into())
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, PrepareError>
{
request_auth_nonce(
client,
self.file.download_url(false),
).map_err(|err| PrepareError::Auth(err))
}
/// Send a request to delete the remote file, with the given data.
@ -142,12 +110,6 @@ impl From<PrepareError> for Error {
}
}
impl From<AuthError> for Error {
fn from(err: AuthError) -> Error {
PrepareError::Auth(err).into()
}
}
impl From<DeleteError> for Error {
fn from(err: DeleteError) -> Error {
Error::Delete(err)
@ -167,7 +129,7 @@ pub enum DeleteDataError {
pub enum PrepareError {
/// Failed to authenticate
#[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthError),
Auth(#[cause] NonceError),
/// An error occurred while building the deletion data that will be
/// send to the server.
@ -181,31 +143,6 @@ impl From<DataError> for PrepareError {
}
}
#[derive(Fail, Debug)]
pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce
/// failed.
#[fail(display = "Failed to request authentication nonce")]
NonceReq,
/// The response for fetching the authentication encryption nonce
/// indicated an error and wasn't successful.
#[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No authentication encryption nonce was included in the response
/// from the server, it was missing.
#[fail(display = "Missing authentication nonce in server response")]
NoNonceHeader,
/// The authentication encryption nonce from the response malformed or
/// empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed authentication nonce")]
MalformedNonce,
}
#[derive(Fail, Debug)]
pub enum DeleteError {
/// Sending the file deletion request failed.

View file

@ -1,5 +1,3 @@
// TODO: define redirect policy
use std::fs::File;
use std::io::{
self,

View file

@ -1,5 +1,3 @@
// TODO: define redirect policy
use reqwest::{Client, StatusCode};
use ext::status_code::StatusCodeExt;

View file

@ -1,5 +1,3 @@
// TODO: define redirect policy
use std::cmp::max;
use reqwest::{
@ -12,13 +10,10 @@ use api::data::{
Error as DataError,
OwnedData,
};
use crypto::b64;
use api::nonce::{NonceError, request_auth_nonce};
use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// An action to fetch info of a shared file.
pub struct Info<'a> {
/// The remote file to fetch the info for.
@ -54,40 +49,13 @@ impl<'a> Info<'a> {
}
/// Fetch the authentication nonce for the file from the remote server.
fn fetch_auth_nonce(&self, client: &Client) -> Result<Vec<u8>, AuthError> {
// Get the download URL, and parse the nonce
let download_url = self.file.download_url(false);
let response = client.get(download_url)
.send()
.map_err(|_| AuthError::NonceReq)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// TODO: should we check here whether a 404 is returned?
// // Handle expired files
// if status == FILE_EXPIRED_STATUS {
// return Err(Error::Expired);
// } else {
return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
// }
}
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthError::NoNonceHeader)?
.one()
.ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthError::MalformedNonce.into())
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, PrepareError>
{
request_auth_nonce(
client,
self.file.download_url(false),
).map_err(|err| PrepareError::Auth(err))
}
/// Send the request for fetching the remote file info.
@ -193,12 +161,6 @@ impl From<PrepareError> for Error {
}
}
impl From<AuthError> for Error {
fn from(err: AuthError) -> Error {
PrepareError::Auth(err).into()
}
}
impl From<InfoError> for Error {
fn from(err: InfoError) -> Error {
Error::Info(err)
@ -218,7 +180,7 @@ pub enum InfoDataError {
pub enum PrepareError {
/// Failed authenticating, needed to fetch the info
#[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthError),
Auth(#[cause] NonceError),
/// An error occurred while building the info data that will be
/// send to the server.
@ -232,31 +194,6 @@ impl From<DataError> for PrepareError {
}
}
#[derive(Fail, Debug)]
pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce
/// failed.
#[fail(display = "Failed to request authentication nonce")]
NonceReq,
/// The response for fetching the authentication encryption nonce
/// indicated an error and wasn't successful.
#[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No authentication encryption nonce was included in the response
/// from the server, it was missing.
#[fail(display = "Missing authentication nonce in server response")]
NoNonceHeader,
/// The authentication encryption nonce from the response malformed or
/// empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed authentication nonce")]
MalformedNonce,
}
#[derive(Fail, Debug)]
pub enum InfoError {
/// Sending the request to fetch the file info failed.

View file

@ -1,11 +1,15 @@
// TODO: define redirect policy
use failure::Error as FailureError;
use openssl::symm::decrypt_aead;
use reqwest::{Client, StatusCode};
use reqwest::header::Authorization;
use serde_json;
use api::nonce::{
HEADER_NONCE,
header_nonce,
NonceError,
request_auth_nonce,
};
use crypto::b64;
use crypto::key_set::KeySet;
use crypto::sig::signature_encoded;
@ -13,9 +17,6 @@ use ext::status_code::StatusCodeExt;
use file::metadata::Metadata as MetadataData;
use file::remote_file::RemoteFile;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// The HTTP status code that is returned for expired files.
const FILE_EXPIRED_STATUS: StatusCode = StatusCode::NotFound;
@ -50,42 +51,14 @@ impl<'a> Metadata<'a> {
.map_err(|err| err.into())
}
/// Fetch the authentication nonce for the file from the Send server.
/// Fetch the authentication nonce for the file from the remote server.
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, Error>
-> Result<Vec<u8>, RequestError>
{
// Get the download url, and parse the nonce
let download_url = self.file.download_url(false);
let response = client.get(download_url)
.send()
.map_err(|_| AuthError::NonceReq)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// Handle expired files
if status == FILE_EXPIRED_STATUS {
return Err(Error::Expired);
} else {
return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
}
}
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthError::NoNonceHeader)?
.one()
.ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthError::MalformedNonce.into())
request_auth_nonce(
client,
self.file.download_url(false),
).map_err(|err| RequestError::Auth(err))
}
/// Create a metadata nonce, and fetch the metadata for the file from the
@ -119,20 +92,8 @@ impl<'a> Metadata<'a> {
}
// Get the metadata nonce
let nonce = b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(MetaError::NoNonceHeader)?
.one()
.ok_or(MetaError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| MetaError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(MetaError::MalformedNonce)?
).map_err(|_| MetaError::MalformedNonce)?;
let nonce = header_nonce(HEADER_NONCE, &response)
.map_err(|err| MetaError::Nonce(err))?;
// Parse the metadata response, and decrypt it
Ok(MetadataResponse::from(
@ -228,9 +189,9 @@ pub enum Error {
Expired,
}
impl From<AuthError> for Error {
fn from(err: AuthError) -> Error {
Error::Request(RequestError::Auth(err))
impl From<RequestError> for Error {
fn from(err: RequestError) -> Error {
Error::Request(err)
}
}
@ -244,38 +205,13 @@ impl From<MetaError> for Error {
pub enum RequestError {
/// Failed authenticating, in order to fetch the file data.
#[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthError),
Auth(#[cause] NonceError),
/// Failed to retrieve the file metadata.
#[fail(display = "Failed to retrieve file metadata")]
Meta(#[cause] MetaError),
}
#[derive(Fail, Debug)]
pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce
/// failed.
#[fail(display = "Failed to request authentication nonce")]
NonceReq,
/// The response for fetching the authentication encryption nonce
/// indicated an error and wasn't successful.
#[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No authentication encryption nonce was included in the response
/// from the server, it was missing.
#[fail(display = "Missing authentication nonce in server response")]
NoNonceHeader,
/// The authentication encryption nonce from the response malformed or
/// empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed authentication nonce")]
MalformedNonce,
}
#[derive(Fail, Debug)]
pub enum MetaError {
/// An error occurred while computing the cryptographic signature used for
@ -292,16 +228,9 @@ pub enum MetaError {
#[fail(display = "Bad HTTP response '{}' while requesting metadata nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No metadata encryption nonce was included in the response from the
/// server, it was missing.
#[fail(display = "Missing metadata nonce in server response")]
NoNonceHeader,
/// The metadata encryption nonce from the response malformed or empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed metadata nonce")]
MalformedNonce,
/// Couldn't parse the metadata encryption nonce.
#[fail(display = "Failed to parse the metadata encryption nonce")]
Nonce(#[cause] NonceError),
/// The received metadata is malformed, and couldn't be decoded or
/// interpreted.

View file

@ -1,18 +1,13 @@
// TODO: define redirect policy
use reqwest::{Client, StatusCode};
use api::data::{
Error as DataError,
OwnedData,
};
use crypto::b64;
use api::nonce::{NonceError, request_auth_nonce};
use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// The default download count.
pub const PARAMS_DEFAULT_DOWNLOAD: u8 = 1;
pub const PARAMS_DEFAULT_DOWNLOAD_STR: &'static str = "1";
@ -69,43 +64,14 @@ impl<'a> Params<'a> {
.map_err(|err| err.into())
}
/// Fetch the authentication nonce for the file from the Send server.
/// Fetch the authentication nonce for the file from the remote server.
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, AuthError>
-> Result<Vec<u8>, PrepareError>
{
// Get the download URL, and parse the nonce
let download_url = self.file.download_url(false);
let response = client.get(download_url)
.send()
.map_err(|_| AuthError::NonceReq)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// TODO: should we check here whether a 404 is returned?
// // Handle expired files
// if status == FILE_EXPIRED_STATUS {
// return Err(Error::Expired);
// } else {
return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
// }
}
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthError::NoNonceHeader)?
.one()
.ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthError::MalformedNonce.into())
request_auth_nonce(
client,
self.file.download_url(false),
).map_err(|err| PrepareError::Auth(err))
}
/// Send the request for changing the parameters.
@ -219,12 +185,6 @@ impl From<PrepareError> for Error {
}
}
impl From<AuthError> for Error {
fn from(err: AuthError) -> Error {
PrepareError::Auth(err).into()
}
}
impl From<ChangeError> for Error {
fn from(err: ChangeError) -> Error {
Error::Change(err)
@ -250,7 +210,7 @@ pub enum ParamsDataError {
pub enum PrepareError {
/// Failed authenticating, needed to change the parameters.
#[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthError),
Auth(#[cause] NonceError),
/// An error occurred while building the parameter data that will be send
/// to the server.
@ -264,31 +224,6 @@ impl From<DataError> for PrepareError {
}
}
#[derive(Fail, Debug)]
pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce
/// failed.
#[fail(display = "Failed to request authentication nonce")]
NonceReq,
/// The response for fetching the authentication encryption nonce
/// indicated an error and wasn't successful.
#[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No authentication encryption nonce was included in the response
/// from the server, it was missing.
#[fail(display = "Missing authentication nonce in server response")]
NoNonceHeader,
/// The authentication encryption nonce from the response malformed or
/// empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed authentication nonce")]
MalformedNonce,
}
#[derive(Fail, Debug)]
pub enum ChangeError {
/// Sending the request to change the parameters failed.

View file

@ -1,19 +1,14 @@
// TODO: define redirect policy
use reqwest::{Client, StatusCode};
use api::data::{
Error as DataError,
OwnedData,
};
use crypto::b64;
use api::nonce::{NonceError, request_auth_nonce};
use crypto::key_set::KeySet;
use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// An action to change a password of an uploaded Send file.
pub struct Password<'a> {
/// The remote file to change the password for.
@ -65,41 +60,12 @@ impl<'a> Password<'a> {
/// Fetch the authentication nonce for the file from the Send server.
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, AuthError>
-> Result<Vec<u8>, PrepareError>
{
// Get the download URL, and parse the nonce
let download_url = self.file.download_url(false);
let response = client.get(download_url)
.send()
.map_err(|_| AuthError::NonceReq)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// TODO: should we check here whether a 404 is returned?
// // Handle expired files
// if status == FILE_EXPIRED_STATUS {
// return Err(Error::Expired);
// } else {
return Err(AuthError::NonceReqStatus(status, status.err_text()).into());
// }
}
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(HEADER_AUTH_NONCE)
.ok_or(AuthError::NoNonceHeader)?
.one()
.ok_or(AuthError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| AuthError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(AuthError::MalformedNonce)?
).map_err(|_| AuthError::MalformedNonce.into())
request_auth_nonce(
client,
self.file.download_url(false),
).map_err(|err| PrepareError::Auth(err))
}
/// Send the request for changing the file password.
@ -165,12 +131,6 @@ impl From<PrepareError> for Error {
}
}
impl From<AuthError> for Error {
fn from(err: AuthError) -> Error {
PrepareError::Auth(err).into()
}
}
impl From<ChangeError> for Error {
fn from(err: ChangeError) -> Error {
Error::Change(err)
@ -181,7 +141,7 @@ impl From<ChangeError> for Error {
pub enum PrepareError {
/// Failed authenticating, needed to set a new password.
#[fail(display = "Failed to authenticate")]
Auth(#[cause] AuthError),
Auth(#[cause] NonceError),
/// Some error occurred while building the data that will be sent.
/// The owner token might possibly be missing, the wrapped error will
@ -196,31 +156,6 @@ impl From<DataError> for PrepareError {
}
}
#[derive(Fail, Debug)]
pub enum AuthError {
/// Sending the request to gather the authentication encryption nonce
/// failed.
#[fail(display = "Failed to request authentication nonce")]
NonceReq,
/// The response for fetching the authentication encryption nonce
/// indicated an error and wasn't successful.
#[fail(display = "Bad HTTP response '{}' while requesting authentication nonce", _1)]
NonceReqStatus(StatusCode, String),
/// No authentication encryption nonce was included in the response
/// from the server, it was missing.
#[fail(display = "Missing authentication nonce in server response")]
NoNonceHeader,
/// The authentication encryption nonce from the response malformed or
/// empty.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed authentication nonce")]
MalformedNonce,
}
#[derive(Fail, Debug)]
pub enum ChangeError {
/// Sending the request to change the password failed.

View file

@ -22,7 +22,7 @@ use url::{
Url,
};
use crypto::b64;
use api::nonce::{HEADER_NONCE, header_nonce};
use crypto::key_set::KeySet;
use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile;
@ -45,9 +45,6 @@ use super::password::{
type EncryptedReader = ProgressReader<BufReader<EncryptedFileReader>>;
/// The name of the header that is used for the authentication nonce.
const HEADER_AUTH_NONCE: &'static str = "WWW-Authenticate";
/// A file upload action to a Send server.
pub struct Upload {
/// The Send host to upload the file to.
@ -262,14 +259,7 @@ impl Upload {
}
// Try to get the nonce, don't error on failure
let nonce = response.headers()
.get_raw(HEADER_AUTH_NONCE)
.and_then(|h| h.one())
.and_then(|line| String::from_utf8(line.to_vec()).ok())
.and_then(|line| line.split_terminator(" ").skip(1).next()
.map(|line| line.to_owned())
)
.and_then(|nonce| b64::decode(&nonce).ok());
let nonce = header_nonce(HEADER_NONCE, &response).ok();
// Decode the response
let response: UploadResponse = match response.json() {

View file

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

84
api/src/api/nonce.rs Normal file
View file

@ -0,0 +1,84 @@
use url::Url;
use reqwest::{Client, Response, StatusCode};
use crypto::b64;
use ext::status_code::StatusCodeExt;
/// The name of the header the nonce is delivered in.
pub const HEADER_NONCE: &'static str = "WWW-Authenticate";
/// Do a new request, and fetch the nonce from the header with the given name.
pub fn request_nonce(client: &Client, url: Url, header: &str)
-> Result<Vec<u8>, NonceError>
{
// Make the request
let response = client.get(url)
.send()
.map_err(|_| NonceError::Req)?;
// Validate the status code
let status = response.status();
if !status.is_success() {
// TODO: should we check here whether a 404 is returned?
// // Handle expired files
// if status == FILE_EXPIRED_STATUS {
// return Err(Error::Expired);
// } else {
return Err(NonceError::ReqStatus(status, status.err_text()).into());
// }
}
// Fetch the nonce
header_nonce(header, &response)
}
/// Do a new request, and fetch the authentication nonce from the header with
/// the given name.
pub fn request_auth_nonce(client: &Client, url: Url)
-> Result<Vec<u8>, NonceError>
{
request_nonce(client, url, HEADER_NONCE)
}
/// Get a nonce from a header in the given response.
pub fn header_nonce(header: &str, response: &Response)
-> Result<Vec<u8>, NonceError>
{
// Get the authentication nonce
b64::decode(
response.headers()
.get_raw(header)
.ok_or(NonceError::NoNonceHeader)?
.one()
.ok_or(NonceError::MalformedNonce)
.and_then(|line| String::from_utf8(line.to_vec())
.map_err(|_| NonceError::MalformedNonce)
)?
.split_terminator(" ")
.skip(1)
.next()
.ok_or(NonceError::MalformedNonce)?
).map_err(|_| NonceError::MalformedNonce.into())
}
#[derive(Fail, Debug)]
pub enum NonceError {
/// Sending the request to fetch a nonce failed.
#[fail(display = "Failed to request nonce")]
Req,
/// The response for fetching the nonce indicated an error and wasn't
/// successful.
#[fail(display = "Bad HTTP response '{}' while requesting nonce", _1)]
ReqStatus(StatusCode, String),
/// The nonce header was missing from the request.
#[fail(display = "Missing nonce in server response")]
NoNonceHeader,
/// The received nonce could not be parsed, because it was malformed.
/// Maybe the server responded with a new format that isn't supported yet
/// by this client.
#[fail(display = "Received malformed nonce")]
MalformedNonce,
}