vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit 8e6c6a1dc43b8993ee8814e6339d955b144d5fe9
parent 7a9cfc45da4cdf3c85ab4363295bb505884f2d76
Author: Daniel GarcĂ­a <dani-garcia@users.noreply.github.com>
Date:   Wed, 12 May 2021 23:05:53 +0200

Merge pull request #1689 from jjlin/hide-email

Add support for hiding the sender's email address in Bitwarden Sends
Diffstat:
Amigrations/mysql/2021-05-11-205202_add_hide_email/down.sql | 0
Amigrations/mysql/2021-05-11-205202_add_hide_email/up.sql | 2++
Amigrations/postgresql/2021-05-11-205202_add_hide_email/down.sql | 0
Amigrations/postgresql/2021-05-11-205202_add_hide_email/up.sql | 2++
Amigrations/sqlite/2021-05-11-205202_add_hide_email/down.sql | 0
Amigrations/sqlite/2021-05-11-205202_add_hide_email/up.sql | 2++
Msrc/api/core/sends.rs | 26+++++++++++++++++++++++++-
Msrc/db/models/org_policy.rs | 34++++++++++++++++++++++++++++++++++
Msrc/db/models/send.rs | 22+++++++++++++++++++++-
Msrc/db/schemas/mysql/schema.rs | 1+
Msrc/db/schemas/postgresql/schema.rs | 1+
Msrc/db/schemas/sqlite/schema.rs | 1+
12 files changed, 89 insertions(+), 2 deletions(-)

diff --git a/migrations/mysql/2021-05-11-205202_add_hide_email/down.sql b/migrations/mysql/2021-05-11-205202_add_hide_email/down.sql diff --git a/migrations/mysql/2021-05-11-205202_add_hide_email/up.sql b/migrations/mysql/2021-05-11-205202_add_hide_email/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE sends +ADD COLUMN hide_email BOOLEAN; diff --git a/migrations/postgresql/2021-05-11-205202_add_hide_email/down.sql b/migrations/postgresql/2021-05-11-205202_add_hide_email/down.sql diff --git a/migrations/postgresql/2021-05-11-205202_add_hide_email/up.sql b/migrations/postgresql/2021-05-11-205202_add_hide_email/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE sends +ADD COLUMN hide_email BOOLEAN; diff --git a/migrations/sqlite/2021-05-11-205202_add_hide_email/down.sql b/migrations/sqlite/2021-05-11-205202_add_hide_email/down.sql diff --git a/migrations/sqlite/2021-05-11-205202_add_hide_email/up.sql b/migrations/sqlite/2021-05-11-205202_add_hide_email/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE sends +ADD COLUMN hide_email BOOLEAN; diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs @@ -38,6 +38,7 @@ pub struct SendData { pub ExpirationDate: Option<DateTime<Utc>>, pub DeletionDate: DateTime<Utc>, pub Disabled: bool, + pub HideEmail: Option<bool>, // Data field pub Name: String, @@ -63,6 +64,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 @@ -91,6 +110,7 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult<Send> { send.max_access_count = data.MaxAccessCount; send.expiration_date = data.ExpirationDate.map(|d| d.naive_utc()); send.disabled = data.Disabled; + send.hide_email = data.HideEmail; send.atype = data.Type; send.set_password(data.Password.as_deref()); @@ -103,6 +123,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") @@ -133,6 +154,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; @@ -246,7 +268,7 @@ fn post_access(access_id: String, data: JsonUpcase<SendAccessData>, conn: DbConn send.save(&conn)?; - Ok(Json(send.to_json_access())) + Ok(Json(send.to_json_access(&conn))) } #[post("/sends/<send_id>/access/file/<file_id>", data = "<data>")] @@ -306,6 +328,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, @@ -343,6 +366,7 @@ fn put_send(id: String, data: JsonUpcase<SendData>, headers: Headers, conn: DbCo send.notes = data.Notes; send.max_access_count = data.MaxAccessCount; send.expiration_date = data.ExpirationDate.map(|d| d.naive_utc()); + send.hide_email = data.HideEmail; send.disabled = data.Disabled; // Only change the value if it's present diff --git 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))) diff --git a/src/db/models/send.rs b/src/db/models/send.rs @@ -36,6 +36,7 @@ db_object! { pub deletion_date: NaiveDateTime, pub disabled: bool, + pub hide_email: Option<bool>, } } @@ -73,6 +74,7 @@ impl Send { deletion_date, disabled: false, + hide_email: None, } } @@ -101,6 +103,22 @@ impl Send { } } + pub fn creator_identifier(&self, conn: &DbConn) -> Option<String> { + if let Some(hide_email) = self.hide_email { + if hide_email { + return None; + } + } + + if let Some(user_uuid) = &self.user_uuid { + if let Some(user) = User::find_by_uuid(user_uuid, conn) { + return Some(user.email); + } + } + + None + } + pub fn to_json(&self) -> Value { use crate::util::format_date; use data_encoding::BASE64URL_NOPAD; @@ -123,6 +141,7 @@ impl Send { "AccessCount": self.access_count, "Password": self.password_hash.as_deref().map(|h| BASE64URL_NOPAD.encode(h)), "Disabled": self.disabled, + "HideEmail": self.hide_email, "RevisionDate": format_date(&self.revision_date), "ExpirationDate": self.expiration_date.as_ref().map(format_date), @@ -131,7 +150,7 @@ impl Send { }) } - pub fn to_json_access(&self) -> Value { + pub fn to_json_access(&self, conn: &DbConn) -> Value { use crate::util::format_date; let data: Value = serde_json::from_str(&self.data).unwrap_or_default(); @@ -145,6 +164,7 @@ impl Send { "File": if self.atype == SendType::File as i32 { Some(&data) } else { None }, "ExpirationDate": self.expiration_date.as_ref().map(format_date), + "CreatorIdentifier": self.creator_identifier(conn), "Object": "send-access", }) } diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs @@ -123,6 +123,7 @@ table! { expiration_date -> Nullable<Datetime>, deletion_date -> Datetime, disabled -> Bool, + hide_email -> Nullable<Bool>, } } diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs @@ -123,6 +123,7 @@ table! { expiration_date -> Nullable<Timestamp>, deletion_date -> Timestamp, disabled -> Bool, + hide_email -> Nullable<Bool>, } } diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs @@ -123,6 +123,7 @@ table! { expiration_date -> Nullable<Timestamp>, deletion_date -> Timestamp, disabled -> Bool, + hide_email -> Nullable<Bool>, } }