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 }