pub mod agreements; pub mod authority; pub mod authority_store; pub mod identity; pub mod internal_identity; pub mod lifecycle; pub mod local; pub mod login; pub mod logout; pub mod me; pub mod notifications; pub mod password; pub mod pow; pub mod preferences; pub mod profile; pub mod register; pub mod reports; pub mod sessions; pub mod suspensions; pub mod two_factor; pub mod users; use config::auth::AuthConfig; use config::pow::PowConfig; use serde::Serialize; use crate::cache::Cache; use crate::db::DbConn; use crate::email::MailPublisher; use crate::errors::{AuthError, AuthResult}; use crate::events::{Labels, label}; #[derive(Clone)] pub struct AuthService { pub(crate) accounts: crate::Accounts, config: AuthSettings, mail: Option, sessions: crate::session::RedisStore, } impl AuthService { pub fn new( accounts: crate::Accounts, config: AuthSettings, mail: Option, sessions: crate::session::RedisStore, ) -> Self { Self { accounts, config, mail, sessions, } } pub fn config(&self) -> &AuthSettings { &self.config } pub fn db(&self) -> &DbConn { self.accounts.db() } pub fn cache(&self) -> &Cache { self.accounts.cache() } pub fn mail(&self) -> Option<&MailPublisher> { self.mail.as_ref() } pub fn auth(&self) -> &AuthConfig { &self.config.auth } pub fn pow(&self) -> &PowConfig { &self.config.pow } pub fn session_max_age_secs(&self) -> u64 { self.config.session.max_age.max(1) as u64 } pub async fn mail_locale( &self, uid: Option, accept_language: Option<&str>, ) -> i18n::UiLocale { let fallback = i18n::UiLocale::parse(self.config().email.default_locale()) .unwrap_or(i18n::UiLocale::En); match uid { Some(uid) => i18n::resolve_user_locale(self.db().read(), uid, fallback).await, None => i18n::negotiate_accept_language(accept_language, fallback), } } pub async fn emit(&self, kind: events::EventKind, payload: T, request_id: Option<&str>) where T: Serialize + Clone, { self.emit_labeled(kind, Labels::default(), payload, request_id) .await; } pub async fn emit_labeled( &self, kind: events::EventKind, labels: Labels, payload: T, request_id: Option<&str>, ) where T: Serialize + Clone, { let enqueued = events::emit_retry( self.db().write(), kind, labels.with_opt(label::REQUEST_ID, request_id), &payload, ) .await; if let Err(error) = enqueued { tracing::error!(kind = %kind, %error, "failed to enqueue the event in the outbox"); } } pub async fn emit_tx( &self, conn: &impl sea_orm::ConnectionTrait, kind: events::EventKind, labels: Labels, payload: T, request_id: Option<&str>, ) -> AuthResult<()> where T: Serialize, { events::emit( conn, kind, labels.with_opt(label::REQUEST_ID, request_id), payload, ) .await?; Ok(()) } pub async fn publish_mail( &self, to: &str, subject: &str, text: &str, html: &str, request_id: Option<&str>, ) -> AuthResult<()> { let Some(mail) = self.mail() else { return Err(AuthError::Unavailable( "mail publisher is not configured".to_owned(), )); }; mail.send_mail(to, subject, text, html, request_id) .await .map_err(|error| AuthError::Internal(error.to_string())) } } #[derive(Clone)] pub struct AuthSettings { pub auth: config::auth::AuthConfig, pub pow: config::pow::PowConfig, pub session: config::session::SessionConfig, pub email: config::email::EmailConfig, } impl AuthSettings { pub fn from_config(config: &config::AppConfig) -> Self { Self { auth: config.auth.clone(), pow: config.pow.clone(), session: config.session.clone(), email: config.email.clone(), } } } pub mod account_access; pub mod account_identity; pub mod account_operations;