|
|
@ -28,20 +28,20 @@ struct LocalAuthPushToken { |
|
|
|
valid_until: Instant, |
|
|
|
} |
|
|
|
|
|
|
|
async fn get_auth_push_token() -> ApiResult<String> { |
|
|
|
static PUSH_TOKEN: Lazy<RwLock<LocalAuthPushToken>> = Lazy::new(|| { |
|
|
|
async fn get_auth_api_token() -> ApiResult<String> { |
|
|
|
static API_TOKEN: Lazy<RwLock<LocalAuthPushToken>> = Lazy::new(|| { |
|
|
|
RwLock::new(LocalAuthPushToken { |
|
|
|
access_token: String::new(), |
|
|
|
valid_until: Instant::now(), |
|
|
|
}) |
|
|
|
}); |
|
|
|
let push_token = PUSH_TOKEN.read().await; |
|
|
|
let api_token = API_TOKEN.read().await; |
|
|
|
|
|
|
|
if push_token.valid_until.saturating_duration_since(Instant::now()).as_secs() > 0 { |
|
|
|
if api_token.valid_until.saturating_duration_since(Instant::now()).as_secs() > 0 { |
|
|
|
debug!("Auth Push token still valid, no need for a new one"); |
|
|
|
return Ok(push_token.access_token.clone()); |
|
|
|
return Ok(api_token.access_token.clone()); |
|
|
|
} |
|
|
|
drop(push_token); // Drop the read lock now
|
|
|
|
drop(api_token); // Drop the read lock now
|
|
|
|
|
|
|
|
let installation_id = CONFIG.push_installation_id(); |
|
|
|
let client_id = format!("installation.{installation_id}"); |
|
|
@ -68,44 +68,48 @@ async fn get_auth_push_token() -> ApiResult<String> { |
|
|
|
Err(e) => err!(format!("Unexpected push token received from bitwarden server: {e}")), |
|
|
|
}; |
|
|
|
|
|
|
|
let mut push_token = PUSH_TOKEN.write().await; |
|
|
|
push_token.valid_until = Instant::now() |
|
|
|
let mut api_token = API_TOKEN.write().await; |
|
|
|
api_token.valid_until = Instant::now() |
|
|
|
.checked_add(Duration::new((json_pushtoken.expires_in / 2) as u64, 0)) // Token valid for half the specified time
|
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
push_token.access_token = json_pushtoken.access_token; |
|
|
|
api_token.access_token = json_pushtoken.access_token; |
|
|
|
|
|
|
|
debug!("Token still valid for {}", push_token.valid_until.saturating_duration_since(Instant::now()).as_secs()); |
|
|
|
Ok(push_token.access_token.clone()) |
|
|
|
debug!("Token still valid for {}", api_token.valid_until.saturating_duration_since(Instant::now()).as_secs()); |
|
|
|
Ok(api_token.access_token.clone()) |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn register_push_device(device: &mut Device, conn: &mut crate::db::DbConn) -> EmptyResult { |
|
|
|
if !CONFIG.push_enabled() || !device.is_push_device() || device.is_registered() { |
|
|
|
if !CONFIG.push_enabled() || !device.is_push_device() { |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
|
|
|
|
if device.push_token.is_none() { |
|
|
|
warn!("Skipping the registration of the device {} because the push_token field is empty.", device.uuid); |
|
|
|
warn!("To get rid of this message you need to clear the app data and reconnect the device."); |
|
|
|
warn!("Skipping the registration of the device {:?} because the push_token field is empty.", device.uuid); |
|
|
|
warn!("To get rid of this message you need to logout, clear the app data and login again on the device."); |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
|
|
|
|
debug!("Registering Device {}", device.uuid); |
|
|
|
debug!("Registering Device {:?}", device.push_uuid); |
|
|
|
|
|
|
|
// generate a random push_uuid so we know the device is registered
|
|
|
|
device.push_uuid = Some(PushId(get_uuid())); |
|
|
|
// Generate a random push_uuid so if it doesn't already have one
|
|
|
|
if device.push_uuid.is_none() { |
|
|
|
device.push_uuid = Some(PushId(get_uuid())); |
|
|
|
} |
|
|
|
|
|
|
|
//Needed to register a device for push to bitwarden :
|
|
|
|
let data = json!({ |
|
|
|
"deviceId": device.push_uuid, // Unique UUID per user/device
|
|
|
|
"pushToken": device.push_token, |
|
|
|
"userId": device.user_uuid, |
|
|
|
"deviceId": device.push_uuid, |
|
|
|
"identifier": device.uuid, |
|
|
|
"type": device.atype, |
|
|
|
"pushToken": device.push_token |
|
|
|
"identifier": device.uuid, // Unique UUID of the device/app, determined by the device/app it self currently registering
|
|
|
|
// "organizationIds:" [] // TODO: This is not yet implemented by Vaultwarden!
|
|
|
|
"installationId": CONFIG.push_installation_id(), |
|
|
|
}); |
|
|
|
|
|
|
|
let auth_push_token = get_auth_push_token().await?; |
|
|
|
let auth_header = format!("Bearer {}", &auth_push_token); |
|
|
|
let auth_api_token = get_auth_api_token().await?; |
|
|
|
let auth_header = format!("Bearer {auth_api_token}"); |
|
|
|
|
|
|
|
if let Err(e) = make_http_request(Method::POST, &(CONFIG.push_relay_uri() + "/push/register"))? |
|
|
|
.header(CONTENT_TYPE, "application/json") |
|
|
@ -126,18 +130,21 @@ pub async fn register_push_device(device: &mut Device, conn: &mut crate::db::DbC |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn unregister_push_device(push_id: Option<PushId>) -> EmptyResult { |
|
|
|
pub async fn unregister_push_device(push_id: &Option<PushId>) -> EmptyResult { |
|
|
|
if !CONFIG.push_enabled() || push_id.is_none() { |
|
|
|
return Ok(()); |
|
|
|
} |
|
|
|
let auth_push_token = get_auth_push_token().await?; |
|
|
|
let auth_api_token = get_auth_api_token().await?; |
|
|
|
|
|
|
|
let auth_header = format!("Bearer {}", &auth_push_token); |
|
|
|
let auth_header = format!("Bearer {auth_api_token}"); |
|
|
|
|
|
|
|
match make_http_request(Method::DELETE, &format!("{}/push/{}", CONFIG.push_relay_uri(), push_id.unwrap()))? |
|
|
|
.header(AUTHORIZATION, auth_header) |
|
|
|
.send() |
|
|
|
.await |
|
|
|
match make_http_request( |
|
|
|
Method::POST, |
|
|
|
&format!("{}/push/delete/{}", CONFIG.push_relay_uri(), push_id.as_ref().unwrap()), |
|
|
|
)? |
|
|
|
.header(AUTHORIZATION, auth_header) |
|
|
|
.send() |
|
|
|
.await |
|
|
|
{ |
|
|
|
Ok(r) => r, |
|
|
|
Err(e) => err!(format!("An error occurred during device unregistration: {e}")), |
|
|
@ -145,12 +152,7 @@ pub async fn unregister_push_device(push_id: Option<PushId>) -> EmptyResult { |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn push_cipher_update( |
|
|
|
ut: UpdateType, |
|
|
|
cipher: &Cipher, |
|
|
|
acting_device_id: &DeviceId, |
|
|
|
conn: &mut crate::db::DbConn, |
|
|
|
) { |
|
|
|
pub async fn push_cipher_update(ut: UpdateType, cipher: &Cipher, device: &Device, conn: &mut crate::db::DbConn) { |
|
|
|
// 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.
|
|
|
|
if cipher.organization_uuid.is_some() { |
|
|
|
return; |
|
|
@ -163,87 +165,97 @@ pub async fn push_cipher_update( |
|
|
|
if Device::check_user_has_push_device(user_id, conn).await { |
|
|
|
send_to_push_relay(json!({ |
|
|
|
"userId": user_id, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": acting_device_id, |
|
|
|
"identifier": acting_device_id, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": device.push_uuid, // Should be the records unique uuid of the acting device (unique uuid per user/device)
|
|
|
|
"identifier": device.uuid, // Should be the acting device id (aka uuid per device/app)
|
|
|
|
"type": ut as i32, |
|
|
|
"payload": { |
|
|
|
"Id": cipher.uuid, |
|
|
|
"UserId": cipher.user_uuid, |
|
|
|
"OrganizationId": (), |
|
|
|
"RevisionDate": format_date(&cipher.updated_at) |
|
|
|
} |
|
|
|
"id": cipher.uuid, |
|
|
|
"userId": cipher.user_uuid, |
|
|
|
"organizationId": null, |
|
|
|
"collectionIds": null, |
|
|
|
"revisionDate": format_date(&cipher.updated_at) |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
})) |
|
|
|
.await; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn push_logout(user: &User, acting_device_id: Option<DeviceId>) { |
|
|
|
pub async fn push_logout(user: &User, acting_device_id: Option<DeviceId>, conn: &mut crate::db::DbConn) { |
|
|
|
let acting_device_id: Value = acting_device_id.map(|v| v.to_string().into()).unwrap_or_else(|| Value::Null); |
|
|
|
|
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user.uuid, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": acting_device_id, |
|
|
|
"identifier": acting_device_id, |
|
|
|
"type": UpdateType::LogOut as i32, |
|
|
|
"payload": { |
|
|
|
"UserId": user.uuid, |
|
|
|
"Date": format_date(&user.updated_at) |
|
|
|
} |
|
|
|
}))); |
|
|
|
if Device::check_user_has_push_device(&user.uuid, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user.uuid, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": acting_device_id, |
|
|
|
"identifier": acting_device_id, |
|
|
|
"type": UpdateType::LogOut as i32, |
|
|
|
"payload": { |
|
|
|
"userId": user.uuid, |
|
|
|
"date": format_date(&user.updated_at) |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub fn push_user_update(ut: UpdateType, user: &User) { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user.uuid, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": (), |
|
|
|
"identifier": (), |
|
|
|
"type": ut as i32, |
|
|
|
"payload": { |
|
|
|
"UserId": user.uuid, |
|
|
|
"Date": format_date(&user.updated_at) |
|
|
|
} |
|
|
|
}))); |
|
|
|
pub async fn push_user_update(ut: UpdateType, user: &User, push_uuid: &Option<PushId>, conn: &mut crate::db::DbConn) { |
|
|
|
if Device::check_user_has_push_device(&user.uuid, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user.uuid, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": push_uuid, |
|
|
|
"identifier": null, |
|
|
|
"type": ut as i32, |
|
|
|
"payload": { |
|
|
|
"userId": user.uuid, |
|
|
|
"date": format_date(&user.updated_at) |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn push_folder_update( |
|
|
|
ut: UpdateType, |
|
|
|
folder: &Folder, |
|
|
|
acting_device_id: &DeviceId, |
|
|
|
conn: &mut crate::db::DbConn, |
|
|
|
) { |
|
|
|
pub async fn push_folder_update(ut: UpdateType, folder: &Folder, device: &Device, conn: &mut crate::db::DbConn) { |
|
|
|
if Device::check_user_has_push_device(&folder.user_uuid, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": folder.user_uuid, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": acting_device_id, |
|
|
|
"identifier": acting_device_id, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": device.push_uuid, // Should be the records unique uuid of the acting device (unique uuid per user/device)
|
|
|
|
"identifier": device.uuid, // Should be the acting device id (aka uuid per device/app)
|
|
|
|
"type": ut as i32, |
|
|
|
"payload": { |
|
|
|
"Id": folder.uuid, |
|
|
|
"UserId": folder.user_uuid, |
|
|
|
"RevisionDate": format_date(&folder.updated_at) |
|
|
|
} |
|
|
|
"id": folder.uuid, |
|
|
|
"userId": folder.user_uuid, |
|
|
|
"revisionDate": format_date(&folder.updated_at) |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn push_send_update(ut: UpdateType, send: &Send, acting_device_id: &DeviceId, conn: &mut crate::db::DbConn) { |
|
|
|
pub async fn push_send_update(ut: UpdateType, send: &Send, device: &Device, conn: &mut crate::db::DbConn) { |
|
|
|
if let Some(s) = &send.user_uuid { |
|
|
|
if Device::check_user_has_push_device(s, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": send.user_uuid, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": acting_device_id, |
|
|
|
"identifier": acting_device_id, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": device.push_uuid, // Should be the records unique uuid of the acting device (unique uuid per user/device)
|
|
|
|
"identifier": device.uuid, // Should be the acting device id (aka uuid per device/app)
|
|
|
|
"type": ut as i32, |
|
|
|
"payload": { |
|
|
|
"Id": send.uuid, |
|
|
|
"UserId": send.user_uuid, |
|
|
|
"RevisionDate": format_date(&send.revision_date) |
|
|
|
} |
|
|
|
"id": send.uuid, |
|
|
|
"userId": send.user_uuid, |
|
|
|
"revisionDate": format_date(&send.revision_date) |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
@ -254,7 +266,7 @@ async fn send_to_push_relay(notification_data: Value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
let auth_push_token = match get_auth_push_token().await { |
|
|
|
let auth_api_token = match get_auth_api_token().await { |
|
|
|
Ok(s) => s, |
|
|
|
Err(e) => { |
|
|
|
debug!("Could not get the auth push token: {e}"); |
|
|
@ -262,7 +274,7 @@ async fn send_to_push_relay(notification_data: Value) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let auth_header = format!("Bearer {}", &auth_push_token); |
|
|
|
let auth_header = format!("Bearer {auth_api_token}"); |
|
|
|
|
|
|
|
let req = match make_http_request(Method::POST, &(CONFIG.push_relay_uri() + "/push/send")) { |
|
|
|
Ok(r) => r, |
|
|
@ -284,18 +296,20 @@ async fn send_to_push_relay(notification_data: Value) { |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
pub async fn push_auth_request(user_id: UserId, auth_request_id: String, conn: &mut crate::db::DbConn) { |
|
|
|
if Device::check_user_has_push_device(&user_id, conn).await { |
|
|
|
pub async fn push_auth_request(user_id: &UserId, auth_request_id: &str, device: &Device, conn: &mut crate::db::DbConn) { |
|
|
|
if Device::check_user_has_push_device(user_id, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user_id, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": null, |
|
|
|
"identifier": null, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": device.push_uuid, // Should be the records unique uuid of the acting device (unique uuid per user/device)
|
|
|
|
"identifier": device.uuid, // Should be the acting device id (aka uuid per device/app)
|
|
|
|
"type": UpdateType::AuthRequest as i32, |
|
|
|
"payload": { |
|
|
|
"Id": auth_request_id, |
|
|
|
"UserId": user_id, |
|
|
|
} |
|
|
|
"userId": user_id, |
|
|
|
"id": auth_request_id, |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
@ -303,20 +317,22 @@ pub async fn push_auth_request(user_id: UserId, auth_request_id: String, conn: & |
|
|
|
pub async fn push_auth_response( |
|
|
|
user_id: &UserId, |
|
|
|
auth_request_id: &AuthRequestId, |
|
|
|
approving_device_id: &DeviceId, |
|
|
|
device: &Device, |
|
|
|
conn: &mut crate::db::DbConn, |
|
|
|
) { |
|
|
|
if Device::check_user_has_push_device(user_id, conn).await { |
|
|
|
tokio::task::spawn(send_to_push_relay(json!({ |
|
|
|
"userId": user_id, |
|
|
|
"organizationId": (), |
|
|
|
"deviceId": approving_device_id, |
|
|
|
"identifier": approving_device_id, |
|
|
|
"organizationId": null, |
|
|
|
"deviceId": device.push_uuid, // Should be the records unique uuid of the acting device (unique uuid per user/device)
|
|
|
|
"identifier": device.uuid, // Should be the acting device id (aka uuid per device/app)
|
|
|
|
"type": UpdateType::AuthRequestResponse as i32, |
|
|
|
"payload": { |
|
|
|
"Id": auth_request_id, |
|
|
|
"UserId": user_id, |
|
|
|
} |
|
|
|
"userId": user_id, |
|
|
|
"id": auth_request_id, |
|
|
|
}, |
|
|
|
"clientType": null, |
|
|
|
"installationId": null |
|
|
|
}))); |
|
|
|
} |
|
|
|
} |
|
|
|