Selaa lähdekoodia

Prompt for owner tokens, allow owner token flag

timvisee 7 vuotta sitten
vanhempi
commit
5a4b1958e1

+ 5 - 0
api/src/file/remote_file.rs

@@ -171,6 +171,11 @@ impl RemoteFile {
         self.owner_token.as_ref()
     }
 
+    /// Get the owner token if set.
+    pub fn owner_token_mut(&mut self) -> &mut Option<String> {
+        &mut self.owner_token
+    }
+
     /// Set the owner token.
     pub fn set_owner_token(&mut self, token: Option<String>) {
         self.owner_token = token;

+ 5 - 4
cli/src/action/delete.rs

@@ -14,7 +14,7 @@ use cmd::matcher::{
     delete::DeleteMatcher,
 };
 use error::ActionError;
-use util::print_success;
+use util::{ensure_owner_token, print_success};
 
 /// A file delete action.
 pub struct Delete<'a> {
@@ -41,10 +41,11 @@ impl<'a> Delete<'a> {
         // Create a reqwest client
         let client = Client::new();
 
-        // Parse the remote file based on the share URL, get the password
-        let file = RemoteFile::parse_url(url, matcher_delete.owner())?;
+        // Parse the remote file based on the share URL, get the owner token
+        let mut file = RemoteFile::parse_url(url, matcher_delete.owner())?;
 
-        // TODO: show an informative error if the owner token isn't set
+        // Ensure the owner token is set
+        ensure_owner_token(file.owner_token_mut());
 
         // Send the file deletion request
         ApiDelete::new(&file, None).invoke(&client)?;

+ 4 - 3
cli/src/action/info.rs

@@ -19,7 +19,7 @@ use cmd::matcher::{
     Matcher,
     info::InfoMatcher,
 };
-use util::{print_error, ensure_password};
+use util::{ensure_owner_token, ensure_password, print_error};
 
 /// A file info action.
 pub struct Info<'a> {
@@ -47,10 +47,11 @@ impl<'a> Info<'a> {
         let client = Client::new();
 
         // Parse the remote file based on the share URL, get the password
-        let file = RemoteFile::parse_url(url, matcher_info.owner())?;
+        let mut file = RemoteFile::parse_url(url, matcher_info.owner())?;
         let mut password = matcher_info.password();
 
-        // TODO: show an informative error if the owner token isn't set
+        // Ensure the owner token is set
+        ensure_owner_token(file.owner_token_mut());
 
         // Check whether the file exists
         let exists = ApiExists::new(&file).invoke(&client)?;

+ 4 - 4
cli/src/action/params.rs

@@ -11,7 +11,7 @@ use cmd::matcher::{
     params::ParamsMatcher,
 };
 use error::ActionError;
-use util::print_success;
+use util::{ensure_owner_token, print_success};
 
 /// A file parameters action.
 pub struct Params<'a> {
@@ -39,10 +39,10 @@ impl<'a> Params<'a> {
         let client = Client::new();
 
         // Parse the remote file based on the share URL
-        // TODO: handle error here
-        let file = RemoteFile::parse_url(url, matcher_params.owner())?;
+        let mut file = RemoteFile::parse_url(url, matcher_params.owner())?;
 
-        // TODO: show an informative error if the owner token isn't set
+        // Ensure the owner token is set
+        ensure_owner_token(file.owner_token_mut());
 
         // Build the parameters data object
         let data = ParamsDataBuilder::default()

+ 4 - 4
cli/src/action/password.rs

@@ -8,7 +8,7 @@ use cmd::matcher::{
     password::PasswordMatcher,
 };
 use error::ActionError;
-use util::print_success;
+use util::{ensure_owner_token, print_success};
 
 /// A file password action.
 pub struct Password<'a> {
@@ -36,10 +36,10 @@ impl<'a> Password<'a> {
         let client = Client::new();
 
         // Parse the remote file based on the share URL
-        // TODO: handle error here
-        let file = RemoteFile::parse_url(url, matcher_password.owner())?;
+        let mut file = RemoteFile::parse_url(url, matcher_password.owner())?;
 
-        // TODO: show an informative error if the owner token isn't set
+        // Ensure the owner token is set
+        ensure_owner_token(file.owner_token_mut());
 
         // Execute an password action
         ApiPassword::new(&file, &matcher_password.password(), None).invoke(&client)?;

+ 20 - 3
cli/src/cmd/arg/owner.rs

@@ -1,6 +1,7 @@
 use clap::{Arg, ArgMatches};
 
-use super::{CmdArg, CmdArgOption};
+use super::{CmdArg, CmdArgFlag, CmdArgOption};
+use util::prompt_owner_token;
 
 /// The owner argument.
 pub struct ArgOwner { }
@@ -18,14 +19,30 @@ impl CmdArg for ArgOwner {
             .alias("owner-token")
             .alias("token")
             .value_name("TOKEN")
+            .min_values(0)
+            .max_values(1)
             .help("Specify the file owner token")
     }
 }
 
+impl CmdArgFlag for ArgOwner { }
+
 impl<'a> CmdArgOption<'a> for ArgOwner {
-    type Value = Option<&'a str>;
+    type Value = Option<String>;
 
     fn value<'b: 'a>(matches: &'a ArgMatches<'b>) -> Self::Value {
-        Self::value_raw(matches)
+        // The owner token flag must be present
+        if !Self::is_present(matches) {
+            return None;
+        }
+
+        // Get the owner token from the argument if set
+        match Self::value_raw(matches) {
+            None => {},
+            p => return p.map(|p| p.into()),
+        }
+
+        // Prompt for the owner token
+        Some(prompt_owner_token())
     }
 }

+ 49 - 2
cli/src/util.rs

@@ -6,7 +6,12 @@ extern crate open;
 #[cfg(feature = "clipboard")]
 use std::error::Error as StdError;
 use std::fmt::{Debug, Display};
-use std::io::Error as IoError;
+use std::io::{
+    Error as IoError,
+    stdin,
+    stderr,
+    Write,
+};
 use std::process::{exit, ExitStatus};
 
 #[cfg(feature = "clipboard")]
@@ -89,7 +94,7 @@ pub fn prompt_password() -> String {
     match prompt_password_stderr("Password: ") {
         Ok(password) => password,
         Err(err) => quit_error(err.context(
-            "Failed to read password from stdin with password prompt"
+            "Failed to read password from password prompt"
         )),
     }
 }
@@ -116,3 +121,45 @@ pub fn ensure_password(password: &mut Option<String>, needs: bool) {
         *password = None;
     }
 }
+
+/// Prompt the user to enter some value.
+/// The prompt that is shown should be passed to `prompt`,
+/// excluding the `:` suffix.
+// TODO: do not prompt if no-interactive
+pub fn prompt(prompt: &str) -> String {
+    // Show the prompt
+    eprint!("{}: ", prompt);
+    let _ = stderr().flush();
+
+    // Get the input
+    let mut input = String::new();
+    if let Err(err) = stdin().read_line(&mut input) {
+        quit_error(err.context(
+            "Failed to read input from prompt"
+        ));
+    }
+
+    // Trim and return
+    input.trim().into()
+}
+
+/// Prompt the user to enter an owner token.
+pub fn prompt_owner_token() -> String {
+    prompt("Owner token")
+}
+
+/// Get the owner token.
+/// This method will ensure an owner token is set in the given `token`
+/// parameter.
+///
+/// This method will prompt the user for the token, if it wasn't set.
+pub fn ensure_owner_token(token: &mut Option<String>) {
+    // Return if we're fine
+    if token.is_some() {
+        return;
+    }
+
+    // Ask for the owner token
+    println!("The file owner token is required for authentication.");
+    *token = Some(prompt_owner_token());
+}