b771a06
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<MailPublisher>,
sessions: crate::session::RedisStore,
}
impl AuthService {
pub fn new(
accounts: crate::Accounts,
config: AuthSettings,
mail: Option<MailPublisher>,
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<i64>,
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<T>(&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<T>(
&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<T>(
&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;