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

org_policy.rs (9904B)


      1 use super::{UserOrgStatus, UserOrgType, UserOrganization};
      2 use crate::api::EmptyResult;
      3 use crate::db::models::TwoFactorType;
      4 use crate::db::DbConn;
      5 use crate::error::{Error, MapResult};
      6 use crate::util;
      7 use diesel::result::{self, DatabaseErrorKind};
      8 use serde::Deserialize;
      9 use serde_json::Value;
     10 
     11 db_object! {
     12     #[derive(AsChangeset, Insertable, Queryable)]
     13     #[diesel(table_name = org_policies)]
     14     pub struct OrgPolicy {
     15         uuid: String,
     16         org_uuid: String,
     17         atype: i32,
     18         pub enabled: bool,
     19         pub data: String,
     20     }
     21 }
     22 
     23 // https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/PolicyType.cs
     24 #[derive(Copy, Clone, Eq, PartialEq)]
     25 pub enum OrgPolicyType {
     26     TwoFactorAuthentication = 0,
     27     MasterPassword = 1,
     28     PasswordGenerator = 2,
     29     SingleOrg = 3,
     30     PersonalOwnership = 5,
     31     DisableSend = 6,
     32     SendOptions = 7,
     33     ResetPassword = 8,
     34 }
     35 impl From<OrgPolicyType> for i32 {
     36     fn from(value: OrgPolicyType) -> Self {
     37         match value {
     38             OrgPolicyType::TwoFactorAuthentication => 0i32,
     39             OrgPolicyType::MasterPassword => 1i32,
     40             OrgPolicyType::PasswordGenerator => 2i32,
     41             OrgPolicyType::SingleOrg => 3i32,
     42             OrgPolicyType::PersonalOwnership => 5i32,
     43             OrgPolicyType::DisableSend => 6i32,
     44             OrgPolicyType::SendOptions => 7i32,
     45             OrgPolicyType::ResetPassword => 8i32,
     46         }
     47     }
     48 }
     49 impl TryFrom<i32> for OrgPolicyType {
     50     type Error = Error;
     51     fn try_from(value: i32) -> Result<Self, Self::Error> {
     52         match value {
     53             0i32 => Ok(Self::TwoFactorAuthentication),
     54             1i32 => Ok(Self::MasterPassword),
     55             2i32 => Ok(Self::PasswordGenerator),
     56             3i32 => Ok(Self::SingleOrg),
     57             5i32 => Ok(Self::PersonalOwnership),
     58             6i32 => Ok(Self::DisableSend),
     59             7i32 => Ok(Self::SendOptions),
     60             8i32 => Ok(Self::ResetPassword),
     61             _ => {
     62                 const MSG: &str = "i32 is invalid OrgPolicyType";
     63                 Err(Error::new(MSG, MSG))
     64             }
     65         }
     66     }
     67 }
     68 
     69 // https://github.com/bitwarden/server/blob/5cbdee137921a19b1f722920f0fa3cd45af2ef0f/src/Core/Models/Data/Organizations/Policies/ResetPasswordDataModel.cs
     70 #[derive(Deserialize)]
     71 #[serde(rename_all = "camelCase")]
     72 struct ResetPasswordDataModel {
     73     auto_enroll_enabled: bool,
     74 }
     75 type OrgPolicyResult = Result<(), OrgPolicyErr>;
     76 pub enum OrgPolicyErr {
     77     TwoFactorMissing,
     78     SingleOrgEnforced,
     79 }
     80 
     81 /// Local methods
     82 impl OrgPolicy {
     83     pub fn new(org_uuid: String, atype: OrgPolicyType, data: String) -> Self {
     84         Self {
     85             uuid: util::get_uuid(),
     86             org_uuid,
     87             atype: i32::from(atype),
     88             enabled: false,
     89             data,
     90         }
     91     }
     92 
     93     pub fn to_json(&self) -> Value {
     94         let data_json: Value = serde_json::from_str(&self.data).unwrap_or(Value::Null);
     95         json!({
     96             "id": self.uuid,
     97             "organizationId": self.org_uuid,
     98             "type": self.atype,
     99             "data": data_json,
    100             "enabled": self.enabled,
    101             "object": "policy",
    102         })
    103     }
    104 }
    105 
    106 /// Database methods
    107 impl OrgPolicy {
    108     pub async fn save(&self, conn: &DbConn) -> EmptyResult {
    109         db_run! { conn:
    110              {
    111                 match diesel::replace_into(org_policies::table)
    112                     .values(OrgPolicyDb::to_db(self))
    113                     .execute(conn)
    114                 {
    115                     Ok(_) => Ok(()),
    116                     // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first.
    117                     Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => {
    118                         diesel::update(org_policies::table)
    119                             .filter(org_policies::uuid.eq(&self.uuid))
    120                             .set(OrgPolicyDb::to_db(self))
    121                             .execute(conn)
    122                             .map_res("Error saving org_policy")
    123                     }
    124                     Err(e) => Err(e.into()),
    125                 }.map_res("Error saving org_policy")
    126             }
    127         }
    128     }
    129     pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> {
    130         db_run! { conn: {
    131             org_policies::table
    132                 .filter(org_policies::org_uuid.eq(org_uuid))
    133                 .load::<OrgPolicyDb>(conn)
    134                 .expect("Error loading org_policy")
    135                 .from_db()
    136         }}
    137     }
    138 
    139     pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> {
    140         db_run! { conn: {
    141             org_policies::table
    142                 .inner_join(
    143                     users_organizations::table.on(
    144                         users_organizations::org_uuid.eq(org_policies::org_uuid)
    145                             .and(users_organizations::user_uuid.eq(user_uuid)))
    146                 )
    147                 .filter(
    148                     users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed))
    149                 )
    150                 .select(org_policies::all_columns)
    151                 .load::<OrgPolicyDb>(conn)
    152                 .expect("Error loading org_policy")
    153                 .from_db()
    154         }}
    155     }
    156 
    157     pub async fn find_by_org_and_type(
    158         org_uuid: &str,
    159         policy_type: OrgPolicyType,
    160         conn: &DbConn,
    161     ) -> Option<Self> {
    162         db_run! { conn: {
    163             org_policies::table
    164                 .filter(org_policies::org_uuid.eq(org_uuid))
    165                 .filter(org_policies::atype.eq(i32::from(policy_type)))
    166                 .first::<OrgPolicyDb>(conn)
    167                 .ok()
    168                 .from_db()
    169         }}
    170     }
    171 
    172     pub async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult {
    173         db_run! { conn: {
    174             diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid)))
    175                 .execute(conn)
    176                 .map_res("Error deleting org_policy")
    177         }}
    178     }
    179 
    180     pub async fn find_accepted_and_confirmed_by_user_and_active_policy(
    181         user_uuid: &str,
    182         policy_type: OrgPolicyType,
    183         conn: &DbConn,
    184     ) -> Vec<Self> {
    185         db_run! { conn: {
    186             org_policies::table
    187                 .inner_join(
    188                     users_organizations::table.on(
    189                         users_organizations::org_uuid.eq(org_policies::org_uuid)
    190                             .and(users_organizations::user_uuid.eq(user_uuid)))
    191                 )
    192                 .filter(
    193                     users_organizations::status.eq(i32::from(UserOrgStatus::Accepted))
    194                 )
    195                 .or_filter(
    196                     users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed))
    197                 )
    198                 .filter(org_policies::atype.eq(i32::from(policy_type)))
    199                 .filter(org_policies::enabled.eq(true))
    200                 .select(org_policies::all_columns)
    201                 .load::<OrgPolicyDb>(conn)
    202                 .expect("Error loading org_policy")
    203                 .from_db()
    204         }}
    205     }
    206 
    207     /// Returns true if the user belongs to an org that has enabled the specified policy type,
    208     /// and the user is not an owner or admin of that org. This is only useful for checking
    209     /// applicability of policy types that have these particular semantics.
    210     pub async fn is_applicable_to_user(
    211         user_uuid: &str,
    212         policy_type: OrgPolicyType,
    213         exclude_org_uuid: Option<&str>,
    214         conn: &DbConn,
    215     ) -> bool {
    216         for policy in Self::find_accepted_and_confirmed_by_user_and_active_policy(
    217             user_uuid,
    218             policy_type,
    219             conn,
    220         )
    221         .await
    222         {
    223             // Check if we need to skip this organization.
    224             if exclude_org_uuid.is_some() && exclude_org_uuid.unwrap() == policy.org_uuid {
    225                 continue;
    226             }
    227             if let Some(user) =
    228                 UserOrganization::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await
    229             {
    230                 if user.atype < UserOrgType::Admin {
    231                     return true;
    232                 }
    233             }
    234         }
    235         false
    236     }
    237 
    238     pub async fn is_user_allowed(
    239         user_uuid: &str,
    240         org_uuid: &str,
    241         exclude_current_org: bool,
    242         conn: &DbConn,
    243     ) -> OrgPolicyResult {
    244         // Enforce TwoFactor/TwoStep login
    245         if !TwoFactorType::has_twofactor(user_uuid, conn)
    246             .await
    247             .expect("unable to get two factor information")
    248         {
    249             match Self::find_by_org_and_type(org_uuid, OrgPolicyType::TwoFactorAuthentication, conn)
    250                 .await
    251             {
    252                 Some(p) if p.enabled => {
    253                     return Err(OrgPolicyErr::TwoFactorMissing);
    254                 }
    255                 _ => {}
    256             };
    257         }
    258 
    259         // Enforce Single Organization Policy of other organizations user is a member of
    260         // This check here needs to exclude this current org-id, else an accepted user can not be confirmed.
    261         let exclude_org = if exclude_current_org {
    262             Some(org_uuid)
    263         } else {
    264             None
    265         };
    266         if Self::is_applicable_to_user(user_uuid, OrgPolicyType::SingleOrg, exclude_org, conn).await
    267         {
    268             return Err(OrgPolicyErr::SingleOrgEnforced);
    269         }
    270         Ok(())
    271     }
    272 
    273     pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &DbConn) -> bool {
    274         match Self::find_by_org_and_type(org_uuid, OrgPolicyType::ResetPassword, conn).await {
    275             Some(policy) => {
    276                 if let Ok(opts) = serde_json::from_str::<ResetPasswordDataModel>(&policy.data) {
    277                     return policy.enabled && opts.auto_enroll_enabled;
    278                 }
    279             }
    280             None => return false,
    281         }
    282         false
    283     }
    284 }