1#[cfg(feature = "mongodb")]
2mod mongodb;
3mod reference;
4
5use authifier::config::Captcha;
6use authifier::config::EmailVerificationConfig;
7use authifier::config::PasswordScanning;
8use authifier::config::ResolveIp;
9use authifier::config::SMTPSettings;
10use authifier::config::Shield;
11use authifier::config::Template;
12use authifier::config::Templates;
13use authifier::config::EmailExpiryConfig;
14use authifier::Authifier;
15use rand::Rng;
16use revolt_config::config;
17
18#[cfg(feature = "mongodb")]
19pub use self::mongodb::*;
20pub use self::reference::*;
21
22pub enum DatabaseInfo {
24 Auto,
26 Test(String),
28 Reference,
30 #[cfg(feature = "mongodb")]
32 MongoDb { uri: String, database_name: String },
33 #[cfg(feature = "mongodb")]
35 MongoDbFromClient(::mongodb::Client, String),
36}
37
38#[derive(Clone, Debug)]
40pub enum Database {
41 Reference(ReferenceDb),
43 #[cfg(feature = "mongodb")]
45 MongoDb(MongoDb),
46}
47
48impl DatabaseInfo {
49 #[async_recursion]
51 pub async fn connect(self) -> Result<Database, String> {
52 let config = config().await;
53
54 match self {
55 DatabaseInfo::Auto => {
56 if std::env::var("TEST_DB").is_ok() {
57 DatabaseInfo::Test(format!(
58 "revolt_test_{}",
59 rand::thread_rng().gen_range(1_000_000..10_000_000)
60 ))
61 .connect()
62 .await
63 } else if !config.database.mongodb.is_empty() {
64 #[cfg(feature = "mongodb")]
65 return DatabaseInfo::MongoDb {
66 uri: config.database.mongodb,
67 database_name: "revolt".to_string(),
68 }
69 .connect()
70 .await;
71
72 #[cfg(not(feature = "mongodb"))]
73 return Err("MongoDB not enabled.".to_string());
74 } else {
75 DatabaseInfo::Reference.connect().await
76 }
77 }
78 DatabaseInfo::Test(database_name) => {
79 match std::env::var("TEST_DB")
80 .expect("`TEST_DB` environment variable should be set to REFERENCE or MONGODB")
81 .as_str()
82 {
83 "REFERENCE" => DatabaseInfo::Reference.connect().await,
84 "MONGODB" => {
85 #[cfg(feature = "mongodb")]
86 return DatabaseInfo::MongoDb {
87 uri: config.database.mongodb,
88 database_name,
89 }
90 .connect()
91 .await;
92
93 #[cfg(not(feature = "mongodb"))]
94 return Err("MongoDB not enabled.".to_string());
95 }
96 _ => unreachable!("must specify REFERENCE or MONGODB"),
97 }
98 }
99 DatabaseInfo::Reference => Ok(Database::Reference(Default::default())),
100 #[cfg(feature = "mongodb")]
101 DatabaseInfo::MongoDb { uri, database_name } => {
102 let client = ::mongodb::Client::with_uri_str(uri)
103 .await
104 .map_err(|_| "Failed to init db connection.".to_string())?;
105
106 Ok(Database::MongoDb(MongoDb(client, database_name)))
107 }
108 #[cfg(feature = "mongodb")]
109 DatabaseInfo::MongoDbFromClient(client, database_name) => {
110 Ok(Database::MongoDb(MongoDb(client, database_name)))
111 }
112 }
113 }
114}
115
116impl Database {
117 pub async fn to_authifier(self) -> Authifier {
119 let config = config().await;
120
121 let mut auth_config = authifier::Config {
122 password_scanning: if config.api.security.easypwned.is_empty() {
123 Default::default()
124 } else {
125 PasswordScanning::EasyPwned {
126 endpoint: config.api.security.easypwned,
127 }
128 },
129 email_verification: if !config.api.smtp.host.is_empty() {
130 EmailVerificationConfig::Enabled {
131 smtp: SMTPSettings {
132 from: config.api.smtp.from_address,
133 host: config.api.smtp.host,
134 username: config.api.smtp.username,
135 password: config.api.smtp.password,
136 reply_to: Some(
137 config
138 .api
139 .smtp
140 .reply_to
141 .unwrap_or("support@stoat.chat".into()),
142 ),
143 port: config.api.smtp.port,
144 use_tls: config.api.smtp.use_tls,
145 use_starttls: config.api.smtp.use_starttls,
146 },
147 expiry: EmailExpiryConfig {
148 expire_verification: 3600 * 24 * 7,
149 expire_password_reset: 3600 * 24,
150 expire_account_deletion: 3600 * 24,
151 },
152 templates: if config.production {
153 Templates {
154 verify: Template {
155 title: "Verify your Stoat account.".into(),
156 text: include_str!("../../templates/verify.txt").into(),
157 url: format!("{}/login/verify/", config.hosts.app),
158 html: Some(include_str!("../../templates/verify.html").into()),
159 },
160 reset: Template {
161 title: "Reset your Stoat password.".into(),
162 text: include_str!("../../templates/reset.txt").into(),
163 url: format!("{}/login/reset/", config.hosts.app),
164 html: Some(include_str!("../../templates/reset.html").into()),
165 },
166 reset_existing: Template {
167 title: "You already have a Stoat account, reset your password."
168 .into(),
169 text: include_str!("../../templates/reset-existing.txt").into(),
170 url: format!("{}/login/reset/", config.hosts.app),
171 html: Some(
172 include_str!("../../templates/reset-existing.html").into(),
173 ),
174 },
175 deletion: Template {
176 title: "Confirm account deletion.".into(),
177 text: include_str!("../../templates/deletion.txt").into(),
178 url: format!("{}/delete/", config.hosts.app),
179 html: Some(include_str!("../../templates/deletion.html").into()),
180 },
181 welcome: None,
182 }
183 } else {
184 Templates {
185 verify: Template {
186 title: "Verify your account.".into(),
187 text: include_str!("../../templates/verify.whitelabel.txt").into(),
188 url: format!("{}/login/verify/", config.hosts.app),
189 html: None,
190 },
191 reset: Template {
192 title: "Reset your password.".into(),
193 text: include_str!("../../templates/reset.whitelabel.txt").into(),
194 url: format!("{}/login/reset/", config.hosts.app),
195 html: None,
196 },
197 reset_existing: Template {
198 title: "Reset your password.".into(),
199 text: include_str!("../../templates/reset.whitelabel.txt").into(),
200 url: format!("{}/login/reset/", config.hosts.app),
201 html: None,
202 },
203 deletion: Template {
204 title: "Confirm account deletion.".into(),
205 text: include_str!("../../templates/deletion.whitelabel.txt")
206 .into(),
207 url: format!("{}/delete/", config.hosts.app),
208 html: None,
209 },
210 welcome: None,
211 }
212 },
213 }
214 } else {
215 EmailVerificationConfig::Disabled
216 },
217 ..Default::default()
218 };
219
220 auth_config.invite_only = config.api.registration.invite_only;
221
222 if !config.api.security.captcha.hcaptcha_key.is_empty() {
223 auth_config.captcha = Captcha::HCaptcha {
224 secret: config.api.security.captcha.hcaptcha_key,
225 };
226 }
227
228 if !config.api.security.authifier_shield_key.is_empty() {
229 auth_config.shield = Shield::Enabled {
230 api_key: config.api.security.authifier_shield_key,
231 strict: false,
232 };
233 }
234
235 if config.api.security.trust_cloudflare {
236 auth_config.resolve_ip = ResolveIp::Cloudflare;
237 }
238
239 Authifier {
240 database: match self {
241 Database::Reference(_) => Default::default(),
242 #[cfg(feature = "mongodb")]
243 Database::MongoDb(MongoDb(client, _)) => authifier::Database::MongoDb(
244 authifier::database::MongoDb(client.database("revolt")),
245 ),
246 },
247 config: auth_config,
248 #[cfg(feature = "tasks")]
249 event_channel: Some(crate::tasks::authifier_relay::sender()),
250 #[cfg(not(feature = "tasks"))]
251 event_channel: None,
252 }
253 }
254}