mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-26 07:50:02 +02:00 
			
		
		
		
	SMTP integration, send password hint by email.
This commit is contained in:
		
							
								
								
									
										7
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								.env
									
									
									
									
									
								
							| @@ -41,3 +41,10 @@ | ||||
| # ROCKET_ADDRESS=0.0.0.0 # Enable this to test mobile app | ||||
| # ROCKET_PORT=8000 | ||||
| # ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"} | ||||
|  | ||||
| ## Mail specific settings, if SMTP_HOST is specified, SMTP_USERNAME and SMTP_PASSWORD are mandatory | ||||
| # SMTP_HOST=smtp.domain.tld | ||||
| # SMTP_PORT=587 | ||||
| # SMTP_SSL=true | ||||
| # SMTP_USERNAME=username | ||||
| # SMTP_PASSWORD=password | ||||
| @@ -58,6 +58,10 @@ lazy_static = "1.0.1" | ||||
| num-traits = "0.2.5" | ||||
| num-derive = "0.2.2" | ||||
|  | ||||
| lettre = "0.8.2" | ||||
| lettre_email = "0.8.2" | ||||
| native-tls = "0.1.5" | ||||
|  | ||||
| [patch.crates-io] | ||||
|  # Make jwt use ring 0.11, to match rocket | ||||
| jsonwebtoken = { path = "libs/jsonwebtoken" } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ use db::models::*; | ||||
|  | ||||
| use api::{PasswordData, JsonResult, EmptyResult, JsonUpcase, NumberOrString}; | ||||
| use auth::Headers; | ||||
| use mail; | ||||
|  | ||||
| use CONFIG; | ||||
|  | ||||
| @@ -258,15 +259,23 @@ struct PasswordHintData { | ||||
| fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResult { | ||||
|     let data: PasswordHintData = data.into_inner().data; | ||||
|  | ||||
|     if !CONFIG.show_password_hint { | ||||
|         return Ok(()) | ||||
|     let user = User::find_by_mail(&data.Email, &conn); | ||||
|     if user.is_none() { | ||||
|         return Ok(()); | ||||
|     } | ||||
|  | ||||
|     match User::find_by_mail(&data.Email, &conn) { | ||||
|         Some(user) => { | ||||
|             let hint = user.password_hint.to_owned().unwrap_or_default(); | ||||
|             err!(format!("Your password hint is: {}", hint)) | ||||
|         }, | ||||
|         None => Ok(()), | ||||
|     let user = user.unwrap(); | ||||
|     let hint = user.password_hint.to_owned().unwrap_or("You don't have any...".to_string()); | ||||
|  | ||||
|     if let Some(ref mail_config) = CONFIG.mail { | ||||
|         if let Err(e) = mail::send_password_hint(&user.email, &hint, mail_config) { | ||||
|             err!(format!("There have been a problem sending the email: {}", e)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if !CONFIG.show_password_hint { | ||||
|         err!(format!("Your password hint is: {}", &hint)); | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
							
								
								
									
										47
									
								
								src/mail.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/mail.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| use std::error::Error; | ||||
| use native_tls::TlsConnector; | ||||
| use native_tls::{Protocol}; | ||||
| use lettre::{EmailTransport, SmtpTransport, ClientTlsParameters, ClientSecurity}; | ||||
| use lettre::smtp::{ConnectionReuseParameters, SmtpTransportBuilder}; | ||||
| use lettre::smtp::authentication::{Credentials, Mechanism}; | ||||
| use lettre_email::EmailBuilder; | ||||
|  | ||||
| use MailConfig; | ||||
|  | ||||
| fn mailer(config: &MailConfig) -> SmtpTransport { | ||||
|     let client_security = if config.smtp_ssl { | ||||
|         let mut tls_builder = TlsConnector::builder().unwrap(); | ||||
|         tls_builder.supported_protocols(&[ | ||||
|             Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12 | ||||
|         ]).unwrap(); | ||||
|  | ||||
|         ClientSecurity::Required( | ||||
|             ClientTlsParameters::new(config.smtp_host.to_owned(), tls_builder.build().unwrap()) | ||||
|         ) | ||||
|     } else { | ||||
|         ClientSecurity::None | ||||
|     }; | ||||
|  | ||||
|     SmtpTransportBuilder::new((config.smtp_host.to_owned().as_str(), config.smtp_port), client_security) | ||||
|         .unwrap() | ||||
|         .credentials(Credentials::new(config.smtp_username.to_owned(), config.smtp_password.to_owned())) | ||||
|         .authentication_mechanism(Mechanism::Login) | ||||
|         .smtp_utf8(true) | ||||
|         .connection_reuse(ConnectionReuseParameters::ReuseUnlimited) | ||||
|         .build() | ||||
| } | ||||
|  | ||||
| pub fn send_password_hint(address: &str, hint: &str, config: &MailConfig) -> Result<(), String> { | ||||
|     let email = EmailBuilder::new() | ||||
|         .to(address) | ||||
|         .from((config.smtp_from.to_owned(), "Bitwarden-rs")) | ||||
|         .subject("Your Master Password Hint") | ||||
|         .body(hint) | ||||
|         .build().unwrap(); | ||||
|  | ||||
|     match mailer(config).send(&email) { | ||||
|         Ok(_) => Ok(()), | ||||
|         Err(e) => Err(e.description().to_string()), | ||||
|     } | ||||
| } | ||||
|          | ||||
							
								
								
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -26,6 +26,9 @@ extern crate lazy_static; | ||||
| #[macro_use] | ||||
| extern crate num_derive; | ||||
| extern crate num_traits; | ||||
| extern crate lettre; | ||||
| extern crate lettre_email; | ||||
| extern crate native_tls; | ||||
|  | ||||
| use std::{env, path::Path, process::{exit, Command}}; | ||||
| use rocket::Rocket; | ||||
| @@ -37,6 +40,7 @@ mod api; | ||||
| mod db; | ||||
| mod crypto; | ||||
| mod auth; | ||||
| mod mail; | ||||
|  | ||||
| fn init_rocket() -> Rocket { | ||||
|     rocket::ignite() | ||||
| @@ -155,10 +159,10 @@ lazy_static! { | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct MailConfig { | ||||
|     reply_to_email: Option<String>, | ||||
|     smtp_host: String, | ||||
|     smtp_port: u16, | ||||
|     smtp_ssl: bool, | ||||
|     smtp_from: String, | ||||
|     smtp_username: String, | ||||
|     smtp_password: String, | ||||
| } | ||||
| @@ -172,22 +176,23 @@ impl MailConfig { | ||||
|             return None | ||||
|         } | ||||
|  | ||||
|         let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(false); | ||||
|         let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(true); | ||||
|         let smtp_port = util::parse_option_string(env::var("SMTP_PORT").ok()) | ||||
|             .unwrap_or_else(|| { | ||||
|                 if smtp_ssl { | ||||
|                     465u16 | ||||
|                     587u16 | ||||
|                 } else { | ||||
|                     25u16 | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         Some(MailConfig { | ||||
|             reply_to_email: util::parse_option_string(env::var("REPLY_TO_EMAIL").ok()), | ||||
|             smtp_host: smtp_host.unwrap(), | ||||
|             smtp_port: smtp_port, | ||||
|             smtp_ssl: smtp_ssl, | ||||
|             // If username or password is not specified, and SMTP support seems to be wanted, | ||||
|             smtp_from: util::parse_option_string(env::var("SMTP_FROM").ok()) | ||||
|                 .unwrap_or("bitwarden@localhost".to_string()), | ||||
|             // If username or password is not specified and SMTP support seems to be wanted, | ||||
|             // don't let the app start: the configuration is clearly incomplete. | ||||
|             smtp_username: util::parse_option_string(env::var("SMTP_USERNAME").ok()) | ||||
|                 .unwrap_or_else(|| { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user