From 029008bad519186d1528526f523c0220aa58ac2a Mon Sep 17 00:00:00 2001
From: Jeremy Lin <jeremy.lin@gmail.com>
Date: Wed, 12 May 2021 00:54:28 -0700
Subject: [PATCH] Add support for the `Send Options` policy

Upstream refs:

* https://github.com/bitwarden/server/pull/1234
* https://bitwarden.com/help/article/policies/#send-options
---
 src/api/core/sends.rs       | 21 +++++++++++++++++++++
 src/db/models/org_policy.rs | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs
index 21ddfcd6..5022af75 100644
--- a/src/api/core/sends.rs
+++ b/src/api/core/sends.rs
@@ -61,6 +61,24 @@ fn enforce_disable_send_policy(headers: &Headers, conn: &DbConn) -> EmptyResult
     Ok(())
 }
 
+/// Enforces the `DisableHideEmail` option of the `Send Options` policy.
+/// A non-owner/admin user belonging to an org with this option enabled isn't
+/// allowed to hide their email address from the recipient of a Bitwarden Send,
+/// but is allowed to remove this option from an existing Send.
+///
+/// Ref: https://bitwarden.com/help/article/policies/#send-options
+fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, conn: &DbConn) -> EmptyResult {
+    let user_uuid = &headers.user.uuid;
+    let hide_email = data.HideEmail.unwrap_or(false);
+    if hide_email && OrgPolicy::is_hide_email_disabled(user_uuid, conn) {
+        err!(
+            "Due to an Enterprise Policy, you are not allowed to hide your email address \
+              from recipients when creating or editing a Send."
+        )
+    }
+    Ok(())
+}
+
 fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> {
     let data_val = if data.Type == SendType::Text as i32 {
         data.Text
@@ -102,6 +120,7 @@ fn post_send(data: JsonUpcase<SendData>, headers: Headers, conn: DbConn, nt: Not
     enforce_disable_send_policy(&headers, &conn)?;
 
     let data: SendData = data.into_inner().data;
+    enforce_disable_hide_email_policy(&data, &headers, &conn)?;
 
     if data.Type == SendType::File as i32 {
         err!("File sends should use /api/sends/file")
@@ -132,6 +151,7 @@ fn post_send_file(data: Data, content_type: &ContentType, headers: Headers, conn
     let mut buf = String::new();
     model_entry.data.read_to_string(&mut buf)?;
     let data = serde_json::from_str::<crate::util::UpCase<SendData>>(&buf)?;
+    enforce_disable_hide_email_policy(&data.data, &headers, &conn)?;
 
     // Get the file length and add an extra 10% to avoid issues
     const SIZE_110_MB: u64 = 115_343_360;
@@ -305,6 +325,7 @@ fn put_send(id: String, data: JsonUpcase<SendData>, headers: Headers, conn: DbCo
     enforce_disable_send_policy(&headers, &conn)?;
 
     let data: SendData = data.into_inner().data;
+    enforce_disable_hide_email_policy(&data, &headers, &conn)?;
 
     let mut send = match Send::find_by_uuid(&id, &conn) {
         Some(s) => s,
diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs
index 1a040fc1..06d7a5fd 100644
--- a/src/db/models/org_policy.rs
+++ b/src/db/models/org_policy.rs
@@ -1,8 +1,10 @@
+use serde::Deserialize;
 use serde_json::Value;
 
 use crate::api::EmptyResult;
 use crate::db::DbConn;
 use crate::error::MapResult;
+use crate::util::UpCase;
 
 use super::{Organization, UserOrgStatus, UserOrgType, UserOrganization};
 
@@ -29,6 +31,14 @@ pub enum OrgPolicyType {
     // RequireSso = 4, // Not currently supported.
     PersonalOwnership = 5,
     DisableSend = 6,
+    SendOptions = 7,
+}
+
+// https://github.com/bitwarden/server/blob/master/src/Core/Models/Data/SendOptionsPolicyData.cs
+#[derive(Deserialize)]
+#[allow(non_snake_case)]
+pub struct SendOptionsPolicyData {
+    pub DisableHideEmail: bool,
 }
 
 /// Local methods
@@ -188,6 +198,30 @@ impl OrgPolicy {
         false
     }
 
+    /// Returns true if the user belongs to an org that has enabled the `DisableHideEmail`
+    /// option of the `Send Options` policy, and the user is not an owner or admin of that org.
+    pub fn is_hide_email_disabled(user_uuid: &str, conn: &DbConn) -> bool {
+        // Returns confirmed users only.
+        for policy in OrgPolicy::find_by_user(user_uuid, conn) {
+            if policy.enabled && policy.has_type(OrgPolicyType::SendOptions) {
+                let org_uuid = &policy.org_uuid;
+                if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) {
+                    if user.atype < UserOrgType::Admin {
+                        match serde_json::from_str::<UpCase<SendOptionsPolicyData>>(&policy.data) {
+                            Ok(opts) => {
+                                if opts.data.DisableHideEmail {
+                                    return true;
+                                }
+                            }
+                            _ => error!("Failed to deserialize policy data: {}", policy.data),
+                        }
+                    }
+                }
+            }
+        }
+        false
+    }
+
     /*pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult {
         db_run! { conn: {
             diesel::delete(twofactor::table.filter(twofactor::user_uuid.eq(user_uuid)))