mod.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. pub mod accounts;
  2. mod ciphers;
  3. mod emergency_access;
  4. mod events;
  5. mod folders;
  6. mod organizations;
  7. mod public;
  8. mod sends;
  9. pub mod two_factor;
  10. pub use accounts::purge_auth_requests;
  11. pub use ciphers::{purge_trashed_ciphers, CipherData, CipherSyncData, CipherSyncType};
  12. pub use emergency_access::{emergency_notification_reminder_job, emergency_request_timeout_job};
  13. pub use events::{event_cleanup_job, log_event, log_user_event};
  14. pub use sends::purge_sends;
  15. pub use two_factor::send_incomplete_2fa_notifications;
  16. pub fn routes() -> Vec<Route> {
  17. let mut eq_domains_routes = routes![get_eq_domains, post_eq_domains, put_eq_domains];
  18. let mut hibp_routes = routes![hibp_breach];
  19. let mut meta_routes = routes![alive, now, version, config];
  20. let mut routes = Vec::new();
  21. routes.append(&mut accounts::routes());
  22. routes.append(&mut ciphers::routes());
  23. routes.append(&mut emergency_access::routes());
  24. routes.append(&mut events::routes());
  25. routes.append(&mut folders::routes());
  26. routes.append(&mut organizations::routes());
  27. routes.append(&mut two_factor::routes());
  28. routes.append(&mut sends::routes());
  29. routes.append(&mut public::routes());
  30. routes.append(&mut eq_domains_routes);
  31. routes.append(&mut hibp_routes);
  32. routes.append(&mut meta_routes);
  33. routes
  34. }
  35. pub fn events_routes() -> Vec<Route> {
  36. let mut routes = Vec::new();
  37. routes.append(&mut events::main_routes());
  38. routes
  39. }
  40. //
  41. // Move this somewhere else
  42. //
  43. use rocket::{serde::json::Json, Catcher, Route};
  44. use serde_json::Value;
  45. use crate::{
  46. api::{JsonResult, JsonUpcase, Notify, UpdateType},
  47. auth::Headers,
  48. db::DbConn,
  49. error::Error,
  50. util::get_reqwest_client,
  51. };
  52. #[derive(Serialize, Deserialize, Debug)]
  53. #[allow(non_snake_case)]
  54. struct GlobalDomain {
  55. Type: i32,
  56. Domains: Vec<String>,
  57. Excluded: bool,
  58. }
  59. const GLOBAL_DOMAINS: &str = include_str!("../../static/global_domains.json");
  60. #[get("/settings/domains")]
  61. fn get_eq_domains(headers: Headers) -> Json<Value> {
  62. _get_eq_domains(headers, false)
  63. }
  64. fn _get_eq_domains(headers: Headers, no_excluded: bool) -> Json<Value> {
  65. let user = headers.user;
  66. use serde_json::from_str;
  67. let equivalent_domains: Vec<Vec<String>> = from_str(&user.equivalent_domains).unwrap();
  68. let excluded_globals: Vec<i32> = from_str(&user.excluded_globals).unwrap();
  69. let mut globals: Vec<GlobalDomain> = from_str(GLOBAL_DOMAINS).unwrap();
  70. for global in &mut globals {
  71. global.Excluded = excluded_globals.contains(&global.Type);
  72. }
  73. if no_excluded {
  74. globals.retain(|g| !g.Excluded);
  75. }
  76. Json(json!({
  77. "EquivalentDomains": equivalent_domains,
  78. "GlobalEquivalentDomains": globals,
  79. "Object": "domains",
  80. }))
  81. }
  82. #[derive(Deserialize, Debug)]
  83. #[allow(non_snake_case)]
  84. struct EquivDomainData {
  85. ExcludedGlobalEquivalentDomains: Option<Vec<i32>>,
  86. EquivalentDomains: Option<Vec<Vec<String>>>,
  87. }
  88. #[post("/settings/domains", data = "<data>")]
  89. async fn post_eq_domains(
  90. data: JsonUpcase<EquivDomainData>,
  91. headers: Headers,
  92. mut conn: DbConn,
  93. nt: Notify<'_>,
  94. ) -> JsonResult {
  95. let data: EquivDomainData = data.into_inner().data;
  96. let excluded_globals = data.ExcludedGlobalEquivalentDomains.unwrap_or_default();
  97. let equivalent_domains = data.EquivalentDomains.unwrap_or_default();
  98. let mut user = headers.user;
  99. use serde_json::to_string;
  100. user.excluded_globals = to_string(&excluded_globals).unwrap_or_else(|_| "[]".to_string());
  101. user.equivalent_domains = to_string(&equivalent_domains).unwrap_or_else(|_| "[]".to_string());
  102. user.save(&mut conn).await?;
  103. nt.send_user_update(UpdateType::SyncSettings, &user).await;
  104. Ok(Json(json!({})))
  105. }
  106. #[put("/settings/domains", data = "<data>")]
  107. async fn put_eq_domains(
  108. data: JsonUpcase<EquivDomainData>,
  109. headers: Headers,
  110. conn: DbConn,
  111. nt: Notify<'_>,
  112. ) -> JsonResult {
  113. post_eq_domains(data, headers, conn, nt).await
  114. }
  115. #[get("/hibp/breach?<username>")]
  116. async fn hibp_breach(username: &str) -> JsonResult {
  117. let url = format!(
  118. "https://haveibeenpwned.com/api/v3/breachedaccount/{username}?truncateResponse=false&includeUnverified=false"
  119. );
  120. if let Some(api_key) = crate::CONFIG.hibp_api_key() {
  121. let hibp_client = get_reqwest_client();
  122. let res = hibp_client.get(&url).header("hibp-api-key", api_key).send().await?;
  123. // If we get a 404, return a 404, it means no breached accounts
  124. if res.status() == 404 {
  125. return Err(Error::empty().with_code(404));
  126. }
  127. let value: Value = res.error_for_status()?.json().await?;
  128. Ok(Json(value))
  129. } else {
  130. Ok(Json(json!([{
  131. "Name": "HaveIBeenPwned",
  132. "Title": "Manual HIBP Check",
  133. "Domain": "haveibeenpwned.com",
  134. "BreachDate": "2019-08-18T00:00:00Z",
  135. "AddedDate": "2019-08-18T00:00:00Z",
  136. "Description": format!("Go to: <a href=\"https://haveibeenpwned.com/account/{username}\" target=\"_blank\" rel=\"noreferrer\">https://haveibeenpwned.com/account/{username}</a> for a manual check.<br/><br/>HaveIBeenPwned API key not set!<br/>Go to <a href=\"https://haveibeenpwned.com/API/Key\" target=\"_blank\" rel=\"noreferrer\">https://haveibeenpwned.com/API/Key</a> to purchase an API key from HaveIBeenPwned.<br/><br/>"),
  137. "LogoPath": "vw_static/hibp.png",
  138. "PwnCount": 0,
  139. "DataClasses": [
  140. "Error - No API key set!"
  141. ]
  142. }])))
  143. }
  144. }
  145. // We use DbConn here to let the alive healthcheck also verify the database connection.
  146. #[get("/alive")]
  147. fn alive(_conn: DbConn) -> Json<String> {
  148. now()
  149. }
  150. #[get("/now")]
  151. pub fn now() -> Json<String> {
  152. Json(crate::util::format_date(&chrono::Utc::now().naive_utc()))
  153. }
  154. #[get("/version")]
  155. fn version() -> Json<&'static str> {
  156. Json(crate::VERSION.unwrap_or_default())
  157. }
  158. #[get("/config")]
  159. fn config() -> Json<Value> {
  160. let domain = crate::CONFIG.domain();
  161. Json(json!({
  162. // Note: The clients use this version to handle backwards compatibility concerns
  163. // This means they expect a version that closely matches the Bitwarden server version
  164. // We should make sure that we keep this updated when we support the new server features
  165. // Version history:
  166. // - Individual cipher key encryption: 2023.9.1
  167. "version": "2023.9.1",
  168. "gitHash": option_env!("GIT_REV"),
  169. "server": {
  170. "name": "Vaultwarden",
  171. "url": "https://github.com/dani-garcia/vaultwarden"
  172. },
  173. "environment": {
  174. "vault": domain,
  175. "api": format!("{domain}/api"),
  176. "identity": format!("{domain}/identity"),
  177. "notifications": format!("{domain}/notifications"),
  178. "sso": "",
  179. },
  180. "featureStates": {
  181. // Any feature flags that we want the clients to use
  182. // Can check the enabled ones at:
  183. // https://vault.bitwarden.com/api/config
  184. "autofill-v2": true
  185. },
  186. "object": "config",
  187. }))
  188. }
  189. pub fn catchers() -> Vec<Catcher> {
  190. catchers![api_not_found]
  191. }
  192. #[catch(404)]
  193. fn api_not_found() -> Json<Value> {
  194. Json(json!({
  195. "error": {
  196. "code": 404,
  197. "reason": "Not Found",
  198. "description": "The requested resource could not be found."
  199. }
  200. }))
  201. }