aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2023-12-08 19:06:12 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2023-12-08 19:06:12 +0100
commit23f918fd0edb224668fb775c770075eb4f44ce4d (patch)
tree7d7a22c144fe57fe7b47f12c99ee6e37aad3cd93
parent532c99f3d30ab8adc0963f0814ce3151e1b61caf (diff)
downloadaerogramme-23f918fd0edb224668fb775c770075eb4f44ce4d.tar.gz
aerogramme-23f918fd0edb224668fb775c770075eb4f44ce4d.zip
implement account create
-rw-r--r--src/config.rs24
-rw-r--r--src/main.rs65
2 files changed, 78 insertions, 11 deletions
diff --git a/src/config.rs b/src/config.rs
index 876091f..506640f 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,5 +1,5 @@
use std::collections::HashMap;
-use std::io::Read;
+use std::io::{Read, Write};
use std::net::SocketAddr;
use std::path::PathBuf;
@@ -132,6 +132,18 @@ pub struct UserEntry {
}
#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct SetupEntry {
+ #[serde(default)]
+ pub email_addresses: Vec<String>,
+
+ #[serde(default)]
+ pub clear_password: Option<String>,
+
+ #[serde(flatten)]
+ pub storage: StaticStorage,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "role")]
pub enum AnyConfig {
Companion(CompanionConfig),
@@ -150,6 +162,16 @@ pub fn read_config<T: serde::de::DeserializeOwned>(config_file: PathBuf) -> Resu
Ok(toml::from_str(&config)?)
}
+pub fn write_config<T: Serialize>(config_file: PathBuf, config: &T) -> Result<()> {
+ let mut file = std::fs::OpenOptions::new()
+ .write(true)
+ .open(config_file.as_path())?;
+
+ file.write_all(toml::to_string(config)?.as_bytes())?;
+
+ Ok(())
+}
+
fn default_mail_attr() -> String {
"mail".into()
}
diff --git a/src/main.rs b/src/main.rs
index 7b567bc..679204d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,11 +14,12 @@ mod storage;
use std::path::PathBuf;
-use anyhow::Result;
+use anyhow::{bail, Result, Context};
use clap::{Parser, Subcommand};
use config::*;
use server::Server;
+use login::{static_provider::*, *};
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
@@ -57,23 +58,28 @@ enum CompanionCommand {
enum ProviderCommand {
/// Runs the IMAP+LMTP server daemon
Daemon,
+ /// Reload the daemon
Reload,
+ /// Manage static accounts
#[clap(subcommand)]
Account(AccountManagement),
}
#[derive(Subcommand, Debug)]
enum AccountManagement {
+ /// Add an account
Add {
#[clap(short, long)]
login: String,
#[clap(short, long)]
setup: PathBuf,
},
+ /// Delete an account
Delete {
#[clap(short, long)]
login: String,
},
+ /// Change password for a given account
ChangePassword {
#[clap(short, long)]
login: String
@@ -98,7 +104,7 @@ async fn main() -> Result<()> {
let args = Args::parse();
let any_config = read_config(args.config_file)?;
- match (args.command, any_config) {
+ match (&args.command, any_config) {
(Command::Companion(subcommand), AnyConfig::Companion(config)) => match subcommand {
CompanionCommand::Daemon => {
let server = Server::from_companion_config(config).await?;
@@ -112,7 +118,7 @@ async fn main() -> Result<()> {
},
CompanionCommand::Account(cmd) => {
let user_file = config.users.user_list;
- account_management(cmd, user_file);
+ account_management(&args.command, cmd, user_file)?;
}
},
(Command::Provider(subcommand), AnyConfig::Provider(config)) => match subcommand {
@@ -128,7 +134,7 @@ async fn main() -> Result<()> {
UserManagement::Static(conf) => conf.user_list,
UserManagement::Ldap(_) => panic!("LDAP account management is not supported from Aerogramme.")
};
- account_management(cmd, user_file);
+ account_management(&args.command, cmd, user_file)?;
}
},
(Command::Provider(_), AnyConfig::Companion(_)) => {
@@ -142,16 +148,55 @@ async fn main() -> Result<()> {
Ok(())
}
-fn account_management(cmd: AccountManagement, users: PathBuf) {
+fn account_management(root: &Command, cmd: &AccountManagement, users: PathBuf) -> Result<()> {
+ let mut ulist: UserList = read_config(users.clone())?;
+
match cmd {
- Add => {
- unimplemented!();
+ AccountManagement::Add { login, setup } => {
+ tracing::debug!(user=login, "will-create");
+ let stp: SetupEntry = read_config(setup.clone())?;
+ tracing::debug!(user=login, "loaded setup entry");
+ let crypto_root = match root {
+ Command::Provider(_) => CryptographyRoot::PasswordProtected,
+ Command::Companion(_) => {
+ // @TODO use keyring by default instead of inplace in the future
+ // @TODO generate keys
+ CryptographyRoot::InPlace {
+ master_key: "".to_string(),
+ secret_key: "".to_string(),
+ }
+ }
+ };
+
+ let password = match stp.clear_password {
+ Some(pwd) => pwd,
+ None => {
+ let password = rpassword::prompt_password("Enter password: ")?;
+ let password_confirm = rpassword::prompt_password("Confirm password: ")?;
+ if password != password_confirm {
+ bail!("Passwords don't match.");
+ }
+ password
+ }
+ };
+ let hash = hash_password(password.as_str()).context("unable to hash password")?;
+
+ ulist.insert(login.clone(), UserEntry {
+ email_addresses: stp.email_addresses,
+ password: hash,
+ crypto_root,
+ storage: stp.storage,
+ });
+
+ write_config(users.clone(), &ulist)?;
},
- Delete => {
+ AccountManagement::Delete { login } => {
unimplemented!();
},
- ChangePassword => {
+ AccountManagement::ChangePassword { login } => {
unimplemented!();
},
- }
+ };
+
+ Ok(())
}