From 424d666a505c3e41bc1e77937a682e120e130423 Mon Sep 17 00:00:00 2001 From: Jeremy Lin Date: Tue, 16 Mar 2021 02:07:45 -0700 Subject: [PATCH] Add support for the Disable Send policy Upstream refs: * https://github.com/bitwarden/server/pull/1130 * https://bitwarden.com/help/article/policies/#disable-send --- src/api/core/ciphers.rs | 17 ++++------------- src/api/core/sends.rs | 22 ++++++++++++++++++++++ src/db/models/org_policy.rs | 22 ++++++++++++++++++++-- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index fe815401..c9abb3d4 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -285,19 +285,10 @@ fn enforce_personal_ownership_policy( ) -> EmptyResult { if data.OrganizationId.is_none() { let user_uuid = &headers.user.uuid; - for policy in OrgPolicy::find_by_user(user_uuid, conn) { - if policy.enabled && policy.has_type(OrgPolicyType::PersonalOwnership) { - let org_uuid = &policy.org_uuid; - match UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn) { - Some(user) => - if user.atype < UserOrgType::Admin && - user.has_status(UserOrgStatus::Confirmed) { - err!("Due to an Enterprise Policy, you are restricted \ - from saving items to your personal vault.") - }, - None => err!("Error looking up user type"), - } - } + let policy_type = OrgPolicyType::PersonalOwnership; + if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) { + err!("Due to an Enterprise Policy, you are restricted from \ + saving items to your personal vault.") } } Ok(()) diff --git a/src/api/core/sends.rs b/src/api/core/sends.rs index b69c4309..4941307a 100644 --- a/src/api/core/sends.rs +++ b/src/api/core/sends.rs @@ -43,6 +43,20 @@ pub struct SendData { pub File: Option, } +/// Enforces the `Disable Send` policy. A non-owner/admin user belonging to +/// an org with this policy enabled isn't allowed to create new Sends or +/// modify existing ones, but is allowed to delete them. +/// +/// Ref: https://bitwarden.com/help/article/policies/#disable-send +fn enforce_disable_send_policy(headers: &Headers,conn: &DbConn) -> EmptyResult { + let user_uuid = &headers.user.uuid; + let policy_type = OrgPolicyType::DisableSend; + if OrgPolicy::is_applicable_to_user(user_uuid, policy_type, conn) { + err!("Due to an Enterprise Policy, you are only able to delete an existing Send.") + } + Ok(()) +} + fn create_send(data: SendData, user_uuid: String) -> ApiResult { let data_val = if data.Type == SendType::Text as i32 { data.Text @@ -80,6 +94,8 @@ fn create_send(data: SendData, user_uuid: String) -> ApiResult { #[post("/sends", data = "")] fn post_send(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { + enforce_disable_send_policy(&headers, &conn)?; + let data: SendData = data.into_inner().data; if data.Type == SendType::File as i32 { @@ -95,6 +111,8 @@ fn post_send(data: JsonUpcase, headers: Headers, conn: DbConn, nt: Not #[post("/sends/file", format = "multipart/form-data", data = "")] fn post_send_file(data: Data, content_type: &ContentType, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { + enforce_disable_send_policy(&headers, &conn)?; + let boundary = content_type.params().next().expect("No boundary provided").1; let mut mpart = Multipart::with_body(data.open(), boundary); @@ -288,6 +306,8 @@ fn post_access_file( #[put("/sends/", data = "")] fn put_send(id: String, data: JsonUpcase, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { + enforce_disable_send_policy(&headers, &conn)?; + let data: SendData = data.into_inner().data; let mut send = match Send::find_by_uuid(&id, &conn) { @@ -366,6 +386,8 @@ fn delete_send(id: String, headers: Headers, conn: DbConn, nt: Notify) -> EmptyR #[put("/sends//remove-password")] fn put_remove_password(id: String, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { + enforce_disable_send_policy(&headers, &conn)?; + let mut send = match Send::find_by_uuid(&id, &conn) { Some(s) => s, None => err!("Send not found"), diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 65569b48..0707eccc 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -4,7 +4,7 @@ use crate::api::EmptyResult; use crate::db::DbConn; use crate::error::MapResult; -use super::{Organization, UserOrgStatus}; +use super::{Organization, UserOrganization, UserOrgStatus, UserOrgType}; db_object! { #[derive(Identifiable, Queryable, Insertable, Associations, AsChangeset)] @@ -20,7 +20,7 @@ db_object! { } } -#[allow(dead_code)] +#[derive(Copy, Clone)] #[derive(num_derive::FromPrimitive)] pub enum OrgPolicyType { TwoFactorAuthentication = 0, @@ -29,6 +29,7 @@ pub enum OrgPolicyType { // SingleOrg = 3, // Not currently supported. // RequireSso = 4, // Not currently supported. PersonalOwnership = 5, + DisableSend = 6, } /// Local methods @@ -170,6 +171,23 @@ impl OrgPolicy { }} } + /// Returns true if the user belongs to an org that has enabled the specified policy type, + /// and the user is not an owner or admin of that org. This is only useful for checking + /// applicability of policy types that have these particular semantics. + pub fn is_applicable_to_user(user_uuid: &str, policy_type: OrgPolicyType, conn: &DbConn) -> bool { + for policy in OrgPolicy::find_by_user(user_uuid, conn) { // Returns confirmed users only. + if policy.enabled && policy.has_type(policy_type) { + 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 { + return true; + } + } + } + } + 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)))