You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Actually send emails
This commit is contained in:
@ -6,13 +6,15 @@ edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.52"
|
||||
async-trait = "0.1.52"
|
||||
tokio = { version = "1.15.0", features = ["macros"] }
|
||||
|
||||
mas-templates = { path = "../templates" }
|
||||
anyhow = "1.0.52"
|
||||
async-trait = "0.1.52"
|
||||
mas-config = { path = "../config" }
|
||||
tracing = "0.1.29"
|
||||
|
||||
[dependencies.lettre]
|
||||
version = "0.10.0-rc.4"
|
||||
default-features = false
|
||||
features = ["tokio1-rustls-tls", "hostname", "builder", "tracing", "pool"]
|
||||
features = ["tokio1-rustls-tls", "hostname", "builder", "tracing", "pool", "smtp-transport"]
|
||||
|
@ -12,24 +12,130 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use lettre::{
|
||||
address::Envelope,
|
||||
message::{Mailbox, MessageBuilder, MultiPart},
|
||||
AsyncTransport, Message,
|
||||
transport::smtp::{authentication::Credentials, AsyncSmtpTransport},
|
||||
AsyncTransport, Message, Tokio1Executor,
|
||||
};
|
||||
use mas_config::{EmailSmtpMode, EmailTransportConfig};
|
||||
use mas_templates::{EmailVerificationContext, Templates};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct MailTransport {
|
||||
inner: Arc<MailTransportInner>,
|
||||
}
|
||||
|
||||
enum MailTransportInner {
|
||||
Blackhole,
|
||||
Smtp(AsyncSmtpTransport<Tokio1Executor>),
|
||||
}
|
||||
|
||||
impl TryFrom<&EmailTransportConfig> for MailTransport {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(config: &EmailTransportConfig) -> Result<Self, Self::Error> {
|
||||
let inner = match config {
|
||||
EmailTransportConfig::Blackhole => MailTransportInner::Blackhole,
|
||||
EmailTransportConfig::Smtp {
|
||||
mode,
|
||||
hostname,
|
||||
credentials,
|
||||
port,
|
||||
} => {
|
||||
let mut t = match mode {
|
||||
EmailSmtpMode::Plain => {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(hostname)
|
||||
}
|
||||
EmailSmtpMode::StartTls => {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(hostname)?
|
||||
}
|
||||
EmailSmtpMode::Tls => AsyncSmtpTransport::<Tokio1Executor>::relay(hostname)?,
|
||||
};
|
||||
|
||||
if let Some(credentials) = credentials {
|
||||
t = t.credentials(Credentials::new(
|
||||
credentials.username.clone(),
|
||||
credentials.password.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(port) = port {
|
||||
t = t.port(*port);
|
||||
}
|
||||
|
||||
MailTransportInner::Smtp(t.build())
|
||||
}
|
||||
};
|
||||
let inner = Arc::new(inner);
|
||||
Ok(Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl MailTransport {
|
||||
pub async fn test_connection(&self) -> anyhow::Result<()> {
|
||||
match self.inner.as_ref() {
|
||||
MailTransportInner::Blackhole => {}
|
||||
MailTransportInner::Smtp(t) => {
|
||||
t.test_connection().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MailTransportInner {
|
||||
fn default() -> Self {
|
||||
Self::Blackhole
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AsyncTransport for MailTransport {
|
||||
type Ok = ();
|
||||
type Error = anyhow::Error;
|
||||
|
||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
match self.inner.as_ref() {
|
||||
MailTransportInner::Blackhole => {
|
||||
tracing::warn!(
|
||||
?envelope,
|
||||
"An email was supposed to be sent but no email backend is configured"
|
||||
);
|
||||
}
|
||||
MailTransportInner::Smtp(t) => {
|
||||
t.send_raw(envelope, email).await?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mailer {
|
||||
templates: Templates,
|
||||
transport: MailTransport,
|
||||
from: Mailbox,
|
||||
reply_to: Mailbox,
|
||||
}
|
||||
|
||||
impl Mailer {
|
||||
pub fn new<T>(templates: &Templates, from: Mailbox, reply_to: Mailbox) -> Self {
|
||||
pub fn new(
|
||||
templates: &Templates,
|
||||
transport: &MailTransport,
|
||||
from: &Mailbox,
|
||||
reply_to: &Mailbox,
|
||||
) -> Self {
|
||||
Self {
|
||||
templates: templates.clone(),
|
||||
from,
|
||||
reply_to,
|
||||
transport: transport.clone(),
|
||||
from: from.clone(),
|
||||
reply_to: reply_to.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,23 +162,23 @@ impl Mailer {
|
||||
|
||||
let multipart = MultiPart::alternative_plain_html(plain, html);
|
||||
|
||||
let message = self.base_message().to(to).multipart(multipart)?;
|
||||
let message = self
|
||||
.base_message()
|
||||
// TODO: template/localize this
|
||||
.subject("Verify your email address")
|
||||
.to(to)
|
||||
.multipart(multipart)?;
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn send_verification_email<T>(
|
||||
pub async fn send_verification_email(
|
||||
&self,
|
||||
transport: &T,
|
||||
to: Mailbox,
|
||||
context: &EmailVerificationContext,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
T: AsyncTransport + Send + Sync,
|
||||
T::Error: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
) -> anyhow::Result<()> {
|
||||
let message = self.prepare_verification_email(to, context).await?;
|
||||
transport.send(message).await?;
|
||||
self.transport.send(message).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user