Microservice to bring 2FA to self hosted PDSes
91
fork

Configure Feed

Select the types of activity you want to include in your feed.

added support for sending emails with sendmail

authored by

Kuba Suder and committed by tangled.org 500ac33c 750a68c0

+276 -8
+230 -1
Cargo.lock
··· 98 98 ] 99 99 100 100 [[package]] 101 + name = "async-executor" 102 + version = "1.13.3" 103 + source = "registry+https://github.com/rust-lang/crates.io-index" 104 + checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" 105 + dependencies = [ 106 + "async-task", 107 + "concurrent-queue", 108 + "fastrand", 109 + "futures-lite", 110 + "pin-project-lite", 111 + "slab", 112 + ] 113 + 114 + [[package]] 115 + name = "async-global-executor" 116 + version = "2.4.1" 117 + source = "registry+https://github.com/rust-lang/crates.io-index" 118 + checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" 119 + dependencies = [ 120 + "async-channel 2.5.0", 121 + "async-executor", 122 + "async-io", 123 + "async-lock", 124 + "blocking", 125 + "futures-lite", 126 + "once_cell", 127 + ] 128 + 129 + [[package]] 130 + name = "async-io" 131 + version = "2.6.0" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" 134 + dependencies = [ 135 + "autocfg", 136 + "cfg-if", 137 + "concurrent-queue", 138 + "futures-io", 139 + "futures-lite", 140 + "parking", 141 + "polling", 142 + "rustix 1.1.2", 143 + "slab", 144 + "windows-sys 0.61.2", 145 + ] 146 + 147 + [[package]] 148 + name = "async-lock" 149 + version = "3.4.1" 150 + source = "registry+https://github.com/rust-lang/crates.io-index" 151 + checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" 152 + dependencies = [ 153 + "event-listener 5.4.1", 154 + "event-listener-strategy", 155 + "pin-project-lite", 156 + ] 157 + 158 + [[package]] 159 + name = "async-process" 160 + version = "2.5.0" 161 + source = "registry+https://github.com/rust-lang/crates.io-index" 162 + checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" 163 + dependencies = [ 164 + "async-channel 2.5.0", 165 + "async-io", 166 + "async-lock", 167 + "async-signal", 168 + "async-task", 169 + "blocking", 170 + "cfg-if", 171 + "event-listener 5.4.1", 172 + "futures-lite", 173 + "rustix 1.1.2", 174 + ] 175 + 176 + [[package]] 177 + name = "async-signal" 178 + version = "0.2.13" 179 + source = "registry+https://github.com/rust-lang/crates.io-index" 180 + checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" 181 + dependencies = [ 182 + "async-io", 183 + "async-lock", 184 + "atomic-waker", 185 + "cfg-if", 186 + "futures-core", 187 + "futures-io", 188 + "rustix 1.1.2", 189 + "signal-hook-registry", 190 + "slab", 191 + "windows-sys 0.61.2", 192 + ] 193 + 194 + [[package]] 195 + name = "async-std" 196 + version = "1.13.2" 197 + source = "registry+https://github.com/rust-lang/crates.io-index" 198 + checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" 199 + dependencies = [ 200 + "async-channel 1.9.0", 201 + "async-global-executor", 202 + "async-io", 203 + "async-lock", 204 + "async-process", 205 + "crossbeam-utils", 206 + "futures-channel", 207 + "futures-core", 208 + "futures-io", 209 + "futures-lite", 210 + "gloo-timers", 211 + "kv-log-macro", 212 + "log", 213 + "memchr", 214 + "once_cell", 215 + "pin-project-lite", 216 + "pin-utils", 217 + "slab", 218 + "wasm-bindgen-futures", 219 + ] 220 + 221 + [[package]] 222 + name = "async-task" 223 + version = "4.7.1" 224 + source = "registry+https://github.com/rust-lang/crates.io-index" 225 + checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 226 + 227 + [[package]] 101 228 name = "async-trait" 102 229 version = "0.1.89" 103 230 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 287 414 checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 288 415 dependencies = [ 289 416 "generic-array", 417 + ] 418 + 419 + [[package]] 420 + name = "blocking" 421 + version = "1.6.2" 422 + source = "registry+https://github.com/rust-lang/crates.io-index" 423 + checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" 424 + dependencies = [ 425 + "async-channel 2.5.0", 426 + "async-task", 427 + "futures-io", 428 + "futures-lite", 429 + "piper", 290 430 ] 291 431 292 432 [[package]] ··· 1009 1149 1010 1150 [[package]] 1011 1151 name = "event-listener" 1152 + version = "2.5.3" 1153 + source = "registry+https://github.com/rust-lang/crates.io-index" 1154 + checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 1155 + 1156 + [[package]] 1157 + name = "event-listener" 1012 1158 version = "5.4.1" 1013 1159 source = "registry+https://github.com/rust-lang/crates.io-index" 1014 1160 checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 1015 1161 dependencies = [ 1016 1162 "concurrent-queue", 1017 1163 "parking", 1164 + "pin-project-lite", 1165 + ] 1166 + 1167 + [[package]] 1168 + name = "event-listener-strategy" 1169 + version = "0.5.4" 1170 + source = "registry+https://github.com/rust-lang/crates.io-index" 1171 + checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 1172 + dependencies = [ 1173 + "event-listener 5.4.1", 1018 1174 "pin-project-lite", 1019 1175 ] 1020 1176 ··· 1302 1458 ] 1303 1459 1304 1460 [[package]] 1461 + name = "gloo-timers" 1462 + version = "0.3.0" 1463 + source = "registry+https://github.com/rust-lang/crates.io-index" 1464 + checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" 1465 + dependencies = [ 1466 + "futures-channel", 1467 + "futures-core", 1468 + "js-sys", 1469 + "wasm-bindgen", 1470 + ] 1471 + 1472 + [[package]] 1305 1473 name = "governor" 1306 1474 version = "0.10.4" 1307 1475 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1463 1631 version = "0.5.0" 1464 1632 source = "registry+https://github.com/rust-lang/crates.io-index" 1465 1633 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1634 + 1635 + [[package]] 1636 + name = "hermit-abi" 1637 + version = "0.5.2" 1638 + source = "registry+https://github.com/rust-lang/crates.io-index" 1639 + checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 1466 1640 1467 1641 [[package]] 1468 1642 name = "hex" ··· 2050 2224 ] 2051 2225 2052 2226 [[package]] 2227 + name = "kv-log-macro" 2228 + version = "1.0.7" 2229 + source = "registry+https://github.com/rust-lang/crates.io-index" 2230 + checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 2231 + dependencies = [ 2232 + "log", 2233 + ] 2234 + 2235 + [[package]] 2053 2236 name = "langtag" 2054 2237 version = "0.4.0" 2055 2238 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2075 2258 source = "registry+https://github.com/rust-lang/crates.io-index" 2076 2259 checksum = "9e13e10e8818f8b2a60f52cb127041d388b89f3a96a62be9ceaffa22262fef7f" 2077 2260 dependencies = [ 2261 + "async-std", 2078 2262 "async-trait", 2079 2263 "base64", 2080 2264 "chumsky", ··· 2594 2778 "tower_governor", 2595 2779 "tracing", 2596 2780 "tracing-subscriber", 2781 + "url", 2597 2782 "urlencoding", 2598 2783 ] 2599 2784 ··· 2688 2873 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 2689 2874 2690 2875 [[package]] 2876 + name = "piper" 2877 + version = "0.2.4" 2878 + source = "registry+https://github.com/rust-lang/crates.io-index" 2879 + checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" 2880 + dependencies = [ 2881 + "atomic-waker", 2882 + "fastrand", 2883 + "futures-io", 2884 + ] 2885 + 2886 + [[package]] 2691 2887 name = "pkcs1" 2692 2888 version = "0.7.5" 2693 2889 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2715 2911 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 2716 2912 2717 2913 [[package]] 2914 + name = "polling" 2915 + version = "3.11.0" 2916 + source = "registry+https://github.com/rust-lang/crates.io-index" 2917 + checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" 2918 + dependencies = [ 2919 + "cfg-if", 2920 + "concurrent-queue", 2921 + "hermit-abi", 2922 + "pin-project-lite", 2923 + "rustix 1.1.2", 2924 + "windows-sys 0.61.2", 2925 + ] 2926 + 2927 + [[package]] 2718 2928 name = "portable-atomic" 2719 2929 version = "1.13.0" 2720 2930 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3206 3416 ] 3207 3417 3208 3418 [[package]] 3419 + name = "rustix" 3420 + version = "1.1.2" 3421 + source = "registry+https://github.com/rust-lang/crates.io-index" 3422 + checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 3423 + dependencies = [ 3424 + "bitflags", 3425 + "errno", 3426 + "libc", 3427 + "linux-raw-sys 0.11.0", 3428 + "windows-sys 0.59.0", 3429 + ] 3430 + 3431 + [[package]] 3209 3432 name = "rustls" 3210 3433 version = "0.23.35" 3211 3434 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3666 3889 "crc", 3667 3890 "crossbeam-queue", 3668 3891 "either", 3669 - "event-listener", 3892 + "event-listener 5.4.1", 3670 3893 "futures-core", 3671 3894 "futures-intrusive", 3672 3895 "futures-io", ··· 4422 4645 version = "0.1.1" 4423 4646 source = "registry+https://github.com/rust-lang/crates.io-index" 4424 4647 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 4648 + 4649 + [[package]] 4650 + name = "value-bag" 4651 + version = "1.12.0" 4652 + source = "registry+https://github.com/rust-lang/crates.io-index" 4653 + checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" 4425 4654 4426 4655 [[package]] 4427 4656 name = "vcpkg"
+1
Cargo.toml
··· 35 35 multibase = "0.9.2" 36 36 reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } 37 37 urlencoding = "2.1" 38 + url = "2.5.7" 38 39 html-escape = "0.2.13" 39 40 josekit = "0.10.3" 40 41 dashmap = "6.1"
+1 -1
src/helpers.rs
··· 17 17 use jacquard_identity::{PublicResolver, resolver::IdentityResolver}; 18 18 use josekit::jwe::alg::direct::DirectJweAlgorithm; 19 19 use lettre::{ 20 - AsyncTransport, Message, 20 + Message, 21 21 message::{MultiPart, SinglePart, header}, 22 22 }; 23 23 use rand::Rng;
+44 -6
src/main.rs
··· 4 4 use crate::xrpc::com_atproto_server::{ 5 5 create_account, create_session, describe_server, get_session, update_email, 6 6 }; 7 + use anyhow::{Result, Context}; 7 8 use axum::{ 8 9 Router, 9 10 body::Body, ··· 18 19 use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor}; 19 20 use jacquard_common::types::did::Did; 20 21 use jacquard_identity::{PublicResolver, resolver::PlcSource}; 21 - use lettre::{AsyncSmtpTransport, Tokio1Executor}; 22 + use lettre::{AsyncTransport, AsyncSmtpTransport, AsyncSendmailTransport, Message, Tokio1Executor}; 22 23 use rand::Rng; 23 24 use rust_embed::RustEmbed; 24 25 use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode}; ··· 36 37 }; 37 38 use tracing::log; 38 39 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 40 + use url::Url; 39 41 40 42 mod auth; 41 43 mod gate; ··· 151 153 account_pool: SqlitePool, 152 154 pds_gatekeeper_pool: SqlitePool, 153 155 reverse_proxy_client: HyperUtilClient, 154 - mailer: AsyncSmtpTransport<Tokio1Executor>, 156 + mailer: Arc<Mailer>, 155 157 template_engine: Engine<Handlebars<'static>>, 156 158 resolver: Arc<PublicResolver>, 157 159 handle_cache: auth::HandleCache, 158 160 app_config: AppConfig, 159 161 } 160 162 163 + pub enum Mailer { 164 + Smtp(AsyncSmtpTransport<Tokio1Executor>), 165 + Sendmail(AsyncSendmailTransport<Tokio1Executor>), 166 + } 167 + 168 + impl Mailer { 169 + pub async fn send(&self, msg: Message) -> Result<()> { 170 + match self { 171 + Mailer::Smtp(m) => { 172 + m.send(msg).await.context("SMTP send failed")?; 173 + Ok(()) 174 + } 175 + Mailer::Sendmail(m) => { 176 + m.send(msg).await.context("sendmail send failed")?; 177 + Ok(()) 178 + } 179 + } 180 + } 181 + } 182 + 183 + fn build_mailer_from_env() -> Result<Mailer> { 184 + let raw = env::var("PDS_EMAIL_SMTP_URL") 185 + .context("PDS_EMAIL_SMTP_URL is not set in your pds.env file")?; 186 + 187 + let url = Url::parse(&raw).context("PDS_EMAIL_SMTP_URL is not a valid URL")?; 188 + 189 + let use_sendmail = url.scheme() == "sendmail" 190 + || url.query_pairs().any(|(k, v)| k == "sendmail" && v == "true"); 191 + 192 + if use_sendmail { 193 + Ok(Mailer::Sendmail(AsyncSendmailTransport::<Tokio1Executor>::new())) 194 + } else { 195 + Ok(Mailer::Smtp( 196 + AsyncSmtpTransport::<Tokio1Executor>::from_url(raw.as_str())? 197 + .build(), 198 + )) 199 + } 200 + } 201 + 161 202 async fn root_handler() -> impl axum::response::IntoResponse { 162 203 let body = r" 163 204 ··· 244 285 .build(HttpConnector::new()); 245 286 246 287 //Emailer set up 247 - let smtp_url = 248 - env::var("PDS_EMAIL_SMTP_URL").expect("PDS_EMAIL_SMTP_URL is not set in your pds.env file"); 288 + let mailer = Arc::new(build_mailer_from_env()?); 249 289 250 - let mailer: AsyncSmtpTransport<Tokio1Executor> = 251 - AsyncSmtpTransport::<Tokio1Executor>::from_url(smtp_url.as_str())?.build(); 252 290 //Email templates setup 253 291 let mut hbs = Handlebars::new(); 254 292