|
|
@ -16,7 +16,7 @@ use crate::{ |
|
|
config::PathType, |
|
|
config::PathType, |
|
|
db::{ |
|
|
db::{ |
|
|
DbConn, DbPool, |
|
|
DbConn, DbPool, |
|
|
models::{Device, OrgPolicy, OrgPolicyType, Send, SendFileId, SendId, SendType, UserId}, |
|
|
models::{Device, OrgPolicy, OrgPolicyType, Send, SendFileId, SendId, SendType, SendWhoCanAccessType, UserId}, |
|
|
}, |
|
|
}, |
|
|
util::{NumberOrString, save_temp_file}, |
|
|
util::{NumberOrString, save_temp_file}, |
|
|
}; |
|
|
}; |
|
|
@ -126,6 +126,72 @@ async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, c |
|
|
Ok(()) |
|
|
Ok(()) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Enforces the restrictions that only exist on the `Send controls` policy. `DisableSend` and
|
|
|
|
|
|
/// `DisableHideEmail` stay with the two functions above, which are mirrored into `Send controls`.
|
|
|
|
|
|
/// `existing` is the Send being updated: the client may leave the password out to keep the existing
|
|
|
|
|
|
/// one, and the deletion date is measured against the date the Send was originally created on.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Ref: https://github.com/bitwarden/server/blob/main/src/Core/Tools/SendFeatures/Services/SendValidationService.cs
|
|
|
|
|
|
async fn enforce_send_controls_policy( |
|
|
|
|
|
data: &SendData, |
|
|
|
|
|
existing: Option<&Send>, |
|
|
|
|
|
headers: &Headers, |
|
|
|
|
|
conn: &DbConn, |
|
|
|
|
|
) -> EmptyResult { |
|
|
|
|
|
let has_password = data.password.is_some() || existing.is_some_and(|s| s.password_hash.is_some()); |
|
|
|
|
|
let creation_date = existing.map_or_else(Utc::now, |s| s.creation_date.and_utc()); |
|
|
|
|
|
let controls = OrgPolicy::send_controls_for_user(&headers.user.uuid, conn).await; |
|
|
|
|
|
|
|
|
|
|
|
match controls.required_access_type() { |
|
|
|
|
|
Some(SendWhoCanAccessType::PasswordProtected) if !has_password => { |
|
|
|
|
|
err!("Due to an Enterprise Policy, your Sends have to be protected by a password.") |
|
|
|
|
|
} |
|
|
|
|
|
// Vaultwarden rejects Sends carrying recipient emails, so this can never be satisfied.
|
|
|
|
|
|
// `put_policy` refuses to store such a policy; this only guards rows written outside it.
|
|
|
|
|
|
Some(SendWhoCanAccessType::SpecificPeople) => { |
|
|
|
|
|
err!( |
|
|
|
|
|
"Due to an Enterprise Policy, your Sends have to be protected by email verification, \ |
|
|
|
|
|
which is not supported." |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
_ => (), |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if let Some(allowed_types) = &controls.allowed_send_types |
|
|
|
|
|
&& !allowed_types.contains(&data.r#type) |
|
|
|
|
|
{ |
|
|
|
|
|
err!("Due to an Enterprise Policy, your Sends have to be of a type the organization allows.") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if let Some(hours) = controls.deletion_hours { |
|
|
|
|
|
// Upstream allows for up to a minute of skew between the deletion and the creation date.
|
|
|
|
|
|
if send_exceeds_max_lifetime(data.deletion_date, creation_date, hours) { |
|
|
|
|
|
err!(format!( |
|
|
|
|
|
"Due to an Enterprise Policy, the deletion date of your Sends has to be within {hours} hours of their creation date." |
|
|
|
|
|
)) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn send_exceeds_max_lifetime(deletion_date: DateTime<Utc>, creation_date: DateTime<Utc>, max_hours: i32) -> bool { |
|
|
|
|
|
deletion_date - creation_date - TimeDelta::minutes(1) > TimeDelta::hours(i64::from(max_hours)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
|
mod send_controls_tests { |
|
|
|
|
|
use super::*; |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
fn send_max_lifetime_respects_exact_one_minute_tolerance() { |
|
|
|
|
|
let created = Utc::now(); |
|
|
|
|
|
let limit = created + TimeDelta::hours(1) + TimeDelta::minutes(1); |
|
|
|
|
|
assert!(!send_exceeds_max_lifetime(limit, created, 1)); |
|
|
|
|
|
assert!(send_exceeds_max_lifetime(limit + TimeDelta::milliseconds(1), created, 1)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> { |
|
|
fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> { |
|
|
let data_val = if data.r#type == SendType::Text as i32 { |
|
|
let data_val = if data.r#type == SendType::Text as i32 { |
|
|
data.text |
|
|
data.text |
|
|
@ -196,6 +262,7 @@ async fn post_send(data: Json<SendData>, headers: Headers, conn: DbConn, nt: Not |
|
|
|
|
|
|
|
|
let data: SendData = data.into_inner(); |
|
|
let data: SendData = data.into_inner(); |
|
|
enforce_disable_hide_email_policy(&data, &headers, &conn).await?; |
|
|
enforce_disable_hide_email_policy(&data, &headers, &conn).await?; |
|
|
|
|
|
enforce_send_controls_policy(&data, None, &headers, &conn).await?; |
|
|
|
|
|
|
|
|
if data.r#type == SendType::File as i32 { |
|
|
if data.r#type == SendType::File as i32 { |
|
|
err!("File sends should use /api/sends/file/v2") |
|
|
err!("File sends should use /api/sends/file/v2") |
|
|
@ -232,6 +299,7 @@ async fn post_send_file_v2(data: Json<SendData>, headers: Headers, conn: DbConn) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
enforce_disable_hide_email_policy(&data, &headers, &conn).await?; |
|
|
enforce_disable_hide_email_policy(&data, &headers, &conn).await?; |
|
|
|
|
|
enforce_send_controls_policy(&data, None, &headers, &conn).await?; |
|
|
|
|
|
|
|
|
let file_length = if let Some(m) = &data.file_length { |
|
|
let file_length = if let Some(m) = &data.file_length { |
|
|
m.into_i64()? |
|
|
m.into_i64()? |
|
|
@ -477,6 +545,8 @@ async fn put_send(send_id: SendId, data: Json<SendData>, headers: Headers, conn: |
|
|
err!("Sends with email verification is not supported"); |
|
|
err!("Sends with email verification is not supported"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
enforce_send_controls_policy(&data, Some(&send), &headers, &conn).await?; |
|
|
|
|
|
|
|
|
update_send_from_data(&mut send, data, &headers, &conn, &nt, UpdateType::SyncSendUpdate).await?; |
|
|
update_send_from_data(&mut send, data, &headers, &conn, &nt, UpdateType::SyncSendUpdate).await?; |
|
|
|
|
|
|
|
|
Ok(Json(send.to_json())) |
|
|
Ok(Json(send.to_json())) |
|
|
@ -563,6 +633,13 @@ async fn delete_send(send_id: SendId, headers: Headers, conn: DbConn, nt: Notify |
|
|
async fn put_remove_password(send_id: SendId, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { |
|
|
async fn put_remove_password(send_id: SendId, headers: Headers, conn: DbConn, nt: Notify<'_>) -> JsonResult { |
|
|
enforce_disable_send_policy(&headers, &conn).await?; |
|
|
enforce_disable_send_policy(&headers, &conn).await?; |
|
|
|
|
|
|
|
|
|
|
|
// Removing the password would leave the Send non compliant with a policy that requires one.
|
|
|
|
|
|
if OrgPolicy::send_controls_for_user(&headers.user.uuid, &conn).await.required_access_type() |
|
|
|
|
|
== Some(SendWhoCanAccessType::PasswordProtected) |
|
|
|
|
|
{ |
|
|
|
|
|
err!("Due to an Enterprise Policy, your Sends have to be protected by a password.") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
let Some(mut send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &conn).await else { |
|
|
let Some(mut send) = Send::find_by_uuid_and_user(&send_id, &headers.user.uuid, &conn).await else { |
|
|
err!("Send not found", "Invalid send uuid, or does not belong to user") |
|
|
err!("Send not found", "Invalid send uuid, or does not belong to user") |
|
|
}; |
|
|
}; |
|
|
|