From f026e48733b3573b9fd60ba1222ba5b8dce453f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20DOUIN?= Date: Fri, 5 May 2023 00:04:12 +0200 Subject: [PATCH] improve oauth2 config deserialization and configuration --- Cargo.lock | 12 +++++++----- Cargo.toml | 3 +-- src/config/mod.rs | 2 +- src/config/prelude.rs | 27 +++++++++++++++++---------- src/config/wizard/imap.rs | 19 +++++++++++++++---- src/config/wizard/mod.rs | 2 +- src/domain/account/args.rs | 24 ++++++++++++++++++++---- src/domain/account/handlers.rs | 23 ++++++++++++++++++++--- src/main.rs | 4 ++-- 9 files changed, 84 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 743137e..8f444ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2098,6 +2098,7 @@ dependencies = [ [[package]] name = "pimalaya-email" version = "0.7.1" +source = "git+https://git.sr.ht/~soywod/pimalaya#22db3ec886536897c54b72d4ab7d20beff0ffecc" dependencies = [ "ammonia", "chrono", @@ -2138,6 +2139,7 @@ dependencies = [ [[package]] name = "pimalaya-oauth2" version = "0.0.1" +source = "git+https://git.sr.ht/~soywod/pimalaya#22db3ec886536897c54b72d4ab7d20beff0ffecc" dependencies = [ "log", "oauth2", @@ -2650,22 +2652,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 1.0.104", + "syn 2.0.15", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 94b2242..245ed59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,8 +48,7 @@ dialoguer = "0.10.2" email_address = "0.2.4" env_logger = "0.8" erased-serde = "0.3" -# pimalaya-email = "0.7.1" -pimalaya-email = { path = "/home/soywod/sourcehut/pimalaya/email" } +pimalaya-email = { git = "https://git.sr.ht/~soywod/pimalaya" } indicatif = "0.17" log = "0.4" once_cell = "1.16.0" diff --git a/src/config/mod.rs b/src/config/mod.rs index b22aa9b..78507a1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,6 @@ pub mod args; pub mod config; pub mod prelude; -mod wizard; +pub(crate) mod wizard; pub use config::*; diff --git a/src/config/prelude.rs b/src/config/prelude.rs index db5dcf6..ad36f49 100644 --- a/src/config/prelude.rs +++ b/src/config/prelude.rs @@ -1,7 +1,7 @@ use pimalaya_email::{ folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat, - ImapAuthConfig, MaildirConfig, OAuth2Config, OAuth2Method, OAuth2Scopes, SendmailConfig, - SmtpConfig, + ImapAuthConfig, MaildirConfig, OAuth2ClientSecret, OAuth2Config, OAuth2Method, OAuth2Scopes, + SendmailConfig, SmtpConfig, }; use serde::{Deserialize, Serialize}; use std::{collections::HashSet, path::PathBuf}; @@ -47,7 +47,7 @@ pub struct ImapConfigDef { pub insecure: Option, #[serde(rename = "imap-login")] pub login: String, - #[serde(flatten, with = "ImapAuthConfigDef")] + #[serde(rename = "imap-auth", with = "ImapAuthConfigDef")] pub auth: ImapAuthConfig, #[serde(rename = "imap-notify-cmd")] pub notify_cmd: Option, @@ -57,16 +57,14 @@ pub struct ImapConfigDef { pub watch_cmds: Option>, } -#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[serde(remote = "ImapAuthConfig", rename_all = "kebab-case")] pub enum ImapAuthConfigDef { - #[default] + #[serde(skip)] None, - #[serde(rename = "imap-passwd")] - Passwd(String), - #[serde(rename = "imap-passwd-cmd")] + RawPasswd(String), PasswdCmd(String), - #[serde(rename = "imap-oauth2", with = "OAuth2ConfigDef")] + #[serde(with = "OAuth2ConfigDef", rename = "oauth2")] OAuth2(OAuth2Config), } @@ -76,7 +74,8 @@ pub struct OAuth2ConfigDef { #[serde(with = "OAuth2MethodDef")] pub method: OAuth2Method, pub client_id: String, - pub client_secret: String, + #[serde(with = "OAuth2ClientSecretDef")] + pub client_secret: OAuth2ClientSecret, pub auth_url: String, pub token_url: String, #[serde(flatten, with = "OAuth2ScopesDef")] @@ -85,6 +84,14 @@ pub struct OAuth2ConfigDef { pub pkce: bool, } +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[serde(remote = "OAuth2ClientSecret", rename_all = "kebab-case")] +pub enum OAuth2ClientSecretDef { + Raw(String), + Cmd(String), + Keyring, +} + #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[serde(remote = "OAuth2Method", rename_all = "lowercase")] pub enum OAuth2MethodDef { diff --git a/src/config/wizard/imap.rs b/src/config/wizard/imap.rs index d251fd0..c6afa41 100644 --- a/src/config/wizard/imap.rs +++ b/src/config/wizard/imap.rs @@ -1,10 +1,13 @@ -use super::{SECURITY_PROTOCOLS, THEME}; -use crate::account::{ - DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig, -}; use anyhow::Result; use dialoguer::{Input, Select}; use pimalaya_email::ImapConfig; +use std::io; + +use crate::account::{ + DeserializedAccountConfig, DeserializedBaseAccountConfig, DeserializedImapAccountConfig, +}; + +use super::{SECURITY_PROTOCOLS, THEME}; #[cfg(feature = "imap-backend")] pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result { @@ -56,3 +59,11 @@ pub(crate) fn configure(base: DeserializedBaseAccountConfig) -> Result io::Result { + Input::with_theme(&*THEME) + .with_prompt("Enter your OAuth 2.0 client secret:") + .report(false) + .interact() +} diff --git a/src/config/wizard/mod.rs b/src/config/wizard/mod.rs index cc58608..443ac81 100644 --- a/src/config/wizard/mod.rs +++ b/src/config/wizard/mod.rs @@ -1,5 +1,5 @@ #[cfg(feature = "imap-backend")] -mod imap; +pub(crate) mod imap; mod maildir; #[cfg(feature = "notmuch-backend")] mod notmuch; diff --git a/src/domain/account/args.rs b/src/domain/account/args.rs index 74a520e..694cf7a 100644 --- a/src/domain/account/args.rs +++ b/src/domain/account/args.rs @@ -10,12 +10,14 @@ use crate::{folder, ui::table}; const ARG_ACCOUNT: &str = "account"; const ARG_DRY_RUN: &str = "dry-run"; +const ARG_RESET: &str = "reset"; const CMD_ACCOUNTS: &str = "accounts"; const CMD_CONFIGURE: &str = "configure"; const CMD_LIST: &str = "list"; const CMD_SYNC: &str = "sync"; type DryRun = bool; +type Reset = bool; /// Represents the account commands. #[derive(Debug, PartialEq, Eq)] @@ -25,7 +27,7 @@ pub enum Cmd { /// Represents the sync account command. Sync(Option, DryRun), /// Configure the current selected account. - Configure, + Configure(Reset), } /// Represents the account command matcher. @@ -54,9 +56,10 @@ pub fn matches(m: &ArgMatches) -> Result> { info!("list accounts subcommand matched"); let max_table_width = table::args::parse_max_width(m); Some(Cmd::List(max_table_width)) - } else if let Some(_) = m.subcommand_matches(CMD_CONFIGURE) { + } else if let Some(m) = m.subcommand_matches(CMD_CONFIGURE) { info!("configure account subcommand matched"); - Some(Cmd::Configure) + let reset = parse_reset_flag(m); + Some(Cmd::Configure(reset)) } else { info!("no account subcommand matched, falling back to subcommand list"); Some(Cmd::List(None)) @@ -88,7 +91,8 @@ pub fn subcmd() -> Command { .arg(dry_run()), Command::new(CMD_CONFIGURE) .about("Configure the current selected account") - .aliases(["config", "conf", "cfg"]), + .aliases(["config", "conf", "cfg"]) + .arg(reset_flag()), ]) } @@ -126,3 +130,15 @@ Changes can be visualized with the RUST_LOG=trace environment variable.", pub fn parse_dry_run_arg(m: &ArgMatches) -> bool { m.get_flag(ARG_DRY_RUN) } + +pub fn reset_flag() -> Arg { + Arg::new(ARG_RESET) + .help("Reset the configuration") + .short('r') + .long("reset") + .action(ArgAction::SetTrue) +} + +pub fn parse_reset_flag(m: &ArgMatches) -> bool { + m.get_flag(ARG_RESET) +} diff --git a/src/domain/account/handlers.rs b/src/domain/account/handlers.rs index 5b3eb92..79f7921 100644 --- a/src/domain/account/handlers.rs +++ b/src/domain/account/handlers.rs @@ -11,15 +11,32 @@ use pimalaya_email::{ }; use crate::{ - config::DeserializedConfig, + config::{wizard::imap::configure_oauth2_client_secret, DeserializedConfig}, printer::{PrintTableOpts, Printer}, Accounts, }; /// Configure the current selected account -pub fn configure(account_config: &AccountConfig, backend_config: &BackendConfig) -> Result<()> { +pub fn configure( + account_config: &AccountConfig, + backend_config: &BackendConfig, + reset: bool, +) -> Result<()> { info!("entering the configure account handler"); - backend_config.configure(&account_config.name)?; + match backend_config { + BackendConfig::None => (), + BackendConfig::Maildir(_) => (), + #[cfg(feature = "imap-backend")] + BackendConfig::Imap(imap_config) => { + imap_config.auth.configure( + &account_config.name, + reset, + configure_oauth2_client_secret, + )?; + } + #[cfg(feature = "notmuch-backend")] + BackendConfig::Notmuch(config) => (), + }; println!("Account {} configured!", account_config.name); Ok(()) } diff --git a/src/main.rs b/src/main.rs index e079c84..f6fff46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,8 +137,8 @@ fn main() -> Result<()> { backend.close()?; return Ok(()); } - Some(account::args::Cmd::Configure) => { - return account::handlers::configure(&account_config, &backend_config); + Some(account::args::Cmd::Configure(reset)) => { + return account::handlers::configure(&account_config, &backend_config, reset); } _ => (), }