use reqwest::Client;
use api::data::{
Error as DataError,
OwnedData,
};
use api::nonce::{NonceError, request_nonce};
use api::request::{ensure_success, ResponseError};
use api::url::UrlBuilder;
use crypto::key_set::KeySet;
use file::remote_file::RemoteFile;
pub struct Password<'a> {
file: &'a RemoteFile,
password: &'a str,
nonce: Vec<u8>,
}
impl<'a> Password<'a> {
pub fn new(
file: &'a RemoteFile,
password: &'a str,
nonce: Option<Vec<u8>>,
) -> Self {
Self {
file,
password,
nonce: nonce.unwrap_or_default(),
}
}
pub fn invoke(mut self, client: &Client) -> Result<(), Error> {
let mut key = KeySet::from(self.file, None);
if self.nonce.is_empty() {
self.nonce = self.fetch_auth_nonce(client)?;
}
key.derive_auth_password(self.password, &UrlBuilder::download(self.file, true));
let data = OwnedData::from(PasswordData::from(&key), &self.file)
.map_err(|err| -> PrepareError { err.into() })?;
self.change_password(client, &data)
}
fn fetch_auth_nonce(&self, client: &Client)
-> Result<Vec<u8>, Error>
{
request_nonce(
client,
UrlBuilder::download(self.file, false),
).map_err(|err| err.into())
}
fn change_password(
&self,
client: &Client,
data: &OwnedData<PasswordData>,
) -> Result<(), Error> {
let url = UrlBuilder::api_password(self.file);
let response = client.post(url)
.json(&data)
.send()
.map_err(|_| ChangeError::Request)?;
ensure_success(&response)
.map_err(|err| err.into())
}
}
#[derive(Debug, Serialize)]
struct PasswordData {
auth: String,
}
impl PasswordData {
pub fn from(key: &KeySet) -> PasswordData {
PasswordData {
auth: key.auth_key_encoded().unwrap(),
}
}
}
#[derive(Fail, Debug)]
pub enum Error {
#[fail(display = "failed to prepare setting the password")]
Prepare(#[cause] PrepareError),
#[fail(display = "the file has expired or did never exist")]
Expired,
#[fail(display = "failed to send the password change request")]
Change(#[cause] ChangeError),
}
impl From<NonceError> for Error {
fn from(err: NonceError) -> Error {
match err {
NonceError::Expired => Error::Expired,
err => Error::Prepare(PrepareError::Auth(err)),
}
}
}
impl From<PrepareError> for Error {
fn from(err: PrepareError) -> Error {
Error::Prepare(err)
}
}
impl From<ChangeError> for Error {
fn from(err: ChangeError) -> Error {
Error::Change(err)
}
}
impl From<ResponseError> for Error {
fn from(err: ResponseError) -> Error {
match err {
ResponseError::Expired => Error::Expired,
err => Error::Change(ChangeError::Response(err)),
}
}
}
#[derive(Fail, Debug)]
pub enum PrepareError {
#[fail(display = "failed to authenticate")]
Auth(#[cause] NonceError),
#[fail(display = "")]
Data(#[cause] DataError),
}
impl From<DataError> for PrepareError {
fn from(err: DataError) -> PrepareError {
PrepareError::Data(err)
}
}
#[derive(Fail, Debug)]
pub enum ChangeError {
#[fail(display = "failed to send password change request")]
Request,
#[fail(display = "bad response from server while changing password")]
Response(#[cause] ResponseError),
}