1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
// use users::get_current_username;
// use dirs::data_dir;
use std::borrow::Borrow;
use std::ops::Deref;
use std::{fs, io};
use std::path::{Path, PathBuf};
use rand::{Rng, CryptoRng};
use crate::error::{IntoIOError, IntoIOResult};
pub struct User(String);
pub struct Pass(String);
impl Borrow<str> for User { fn borrow(&self) -> &str { &self.0 } }
impl Borrow<str> for Pass { fn borrow(&self) -> &str { &self.0 } }
impl Deref for User { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } }
impl Deref for Pass { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } }
static PASSWORD_FILE_SUBDIR: &str = "tomsg_client"; // ~/.local/share/PASSWORD_FILE_SUBDIR/...
static PASSWORD_FILE_SUBDIR_HOME: &str = ".tomsg_client"; // ~/PASSWORD_FILE_SUBDIR_HOME/...
static PASSWORD_FILE_NAME: &str = "password.txt";
pub fn get_auth_info() -> io::Result<(User, Pass)> {
Ok((get_username()?, obtain_password()?))
}
fn get_username() -> io::Result<User> {
match users::get_current_username().map(|u| u.to_str().map(|u| u.to_string())) {
Some(Some(u)) => Ok(User(u)),
Some(None) => Err("Username not valid UTF-8".ioerr()),
None => Err("Cannot obtain username".ioerr()),
}
}
fn obtain_password() -> io::Result<Pass> {
let dir = storage_directory()?;
if let Ok(pass) = read_password(&dir) {
return Ok(pass);
}
let pass = generate_password();
write_password(&dir, &pass)
.map_err(|e| format!("Cannot write password to home directory: {}", e).ioerr())?;
Ok(pass)
}
fn read_password(data_dir: &Path) -> io::Result<Pass> {
fs::read(data_dir.join(PASSWORD_FILE_NAME))
.and_then(|mut v| {
// Trim final newline if there is one
if v.last() == Some(&b'\n') {
v.pop();
}
String::from_utf8(v).map(Pass).iores()
})
}
fn write_password(data_dir: &Path, pass: &Pass) -> io::Result<()> {
fs::create_dir_all(data_dir)?;
fs::write(data_dir.join(PASSWORD_FILE_NAME), pass.as_bytes())
}
fn storage_directory() -> io::Result<PathBuf> {
dirs::data_dir()
.map(|mut dir| { dir.push(PASSWORD_FILE_SUBDIR); dir })
.or_else(|| dirs::home_dir()
.map(|mut dir| {
dir.push(PASSWORD_FILE_SUBDIR_HOME); dir
}))
.ok_or("Cannot determine home directory".ioerr())
}
fn generate_password() -> Pass {
Pass(secure_rng()
.sample_iter(rand::distributions::Alphanumeric)
.take(32)
.collect::<String>()
)
}
// Wrapper for thread_rng() that statically enforces that the generator is CryptoRng.
fn secure_rng() -> impl Rng + CryptoRng {
rand::thread_rng()
}
|