| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
use crate::db::accounts; |
| 10 |
use crate::Shared; |
| 11 |
use std::path::PathBuf; |
| 12 |
|
| 13 |
pub fn path(app: &Shared) -> PathBuf { |
| 14 |
app.config.data.join("setup-code") |
| 15 |
} |
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
pub async fn ensure(app: &Shared) -> anyhow::Result<()> { |
| 20 |
let file = path(app); |
| 21 |
if accounts::account_count(&app.db).await? > 0 { |
| 22 |
let _ = std::fs::remove_file(&file); |
| 23 |
return Ok(()); |
| 24 |
} |
| 25 |
let code = match expected(app) { |
| 26 |
Some(code) => code, |
| 27 |
None => { |
| 28 |
let code = crate::auth::random_hex(12); |
| 29 |
write_private(&file, &code)?; |
| 30 |
code |
| 31 |
} |
| 32 |
}; |
| 33 |
tracing::warn!( |
| 34 |
"this hub has no administrator yet: register the first account at {}/register with setup code {code} (also in {}); it works once", |
| 35 |
app.config.base_url(), |
| 36 |
file.display() |
| 37 |
); |
| 38 |
Ok(()) |
| 39 |
} |
| 40 |
|
| 41 |
|
| 42 |
pub fn expected(app: &Shared) -> Option<String> { |
| 43 |
let configured = app.config.registration.setup_code.trim(); |
| 44 |
if !configured.is_empty() { |
| 45 |
return Some(configured.to_string()); |
| 46 |
} |
| 47 |
std::fs::read_to_string(path(app)).ok().map(|s| s.trim().to_string()).filter(|s| !s.is_empty()) |
| 48 |
} |
| 49 |
|
| 50 |
|
| 51 |
pub fn matches(app: &Shared, given: &str) -> bool { |
| 52 |
match expected(app) { |
| 53 |
Some(code) => { |
| 54 |
let (a, b) = (code.as_bytes(), given.trim().as_bytes()); |
| 55 |
a.len() == b.len() && a.iter().zip(b).fold(0u8, |acc, (x, y)| acc | (x ^ y)) == 0 |
| 56 |
} |
| 57 |
None => false, |
| 58 |
} |
| 59 |
} |
| 60 |
|
| 61 |
|
| 62 |
pub fn spend(app: &Shared) { |
| 63 |
let _ = std::fs::remove_file(path(app)); |
| 64 |
} |
| 65 |
|
| 66 |
fn write_private(file: &std::path::Path, code: &str) -> std::io::Result<()> { |
| 67 |
use std::io::Write; |
| 68 |
let mut options = std::fs::OpenOptions::new(); |
| 69 |
options.write(true).create(true).truncate(true); |
| 70 |
#[cfg(unix)] |
| 71 |
{ |
| 72 |
use std::os::unix::fs::OpenOptionsExt; |
| 73 |
options.mode(0o600); |
| 74 |
} |
| 75 |
let mut f = options.open(file)?; |
| 76 |
writeln!(f, "{code}") |
| 77 |
} |