Permalink
Browse files

30DaysOfTests: default password generation length

  • Loading branch information...
conradkdotcom committed Aug 12, 2017
1 parent 173a00b commit db24a683bddc675d32c628ae6778cba567e753d0
Showing with 86 additions and 71 deletions.
  1. +14 −16 src/commands/generate.rs
  2. +14 −16 src/commands/regenerate.rs
  3. +58 −39 src/generate.rs
View
@@ -14,7 +14,7 @@
use getopts;
use password;
use generate::{PasswordSpec, generate_hard_password};
use generate::{PasswordSpec, check_password_len};
use clip::{copy_to_clipboard, paste_keys};
use std::io::Write;
use std::ops::Deref;
@@ -55,24 +55,22 @@ pub fn callback_exec(
return Err(1);
}
let password_spec = PasswordSpec::from_matches(matches);
let pwspec = PasswordSpec::new(
matches.opt_present("alnum"),
matches.opt_str("length").and_then(|len| {
check_password_len(len.parse::<usize>().ok())
}),
);
let password_as_string = match password_spec {
None => {
let password_as_string = match pwspec.generate_hard_password() {
Ok(password_as_string) => password_as_string,
Err(io_err) => {
println_stderr!(
"Woops, I could not generate the password (reason: {:?}).",
io_err
);
return Err(1);
}
Some(spec) => {
match generate_hard_password(spec.alnum, spec.len) {
Ok(password_as_string) => password_as_string,
Err(io_err) => {
println_stderr!(
"Woops, I could not generate the password (reason: {:?}).",
io_err
);
return Err(1);
}
}
}
};
// Read the master password and try to save the new password.
View
@@ -16,7 +16,7 @@ use getopts;
use ffi;
use list;
use password;
use generate::{PasswordSpec, generate_hard_password};
use generate::{PasswordSpec, check_password_len};
use clip;
use std::io::Write;
@@ -57,24 +57,22 @@ pub fn callback_exec(
).ok_or(1)?
.clone();
let password_spec = PasswordSpec::from_matches(matches);
let pwspec = PasswordSpec::new(
matches.opt_present("alnum"),
matches.opt_str("length").and_then(|len| {
check_password_len(len.parse::<usize>().ok())
}),
);
let password_as_string = match password_spec {
None => {
let password_as_string = match pwspec.generate_hard_password() {
Ok(password_as_string) => password_as_string,
Err(io_err) => {
println_stderr!(
"Woops, I could not generate the password (reason: {:?}).",
io_err
);
return Err(1);
}
Some(spec) => {
match generate_hard_password(spec.alnum, spec.len) {
Ok(password_as_string) => password_as_string,
Err(io_err) => {
println_stderr!(
"Woops, I could not generate the password (reason: {:?}).",
io_err
);
return Err(1);
}
}
}
};
let change_result =
View
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use getopts;
use rand::{Rng, OsRng};
use std::io::{Write, Result as IoResult};
use safe_string::SafeString;
@@ -48,51 +47,71 @@ fn password_is_hard(password: &str, alnum: bool) -> bool {
(alnum || password.find(is_punctuation).is_some())
}
pub fn generate_hard_password(alnum: bool, len: usize) -> IoResult<SafeString> {
loop {
let password = generate_password(alnum, len)?;
if password_is_hard(password.as_ref(), alnum) {
return Ok(password);
}
}
}
pub struct PasswordSpec {
pub alnum: bool,
pub len: usize,
}
impl PasswordSpec {
pub fn from_matches(matches: &getopts::Matches) -> Option<PasswordSpec> {
let alnum = matches.opt_present("alnum");
let mut password_len = 32;
if let Some(len) = matches.opt_str("length") {
password_len = match len.parse::<usize>() {
Ok(parsed_len) => {
// We want passwords to contain at least one uppercase letter, one lowercase
// letter and one digit. So we need at least 4 characters for each password.
// This checks makes sure we don't run into an infinite loop trying to generate
// a password of length <4 with 4 different kinds of characters (uppercase,
// lowercase, numeric, punctuation).
if parsed_len < 4 {
println_err!("Woops! The length of the password must be at least 4. This");
println_err!("allows us to make sure your password is secure.");
return None;
}
parsed_len
}
Err(_) => {
println_err!(
"Woops! The length option must be a valid number, for instance \
8 or 16."
);
return None;
}
pub fn new(alnum: bool, password_len: Option<usize>) -> PasswordSpec {
PasswordSpec {
alnum: alnum,
len: password_len.unwrap_or(32),
}
}
pub fn generate_hard_password(&self) -> IoResult<SafeString> {
loop {
let password = generate_password(self.alnum, self.len)?;
if password_is_hard(password.as_ref(), self.alnum) {
return Ok(password);
}
}
Some(PasswordSpec {
alnum: alnum,
len: password_len,
})
}
}
pub fn check_password_len(opt: Option<usize>) -> Option<usize> {
match opt {
Some(len) => {
// We want passwords to contain at least one uppercase letter, one lowercase
// letter and one digit. So we need at least 4 characters for each password.
// This checks makes sure we don't run into an infinite loop trying to generate
// a password of length < 4 with 4 different kinds of characters (uppercase,
// lowercase, numeric, punctuation).
if len < 4 {
println_err!("Woops! The length of the password must be at least 4. This");
println_err!("allows us to make sure your password is secure.");
None
} else {
Some(len)
}
}
None => {
println_err!("Woops! The length option must be a valid number, for instance 8 or 16.");
None
}
}
}
#[cfg(test)]
mod test {
use super::PasswordSpec;
#[test]
fn default_password_size_is_32() {
assert_eq!(
PasswordSpec::new(false, None)
.generate_hard_password()
.unwrap()
.len(),
32
);
assert_eq!(
PasswordSpec::new(false, Some(16))
.generate_hard_password()
.unwrap()
.len(),
16
);
}
}

0 comments on commit db24a68

Please sign in to comment.