ffsend_api/action/
password.rs1use thiserror::Error;
2
3use crate::api::data::{Error as DataError, OwnedData};
4use crate::api::nonce::{request_nonce, NonceError};
5use crate::api::request::{ensure_success, ResponseError};
6use crate::api::url::UrlBuilder;
7use crate::client::Client;
8use crate::crypto::key_set::KeySet;
9use crate::file::remote_file::RemoteFile;
10
11pub struct Password<'a> {
15 file: &'a RemoteFile,
17
18 password: &'a str,
20
21 nonce: Vec<u8>,
24}
25
26impl<'a> Password<'a> {
27 pub fn new(file: &'a RemoteFile, password: &'a str, nonce: Option<Vec<u8>>) -> Self {
29 Self {
30 file,
31 password,
32 nonce: nonce.unwrap_or_default(),
33 }
34 }
35
36 pub fn invoke(mut self, client: &Client) -> Result<(), Error> {
38 let mut key = KeySet::from(self.file, None);
40
41 if self.nonce.is_empty() {
43 self.nonce = self.fetch_auth_nonce(client)?;
44 }
45
46 key.derive_auth_password(self.password, &UrlBuilder::download(self.file, true));
48
49 let data = OwnedData::from(PasswordData::from(&key), &self.file)
51 .map_err(|err| -> PrepareError { err.into() })?;
52
53 self.change_password(client, &data)
55 }
56
57 fn fetch_auth_nonce(&self, client: &Client) -> Result<Vec<u8>, Error> {
59 request_nonce(client, UrlBuilder::download(self.file, false)).map_err(|err| err.into())
60 }
61
62 fn change_password(
64 &self,
65 client: &Client,
66 data: &OwnedData<PasswordData>,
67 ) -> Result<(), Error> {
68 let url = UrlBuilder::api_password(self.file);
70 let response = client
71 .post(url)
72 .json(&data)
73 .send()
74 .map_err(|_| ChangeError::Request)?;
75
76 ensure_success(&response).map_err(|err| err.into())
78 }
79}
80
81#[derive(Debug, Serialize)]
84struct PasswordData {
85 auth: String,
87}
88
89impl PasswordData {
90 pub fn from(key: &KeySet) -> PasswordData {
92 PasswordData {
93 auth: key.auth_key_encoded().unwrap(),
94 }
95 }
96}
97
98#[derive(Error, Debug)]
99pub enum Error {
100 #[error("failed to prepare setting the password")]
102 Prepare(#[from] PrepareError),
103
104 #[error("the file has expired or did never exist")]
107 Expired,
108
109 #[error("failed to send the password change request")]
112 Change(#[from] ChangeError),
113}
114
115impl From<NonceError> for Error {
116 fn from(err: NonceError) -> Error {
117 match err {
118 NonceError::Expired => Error::Expired,
119 err => Error::Prepare(PrepareError::Auth(err)),
120 }
121 }
122}
123
124impl From<ResponseError> for Error {
125 fn from(err: ResponseError) -> Error {
126 match err {
127 ResponseError::Expired => Error::Expired,
128 err => Error::Change(ChangeError::Response(err)),
129 }
130 }
131}
132
133#[derive(Error, Debug)]
134pub enum PrepareError {
135 #[error("failed to authenticate")]
137 Auth(#[from] NonceError),
138
139 #[error("")]
143 Data(#[from] DataError),
144}
145
146#[derive(Error, Debug)]
147pub enum ChangeError {
148 #[error("failed to send password change request")]
150 Request,
151
152 #[error("bad response from server while changing password")]
154 Response(#[from] ResponseError),
155}