push.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. use reqwest::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
  2. use serde_json::Value;
  3. use tokio::sync::RwLock;
  4. use crate::{
  5. api::{ApiResult, EmptyResult, UpdateType},
  6. db::models::{Cipher, Device, Folder, Send, User},
  7. util::get_reqwest_client,
  8. CONFIG,
  9. };
  10. use once_cell::sync::Lazy;
  11. use std::time::{Duration, Instant};
  12. #[derive(Deserialize)]
  13. struct AuthPushToken {
  14. access_token: String,
  15. expires_in: i32,
  16. }
  17. #[derive(Debug)]
  18. struct LocalAuthPushToken {
  19. access_token: String,
  20. valid_until: Instant,
  21. }
  22. async fn get_auth_push_token() -> ApiResult<String> {
  23. static PUSH_TOKEN: Lazy<RwLock<LocalAuthPushToken>> = Lazy::new(|| {
  24. RwLock::new(LocalAuthPushToken {
  25. access_token: String::new(),
  26. valid_until: Instant::now(),
  27. })
  28. });
  29. let push_token = PUSH_TOKEN.read().await;
  30. if push_token.valid_until.saturating_duration_since(Instant::now()).as_secs() > 0 {
  31. debug!("Auth Push token still valid, no need for a new one");
  32. return Ok(push_token.access_token.clone());
  33. }
  34. drop(push_token); // Drop the read lock now
  35. let installation_id = CONFIG.push_installation_id();
  36. let client_id = format!("installation.{installation_id}");
  37. let client_secret = CONFIG.push_installation_key();
  38. let params = [
  39. ("grant_type", "client_credentials"),
  40. ("scope", "api.push"),
  41. ("client_id", &client_id),
  42. ("client_secret", &client_secret),
  43. ];
  44. let res = match get_reqwest_client().post("https://identity.bitwarden.com/connect/token").form(&params).send().await
  45. {
  46. Ok(r) => r,
  47. Err(e) => err!(format!("Error getting push token from bitwarden server: {e}")),
  48. };
  49. let json_pushtoken = match res.json::<AuthPushToken>().await {
  50. Ok(r) => r,
  51. Err(e) => err!(format!("Unexpected push token received from bitwarden server: {e}")),
  52. };
  53. let mut push_token = PUSH_TOKEN.write().await;
  54. push_token.valid_until = Instant::now()
  55. .checked_add(Duration::new((json_pushtoken.expires_in / 2) as u64, 0)) // Token valid for half the specified time
  56. .unwrap();
  57. push_token.access_token = json_pushtoken.access_token;
  58. debug!("Token still valid for {}", push_token.valid_until.saturating_duration_since(Instant::now()).as_secs());
  59. Ok(push_token.access_token.clone())
  60. }
  61. pub async fn register_push_device(user_uuid: String, device: Device) -> EmptyResult {
  62. if !CONFIG.push_enabled() {
  63. return Ok(());
  64. }
  65. let auth_push_token = get_auth_push_token().await?;
  66. //Needed to register a device for push to bitwarden :
  67. let data = json!({
  68. "userId": user_uuid,
  69. "deviceId": device.push_uuid,
  70. "identifier": device.uuid,
  71. "type": device.atype,
  72. "pushToken": device.push_token
  73. });
  74. let auth_header = format!("Bearer {}", &auth_push_token);
  75. get_reqwest_client()
  76. .post(CONFIG.push_relay_uri() + "/push/register")
  77. .header(CONTENT_TYPE, "application/json")
  78. .header(ACCEPT, "application/json")
  79. .header(AUTHORIZATION, auth_header)
  80. .json(&data)
  81. .send()
  82. .await?
  83. .error_for_status()?;
  84. Ok(())
  85. }
  86. pub async fn unregister_push_device(uuid: String) -> EmptyResult {
  87. if !CONFIG.push_enabled() {
  88. return Ok(());
  89. }
  90. let auth_push_token = get_auth_push_token().await?;
  91. let auth_header = format!("Bearer {}", &auth_push_token);
  92. match get_reqwest_client()
  93. .delete(CONFIG.push_relay_uri() + "/push/" + &uuid)
  94. .header(AUTHORIZATION, auth_header)
  95. .send()
  96. .await
  97. {
  98. Ok(r) => r,
  99. Err(e) => err!(format!("An error occured during device unregistration: {e}")),
  100. };
  101. Ok(())
  102. }
  103. pub async fn push_cipher_update(
  104. ut: UpdateType,
  105. cipher: &Cipher,
  106. acting_device_uuid: &String,
  107. conn: &mut crate::db::DbConn,
  108. ) {
  109. // We shouldn't send a push notification on cipher update if the cipher belongs to an organization, this isn't implemented in the upstream server too.
  110. if cipher.organization_uuid.is_some() {
  111. return;
  112. };
  113. let user_uuid = match &cipher.user_uuid {
  114. Some(c) => c,
  115. None => {
  116. debug!("Cipher has no uuid");
  117. return;
  118. }
  119. };
  120. for device in Device::find_by_user(user_uuid, conn).await {
  121. let data = json!({
  122. "userId": user_uuid,
  123. "organizationId": (),
  124. "deviceId": device.push_uuid,
  125. "identifier": acting_device_uuid,
  126. "type": ut as i32,
  127. "payload": {
  128. "Id": cipher.uuid,
  129. "UserId": cipher.user_uuid,
  130. "OrganizationId": (),
  131. "RevisionDate": cipher.updated_at
  132. }
  133. });
  134. send_to_push_relay(data).await;
  135. }
  136. }
  137. pub async fn push_logout(user: &User, acting_device_uuid: Option<String>, conn: &mut crate::db::DbConn) {
  138. if let Some(d) = acting_device_uuid {
  139. for device in Device::find_by_user(&user.uuid, conn).await {
  140. let data = json!({
  141. "userId": user.uuid,
  142. "organizationId": (),
  143. "deviceId": device.push_uuid,
  144. "identifier": d,
  145. "type": UpdateType::LogOut as i32,
  146. "payload": {
  147. "UserId": user.uuid,
  148. "Date": user.updated_at
  149. }
  150. });
  151. send_to_push_relay(data).await;
  152. }
  153. } else {
  154. let data = json!({
  155. "userId": user.uuid,
  156. "organizationId": (),
  157. "deviceId": (),
  158. "identifier": (),
  159. "type": UpdateType::LogOut as i32,
  160. "payload": {
  161. "UserId": user.uuid,
  162. "Date": user.updated_at
  163. }
  164. });
  165. send_to_push_relay(data).await;
  166. }
  167. }
  168. pub async fn push_user_update(ut: UpdateType, user: &User) {
  169. let data = json!({
  170. "userId": user.uuid,
  171. "organizationId": (),
  172. "deviceId": (),
  173. "identifier": (),
  174. "type": ut as i32,
  175. "payload": {
  176. "UserId": user.uuid,
  177. "Date": user.updated_at
  178. }
  179. });
  180. send_to_push_relay(data).await;
  181. }
  182. pub async fn push_folder_update(
  183. ut: UpdateType,
  184. folder: &Folder,
  185. acting_device_uuid: &String,
  186. conn: &mut crate::db::DbConn,
  187. ) {
  188. for device in Device::find_by_user(&folder.user_uuid, conn).await {
  189. let data = json!({
  190. "userId": folder.user_uuid,
  191. "organizationId": (),
  192. "deviceId": device.push_uuid,
  193. "identifier": acting_device_uuid,
  194. "type": ut as i32,
  195. "payload": {
  196. "Id": folder.uuid,
  197. "UserId": folder.user_uuid,
  198. "RevisionDate": folder.updated_at
  199. }
  200. });
  201. send_to_push_relay(data).await;
  202. }
  203. }
  204. pub async fn push_send_update(ut: UpdateType, send: &Send, conn: &mut crate::db::DbConn) {
  205. if let Some(s) = &send.user_uuid {
  206. for device in Device::find_by_user(s, conn).await {
  207. let data = json!({
  208. "userId": send.user_uuid,
  209. "organizationId": (),
  210. "deviceId": device.push_uuid,
  211. "identifier": (),
  212. "type": ut as i32,
  213. "payload": {
  214. "Id": send.uuid,
  215. "UserId": send.user_uuid,
  216. "RevisionDate": send.revision_date
  217. }
  218. });
  219. send_to_push_relay(data).await;
  220. }
  221. }
  222. }
  223. async fn send_to_push_relay(data: Value) {
  224. if !CONFIG.push_enabled() {
  225. return;
  226. }
  227. let auth_push_token = match get_auth_push_token().await {
  228. Ok(s) => s,
  229. Err(e) => {
  230. debug!("Could not get the auth push token: {}", e);
  231. return;
  232. }
  233. };
  234. let auth_header = format!("Bearer {}", &auth_push_token);
  235. if let Err(e) = get_reqwest_client()
  236. .post(CONFIG.push_relay_uri() + "/push/send")
  237. .header(ACCEPT, "application/json")
  238. .header(CONTENT_TYPE, "application/json")
  239. .header(AUTHORIZATION, auth_header)
  240. .json(&data)
  241. .send()
  242. .await
  243. {
  244. error!("An error occured while sending a send update to the push relay: {}", e);
  245. };
  246. }