Merge branch 'password-diceware' into 'master'
Add feature to generate easily rememberable passphrase See merge request timvisee/ffsend!4
This commit is contained in:
commit
743b84af67
11 changed files with 166 additions and 11 deletions
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -130,6 +130,14 @@ name = "cfg-if"
|
|||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chbs"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.3"
|
||||
|
@ -175,6 +183,14 @@ dependencies = [
|
|||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.6.0"
|
||||
|
@ -351,6 +367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "ffsend"
|
||||
version = "0.0.7"
|
||||
dependencies = [
|
||||
"chbs 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clipboard 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -931,6 +948,23 @@ dependencies = [
|
|||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.40"
|
||||
|
@ -1658,10 +1692,12 @@ dependencies = [
|
|||
"checksum bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd32989a66957d3f0cba6588f15d4281a733f4e9ffc43fcd2385f57d3bf99ff"
|
||||
"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d"
|
||||
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
|
||||
"checksum chbs 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7580cf614cddea9bbf9de26bb17faa88aba720c745e65bd96fdfd794d2bf30ee"
|
||||
"checksum chrono 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a81892f0d5a53f46fc05ef0b917305a81c13f1f13bb59ac91ff595817f0764b1"
|
||||
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
|
||||
"checksum clipboard 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b4623b47d8637fc9d47564583d4cc01eb8c8e34e26b2bf348bf4b036acb657"
|
||||
"checksum clipboard-win 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "289da2fc09ab964a4948a63287c94fcb4698fa823c46da84c3792928c9d36110"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc"
|
||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
|
||||
|
@ -1746,6 +1782,8 @@ dependencies = [
|
|||
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
|
||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
|
||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
||||
"checksum rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "12397506224b2f93e6664ffc4f664b29be8208e5157d3d90b44f09b5fae470ea"
|
||||
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
|
||||
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3"
|
||||
|
|
|
@ -61,6 +61,7 @@ history = []
|
|||
no-color = ["colored/no-color"]
|
||||
|
||||
[dependencies]
|
||||
chbs = "0.0.1"
|
||||
chrono = "0.4"
|
||||
clap = "2.31"
|
||||
colored = "1.6"
|
||||
|
|
|
@ -5,6 +5,12 @@ use ffsend_api::action::password::{
|
|||
};
|
||||
use ffsend_api::file::remote_file::RemoteFile;
|
||||
use ffsend_api::reqwest::Client;
|
||||
use prettytable::{
|
||||
cell::Cell,
|
||||
format::FormatBuilder,
|
||||
row::Row,
|
||||
Table,
|
||||
};
|
||||
|
||||
use cmd::matcher::{
|
||||
main::MainMatcher,
|
||||
|
@ -50,10 +56,13 @@ impl<'a> Password<'a> {
|
|||
// Ensure the owner token is set
|
||||
ensure_owner_token(file.owner_token_mut(), &matcher_main);
|
||||
|
||||
// Get the password to use and whether it was generated
|
||||
let (password, password_generated) = matcher_password.password();
|
||||
|
||||
// Execute an password action
|
||||
let result = ApiPassword::new(
|
||||
&file,
|
||||
&matcher_password.password(),
|
||||
&password,
|
||||
None,
|
||||
).invoke(&client);
|
||||
if let Err(PasswordError::Expired) = result {
|
||||
|
@ -67,6 +76,17 @@ impl<'a> Password<'a> {
|
|||
#[cfg(feature = "history")]
|
||||
history_tool::add(&matcher_main, file, true);
|
||||
|
||||
// Print the passphrase if one was generated
|
||||
if password_generated {
|
||||
let mut table = Table::new();
|
||||
table.set_format(FormatBuilder::new().padding(0, 2).build());
|
||||
table.add_row(Row::new(vec![
|
||||
Cell::new("Passphrase:"),
|
||||
Cell::new(&password),
|
||||
]));
|
||||
table.printstd();
|
||||
}
|
||||
|
||||
// Print a success message
|
||||
print_success("Password set");
|
||||
|
||||
|
|
|
@ -208,12 +208,18 @@ impl<'a> Upload<'a> {
|
|||
// Build the progress reporter
|
||||
let progress_reporter: Arc<Mutex<ProgressReporter>> = progress_bar;
|
||||
|
||||
// Get the password to use and whether it was generated
|
||||
let password = matcher_upload.password();
|
||||
let (password, password_generated) = password
|
||||
.map(|(p, g)| (Some(p), g))
|
||||
.unwrap_or((None, false));
|
||||
|
||||
// Execute an upload action
|
||||
let file = ApiUpload::new(
|
||||
host,
|
||||
path.clone(),
|
||||
file_name,
|
||||
matcher_upload.password(),
|
||||
password.clone(),
|
||||
params,
|
||||
).invoke(&client, &progress_reporter)?;
|
||||
|
||||
|
@ -225,6 +231,12 @@ impl<'a> Upload<'a> {
|
|||
Cell::new("Share link:"),
|
||||
Cell::new(url.as_str()),
|
||||
]));
|
||||
if password_generated {
|
||||
table.add_row(Row::new(vec![
|
||||
Cell::new("Passphrase:"),
|
||||
Cell::new(&password.unwrap_or("?".into())),
|
||||
]));
|
||||
}
|
||||
if matcher_main.verbose() {
|
||||
table.add_row(Row::new(vec![
|
||||
Cell::new("Owner token:"),
|
||||
|
|
36
src/cmd/arg/gen_passphrase.rs
Normal file
36
src/cmd/arg/gen_passphrase.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use clap::Arg;
|
||||
use chbs;
|
||||
|
||||
use super::{CmdArg, CmdArgFlag};
|
||||
|
||||
/// The number of words the passphrase must consist of.
|
||||
const PASSPHRASE_WORDS: usize = 4;
|
||||
|
||||
/// The passphrase generation argument.
|
||||
pub struct ArgGenPassphrase { }
|
||||
|
||||
impl ArgGenPassphrase {
|
||||
/// Generate a cryptographically secure passphrase that is easily
|
||||
/// rememberable using diceware.
|
||||
pub fn gen_passphrase() -> String {
|
||||
chbs::passphrase(PASSPHRASE_WORDS)
|
||||
}
|
||||
}
|
||||
|
||||
impl CmdArg for ArgGenPassphrase {
|
||||
fn name() -> &'static str {
|
||||
"gen-passphrase"
|
||||
}
|
||||
|
||||
fn build<'b, 'c>() -> Arg<'b, 'c> {
|
||||
Arg::with_name("gen-passphrase")
|
||||
.long("gen-passphrase")
|
||||
.alias("gen-password")
|
||||
.alias("generate-passphrase")
|
||||
.alias("generate-password")
|
||||
.short("P")
|
||||
.help("Protect the file with a generated passphrase")
|
||||
}
|
||||
}
|
||||
|
||||
impl CmdArgFlag for ArgGenPassphrase { }
|
|
@ -1,4 +1,5 @@
|
|||
pub mod download_limit;
|
||||
pub mod gen_passphrase;
|
||||
pub mod host;
|
||||
pub mod owner;
|
||||
pub mod password;
|
||||
|
@ -6,6 +7,7 @@ pub mod url;
|
|||
|
||||
// Reexport to arg module
|
||||
pub use self::download_limit::ArgDownloadLimit;
|
||||
pub use self::gen_passphrase::ArgGenPassphrase;
|
||||
pub use self::host::ArgHost;
|
||||
pub use self::owner::ArgOwner;
|
||||
pub use self::password::ArgPassword;
|
||||
|
|
|
@ -2,7 +2,14 @@ use clap::ArgMatches;
|
|||
use ffsend_api::url::Url;
|
||||
use rpassword::prompt_password_stderr;
|
||||
|
||||
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArgOption};
|
||||
use cmd::arg::{
|
||||
ArgGenPassphrase,
|
||||
ArgOwner,
|
||||
ArgPassword,
|
||||
ArgUrl,
|
||||
CmdArgFlag,
|
||||
CmdArgOption,
|
||||
};
|
||||
use cmd::matcher::{MainMatcher, Matcher};
|
||||
use util::check_empty_password;
|
||||
|
||||
|
@ -29,7 +36,15 @@ impl<'a: 'b, 'b> PasswordMatcher<'a> {
|
|||
}
|
||||
|
||||
/// Get the password.
|
||||
pub fn password(&'a self) -> String {
|
||||
///
|
||||
/// The password is returned in the following format:
|
||||
/// `(password, generated)`
|
||||
pub fn password(&'a self) -> (String, bool) {
|
||||
// Generate a passphrase if requested
|
||||
if ArgGenPassphrase::is_present(self.matches) {
|
||||
return (ArgGenPassphrase::gen_passphrase(), true);
|
||||
}
|
||||
|
||||
// Get the password, or prompt for it
|
||||
let password = match ArgPassword::value(self.matches) {
|
||||
Some(password) => password,
|
||||
|
@ -48,7 +63,7 @@ impl<'a: 'b, 'b> PasswordMatcher<'a> {
|
|||
// Check for empty passwords
|
||||
check_empty_password(&password, &matcher_main);
|
||||
|
||||
password
|
||||
(password, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,14 @@ use ffsend_api::action::params::{
|
|||
};
|
||||
use ffsend_api::url::Url;
|
||||
|
||||
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArgOption};
|
||||
use cmd::arg::{
|
||||
ArgDownloadLimit,
|
||||
ArgGenPassphrase,
|
||||
ArgHost,
|
||||
ArgPassword,
|
||||
CmdArgFlag,
|
||||
CmdArgOption,
|
||||
};
|
||||
use super::Matcher;
|
||||
use util::{env_var_present, ErrorHintsBuilder, quit_error_msg};
|
||||
|
||||
|
@ -55,9 +62,24 @@ impl<'a: 'b, 'b> UploadMatcher<'a> {
|
|||
}
|
||||
|
||||
/// Get the password.
|
||||
/// `None` is returned if no password was specified.
|
||||
pub fn password(&'a self) -> Option<String> {
|
||||
/// A generated passphrase will be returned if the user requested so,
|
||||
/// otherwise the specified password is returned.
|
||||
/// If no password was set, `None` is returned instead.
|
||||
///
|
||||
/// The password is returned in the following format:
|
||||
/// `(password, generated)`
|
||||
pub fn password(&'a self) -> Option<(String, bool)> {
|
||||
// Generate a passphrase if requested
|
||||
if ArgGenPassphrase::is_present(self.matches) {
|
||||
return Some((
|
||||
ArgGenPassphrase::gen_passphrase(),
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
// Use a specified password or use nothing
|
||||
ArgPassword::value(self.matches)
|
||||
.map(|password| (password, false))
|
||||
}
|
||||
|
||||
/// Get the download limit.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clap::{App, SubCommand};
|
||||
|
||||
use cmd::arg::{ArgOwner, ArgPassword, ArgUrl, CmdArg};
|
||||
use cmd::arg::{ArgGenPassphrase, ArgOwner, ArgPassword, ArgUrl, CmdArg};
|
||||
|
||||
/// The password command definition.
|
||||
pub struct CmdPassword;
|
||||
|
@ -14,6 +14,7 @@ impl CmdPassword {
|
|||
.arg(ArgUrl::build())
|
||||
.arg(ArgPassword::build()
|
||||
.help("Specify a password, do not prompt"))
|
||||
.arg(ArgGenPassphrase::build())
|
||||
.arg(ArgOwner::build())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,15 @@ use ffsend_api::action::params::{
|
|||
PARAMS_DEFAULT_DOWNLOAD_STR as DOWNLOAD_DEFAULT,
|
||||
};
|
||||
|
||||
use cmd::arg::{ArgDownloadLimit, ArgHost, ArgPassword, CmdArg};
|
||||
use cmd::arg::{
|
||||
ArgDownloadLimit,
|
||||
ArgGenPassphrase,
|
||||
ArgHost,
|
||||
ArgPassword,
|
||||
CmdArg,
|
||||
};
|
||||
|
||||
/// The uplaod command definition.
|
||||
/// The upload command definition.
|
||||
pub struct CmdUpload;
|
||||
|
||||
impl CmdUpload {
|
||||
|
@ -22,6 +28,7 @@ impl CmdUpload {
|
|||
.multiple(false))
|
||||
.arg(ArgPassword::build()
|
||||
.help("Protect the file with a password"))
|
||||
.arg(ArgGenPassphrase::build())
|
||||
.arg(ArgDownloadLimit::build()
|
||||
.default_value(DOWNLOAD_DEFAULT))
|
||||
.arg(ArgHost::build())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
extern crate chbs;
|
||||
extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
|
Loading…
Reference in a new issue