mod.rs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. use chrono::{TimeDelta, Utc};
  2. use data_encoding::BASE32;
  3. use rocket::serde::json::Json;
  4. use rocket::Route;
  5. use serde_json::Value;
  6. use crate::{
  7. api::{
  8. core::{log_event, log_user_event},
  9. EmptyResult, JsonResult, PasswordOrOtpData,
  10. },
  11. auth::{ClientHeaders, Headers},
  12. crypto,
  13. db::{models::*, DbConn, DbPool},
  14. mail,
  15. util::NumberOrString,
  16. CONFIG,
  17. };
  18. pub mod authenticator;
  19. pub mod duo;
  20. pub mod duo_oidc;
  21. pub mod email;
  22. pub mod protected_actions;
  23. pub mod webauthn;
  24. pub mod yubikey;
  25. pub fn routes() -> Vec<Route> {
  26. let mut routes = routes![
  27. get_twofactor,
  28. get_recover,
  29. recover,
  30. disable_twofactor,
  31. disable_twofactor_put,
  32. get_device_verification_settings,
  33. ];
  34. routes.append(&mut authenticator::routes());
  35. routes.append(&mut duo::routes());
  36. routes.append(&mut email::routes());
  37. routes.append(&mut webauthn::routes());
  38. routes.append(&mut yubikey::routes());
  39. routes.append(&mut protected_actions::routes());
  40. routes
  41. }
  42. #[get("/two-factor")]
  43. async fn get_twofactor(headers: Headers, mut conn: DbConn) -> Json<Value> {
  44. let twofactors = TwoFactor::find_by_user(&headers.user.uuid, &mut conn).await;
  45. let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_provider).collect();
  46. Json(json!({
  47. "data": twofactors_json,
  48. "object": "list",
  49. "continuationToken": null,
  50. }))
  51. }
  52. #[post("/two-factor/get-recover", data = "<data>")]
  53. async fn get_recover(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
  54. let data: PasswordOrOtpData = data.into_inner();
  55. let user = headers.user;
  56. data.validate(&user, true, &mut conn).await?;
  57. Ok(Json(json!({
  58. "code": user.totp_recover,
  59. "object": "twoFactorRecover"
  60. })))
  61. }
  62. #[derive(Deserialize)]
  63. #[serde(rename_all = "camelCase")]
  64. struct RecoverTwoFactor {
  65. master_password_hash: String,
  66. email: String,
  67. recovery_code: String,
  68. }
  69. #[post("/two-factor/recover", data = "<data>")]
  70. async fn recover(data: Json<RecoverTwoFactor>, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult {
  71. let data: RecoverTwoFactor = data.into_inner();
  72. use crate::db::models::User;
  73. // Get the user
  74. let mut user = match User::find_by_mail(&data.email, &mut conn).await {
  75. Some(user) => user,
  76. None => err!("Username or password is incorrect. Try again."),
  77. };
  78. // Check password
  79. if !user.check_valid_password(&data.master_password_hash) {
  80. err!("Username or password is incorrect. Try again.")
  81. }
  82. // Check if recovery code is correct
  83. if !user.check_valid_recovery_code(&data.recovery_code) {
  84. err!("Recovery code is incorrect. Try again.")
  85. }
  86. // Remove all twofactors from the user
  87. TwoFactor::delete_all_by_user(&user.uuid, &mut conn).await?;
  88. enforce_2fa_policy(&user, &user.uuid, client_headers.device_type, &client_headers.ip.ip, &mut conn).await?;
  89. log_user_event(
  90. EventType::UserRecovered2fa as i32,
  91. &user.uuid,
  92. client_headers.device_type,
  93. &client_headers.ip.ip,
  94. &mut conn,
  95. )
  96. .await;
  97. // Remove the recovery code, not needed without twofactors
  98. user.totp_recover = None;
  99. user.save(&mut conn).await?;
  100. Ok(Json(Value::Object(serde_json::Map::new())))
  101. }
  102. async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) {
  103. if user.totp_recover.is_none() {
  104. let totp_recover = crypto::encode_random_bytes::<20>(BASE32);
  105. user.totp_recover = Some(totp_recover);
  106. user.save(conn).await.ok();
  107. }
  108. }
  109. #[derive(Deserialize)]
  110. #[serde(rename_all = "camelCase")]
  111. struct DisableTwoFactorData {
  112. master_password_hash: Option<String>,
  113. otp: Option<String>,
  114. r#type: NumberOrString,
  115. }
  116. #[post("/two-factor/disable", data = "<data>")]
  117. async fn disable_twofactor(data: Json<DisableTwoFactorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
  118. let data: DisableTwoFactorData = data.into_inner();
  119. let user = headers.user;
  120. // Delete directly after a valid token has been provided
  121. PasswordOrOtpData {
  122. master_password_hash: data.master_password_hash,
  123. otp: data.otp,
  124. }
  125. .validate(&user, true, &mut conn)
  126. .await?;
  127. let type_ = data.r#type.into_i32()?;
  128. if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await {
  129. twofactor.delete(&mut conn).await?;
  130. log_user_event(EventType::UserDisabled2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn)
  131. .await;
  132. }
  133. if TwoFactor::find_by_user(&user.uuid, &mut conn).await.is_empty() {
  134. enforce_2fa_policy(&user, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await?;
  135. }
  136. Ok(Json(json!({
  137. "enabled": false,
  138. "type": type_,
  139. "object": "twoFactorProvider"
  140. })))
  141. }
  142. #[put("/two-factor/disable", data = "<data>")]
  143. async fn disable_twofactor_put(data: Json<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
  144. disable_twofactor(data, headers, conn).await
  145. }
  146. pub async fn enforce_2fa_policy(
  147. user: &User,
  148. act_uuid: &str,
  149. device_type: i32,
  150. ip: &std::net::IpAddr,
  151. conn: &mut DbConn,
  152. ) -> EmptyResult {
  153. for member in UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn)
  154. .await
  155. .into_iter()
  156. {
  157. // Policy only applies to non-Owner/non-Admin members who have accepted joining the org
  158. if member.atype < UserOrgType::Admin {
  159. if CONFIG.mail_enabled() {
  160. let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap();
  161. mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
  162. }
  163. let mut member = member;
  164. member.revoke();
  165. member.save(conn).await?;
  166. log_event(
  167. EventType::OrganizationUserRevoked as i32,
  168. &member.uuid,
  169. &member.org_uuid,
  170. act_uuid,
  171. device_type,
  172. ip,
  173. conn,
  174. )
  175. .await;
  176. }
  177. }
  178. Ok(())
  179. }
  180. pub async fn enforce_2fa_policy_for_org(
  181. org_uuid: &str,
  182. act_uuid: &str,
  183. device_type: i32,
  184. ip: &std::net::IpAddr,
  185. conn: &mut DbConn,
  186. ) -> EmptyResult {
  187. let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap();
  188. for member in UserOrganization::find_confirmed_by_org(org_uuid, conn).await.into_iter() {
  189. // Don't enforce the policy for Admins and Owners.
  190. if member.atype < UserOrgType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() {
  191. if CONFIG.mail_enabled() {
  192. let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap();
  193. mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
  194. }
  195. let mut member = member;
  196. member.revoke();
  197. member.save(conn).await?;
  198. log_event(
  199. EventType::OrganizationUserRevoked as i32,
  200. &member.uuid,
  201. org_uuid,
  202. act_uuid,
  203. device_type,
  204. ip,
  205. conn,
  206. )
  207. .await;
  208. }
  209. }
  210. Ok(())
  211. }
  212. pub async fn send_incomplete_2fa_notifications(pool: DbPool) {
  213. debug!("Sending notifications for incomplete 2FA logins");
  214. if CONFIG.incomplete_2fa_time_limit() <= 0 || !CONFIG.mail_enabled() {
  215. return;
  216. }
  217. let mut conn = match pool.get().await {
  218. Ok(conn) => conn,
  219. _ => {
  220. error!("Failed to get DB connection in send_incomplete_2fa_notifications()");
  221. return;
  222. }
  223. };
  224. let now = Utc::now().naive_utc();
  225. let time_limit = TimeDelta::try_minutes(CONFIG.incomplete_2fa_time_limit()).unwrap();
  226. let time_before = now - time_limit;
  227. let incomplete_logins = TwoFactorIncomplete::find_logins_before(&time_before, &mut conn).await;
  228. for login in incomplete_logins {
  229. let user = User::find_by_uuid(&login.user_uuid, &mut conn).await.expect("User not found");
  230. info!(
  231. "User {} did not complete a 2FA login within the configured time limit. IP: {}",
  232. user.email, login.ip_address
  233. );
  234. mail::send_incomplete_2fa_login(&user.email, &login.ip_address, &login.login_time, &login.device_name)
  235. .await
  236. .expect("Error sending incomplete 2FA email");
  237. login.delete(&mut conn).await.expect("Error deleting incomplete 2FA record");
  238. }
  239. }
  240. // This function currently is just a dummy and the actual part is not implemented yet.
  241. // This also prevents 404 errors.
  242. //
  243. // See the following Bitwarden PR's regarding this feature.
  244. // https://github.com/bitwarden/clients/pull/2843
  245. // https://github.com/bitwarden/clients/pull/2839
  246. // https://github.com/bitwarden/server/pull/2016
  247. //
  248. // The HTML part is hidden via the CSS patches done via the bw_web_build repo
  249. #[get("/two-factor/get-device-verification-settings")]
  250. fn get_device_verification_settings(_headers: Headers, _conn: DbConn) -> Json<Value> {
  251. Json(json!({
  252. "isDeviceVerificationSectionEnabled":false,
  253. "unknownDeviceVerificationEnabled":false,
  254. "object":"deviceVerificationSettings"
  255. }))
  256. }