Browse Source

fix: Change model to use i16 newtype for `OrganizationCollectionSettings`

pull/7452/head
Raphaël Roumezin 21 hours ago
parent
commit
b6be6472f9
  1. 29
      src/api/core/organizations.rs
  2. 3
      src/db/models/cipher.rs
  3. 4
      src/db/models/mod.rs
  4. 112
      src/db/models/organization.rs

29
src/api/core/organizations.rs

@ -6,9 +6,9 @@ use serde_json::Value;
use crate::{ use crate::{
CONFIG, CONFIG,
api::admin::FAKE_ADMIN_UUID,
api::{ api::{
EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType, EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType,
admin::FAKE_ADMIN_UUID,
core::{CipherSyncData, CipherSyncType, accept_org_invite, log_event, two_factor}, core::{CipherSyncData, CipherSyncType, accept_org_invite, log_event, two_factor},
}, },
auth::{AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OrgMemberHeaders, OwnerHeaders, decode_invite}, auth::{AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OrgMemberHeaders, OwnerHeaders, decode_invite},
@ -17,7 +17,8 @@ use crate::{
models::{ models::{
Cipher, CipherId, Collection, CollectionCipher, CollectionGroup, CollectionId, CollectionUser, EventType, Cipher, CipherId, Collection, CollectionCipher, CollectionGroup, CollectionId, CollectionUser, EventType,
Group, GroupId, GroupUser, Invitation, Membership, MembershipId, MembershipStatus, MembershipType, Group, GroupId, GroupUser, Invitation, Membership, MembershipId, MembershipStatus, MembershipType,
OrgPolicy, OrgPolicyType, Organization, OrganizationApiKey, OrganizationId, User, UserId, OrgCollectionSetting, OrgPolicy, OrgPolicyType, Organization, OrganizationApiKey, OrganizationId, User,
UserId,
}, },
}, },
mail, mail,
@ -367,11 +368,16 @@ async fn put_organization_collection_management(
err!("Organization not found") err!("Organization not found")
}; };
org.collection_settings.allow_admin_access_to_all_collection_items = org.collection_settings.set_flag(
data.allow_admin_access_to_all_collection_items.unwrap_or(false); OrgCollectionSetting::AllowAdminAccessToAllCollectionItems,
org.collection_settings.limit_collection_creation = data.limit_collection_creation.unwrap_or(false); data.allow_admin_access_to_all_collection_items.unwrap_or(false),
org.collection_settings.limit_collection_deletion = data.limit_collection_deletion.unwrap_or(false); );
org.collection_settings.limit_item_deletion = data.limit_item_deletion.unwrap_or(false); org.collection_settings
.set_flag(OrgCollectionSetting::LimitCollectionCreation, data.limit_collection_creation.unwrap_or(false));
org.collection_settings
.set_flag(OrgCollectionSetting::LimitCollectionDeletion, data.limit_collection_deletion.unwrap_or(false));
org.collection_settings
.set_flag(OrgCollectionSetting::LimitItemDeletion, data.limit_item_deletion.unwrap_or(false));
org.save(&conn).await?; org.save(&conn).await?;
@ -560,7 +566,7 @@ async fn post_organization_collections(
}; };
if headers.membership.atype == MembershipType::Manager if headers.membership.atype == MembershipType::Manager
&& (!headers.membership.access_all || org_settings.limit_collection_creation) && (!headers.membership.access_all || org_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation))
{ {
err!("You don't have permission to create collections") err!("You don't have permission to create collections")
} }
@ -775,7 +781,9 @@ async fn delete_organization_collection_impl(
err!("Collection not found", "Collection does not exist or does not belong to this organization") err!("Collection not found", "Collection does not exist or does not belong to this organization")
}; };
if headers.membership_type <= MembershipType::Manager && org_settings.limit_collection_deletion { if headers.membership_type <= MembershipType::Manager
&& org_settings.get_flag(OrgCollectionSetting::LimitCollectionDeletion)
{
err!("The current user isn't allowed to delete this collection") err!("The current user isn't allowed to delete this collection")
} }
@ -1898,7 +1906,8 @@ async fn post_org_import(
// We do not allow users or managers which can not manage all collections to create new collections // We do not allow users or managers which can not manage all collections to create new collections
// If there is any collection other than an existing import collection, abort the import. // If there is any collection other than an existing import collection, abort the import.
if headers.membership.atype <= MembershipType::Manager if headers.membership.atype <= MembershipType::Manager
&& (!headers.membership.has_full_access() || org_settings.limit_collection_creation) && (!headers.membership.has_full_access()
|| org_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation))
{ {
err!(Compact, "The current user isn't allowed to create new collections") err!(Compact, "The current user isn't allowed to create new collections")
} }

3
src/db/models/cipher.rs

@ -13,6 +13,7 @@ use crate::{
}, },
db::{ db::{
DbConn, DbConn,
models::OrgCollectionSetting,
schema::{ schema::{
ciphers, ciphers_collections, collections, collections_groups, folders, folders_ciphers, groups, ciphers, ciphers_collections, collections, collections_groups, folders, folders_ciphers, groups,
groups_users, users_collections, users_organizations, groups_users, users_collections, users_organizations,
@ -728,7 +729,7 @@ impl Cipher {
pub async fn is_deletable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool { pub async fn is_deletable_by_user(&self, user_uuid: &UserId, conn: &DbConn) -> bool {
let manage_required = match self.organization_uuid { let manage_required = match self.organization_uuid {
Some(ref org_id) => match Organization::find_collection_settings_by_uuid(org_id, conn).await { Some(ref org_id) => match Organization::find_collection_settings_by_uuid(org_id, conn).await {
Some(settings) => settings.limit_item_deletion, Some(settings) => settings.get_flag(OrgCollectionSetting::LimitItemDeletion),
None => false, None => false,
}, },
None => false, None => false,

4
src/db/models/mod.rs

@ -31,8 +31,8 @@ pub use self::folder::{Folder, FolderCipher, FolderId};
pub use self::group::{CollectionGroup, Group, GroupId, GroupUser}; pub use self::group::{CollectionGroup, Group, GroupId, GroupUser};
pub use self::org_policy::{OrgPolicy, OrgPolicyId, OrgPolicyType}; pub use self::org_policy::{OrgPolicy, OrgPolicyId, OrgPolicyType};
pub use self::organization::{ pub use self::organization::{
Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, Organization, OrganizationApiKey, Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, OrgCollectionSetting, Organization,
OrganizationId, OrganizationApiKey, OrganizationId,
}; };
pub use self::send::{Send, SendFileId, SendId, SendType}; pub use self::send::{Send, SendFileId, SendId, SendType};
pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeResponseError, SsoAuth}; pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeResponseError, SsoAuth};

112
src/db/models/organization.rs

@ -5,15 +5,9 @@ use std::{
use chrono::{NaiveDateTime, Utc}; use chrono::{NaiveDateTime, Utc};
use derive_more::{AsRef, Deref, Display, From}; use derive_more::{AsRef, Deref, Display, From};
use diesel::{ use diesel::{deserialize::FromSql, prelude::*, serialize::ToSql, sql_types::SmallInt};
deserialize::FromSql,
prelude::*,
serialize::{IsNull, ToSql},
sql_types::SmallInt,
};
use num_traits::FromPrimitive; use num_traits::FromPrimitive;
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
use num_traits::ToPrimitive;
use serde_json::Value; use serde_json::Value;
use crate::{ use crate::{
@ -35,59 +29,39 @@ use super::{
OrgPolicyType, TwoFactor, User, UserId, OrgPolicyType, TwoFactor, User, UserId,
}; };
#[repr(i16)]
pub enum OrgCollectionSetting {
AllowAdminAccessToAllCollectionItems = 0b1,
LimitCollectionCreation = 0b10,
LimitCollectionDeletion = 0b100,
LimitItemDeletion = 0b1000,
}
#[derive(Debug, Clone, Copy, FromSqlRow, AsExpression)] #[derive(Debug, Clone, Copy, FromSqlRow, AsExpression)]
#[diesel(sql_type = SmallInt)] #[diesel(sql_type = SmallInt)]
#[allow(clippy::struct_excessive_bools)] pub struct OrganizationCollectionSettings(i16);
pub struct OrganizationCollectionSettings {
pub allow_admin_access_to_all_collection_items: bool,
pub limit_collection_creation: bool,
pub limit_collection_deletion: bool,
pub limit_item_deletion: bool,
}
impl From<&OrganizationCollectionSettings> for i16 { impl OrganizationCollectionSettings {
fn from(value: &OrganizationCollectionSettings) -> Self { pub fn get_flag(self, flag: OrgCollectionSetting) -> bool {
i16::from(value.allow_admin_access_to_all_collection_items) (self.0 | flag as i16) > 0
| (0b10 * i16::from(value.limit_collection_creation))
| (0b100 * i16::from(value.limit_collection_deletion))
| (0b1000 * i16::from(value.limit_item_deletion))
} }
}
#[cfg(feature = "sqlite")] pub fn set_flag(&mut self, flag: OrgCollectionSetting, val: bool) {
use diesel::sqlite::Sqlite; if val {
#[cfg(feature = "sqlite")] self.0 |= flag as i16;
impl ToSql<SmallInt, Sqlite> for OrganizationCollectionSettings } else {
where self.0 &= i16::MAX - flag as i16;
i16: ToSql<SmallInt, Sqlite>, }
{
fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, Sqlite>) -> diesel::serialize::Result {
out.set_value(i16::from(self).to_i32());
Ok(IsNull::No)
}
}
#[cfg(feature = "postgresql")]
use diesel::pg::Pg;
#[cfg(feature = "postgresql")]
impl ToSql<SmallInt, Pg> for OrganizationCollectionSettings
where
i16: ToSql<SmallInt, Pg>,
{
fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, Pg>) -> diesel::serialize::Result {
<i16 as ToSql<SmallInt, Pg>>::to_sql(&i16::from(self), &mut out.reborrow())
} }
} }
#[cfg(feature = "mysql")] impl<DB> ToSql<SmallInt, DB> for OrganizationCollectionSettings
use diesel::mysql::Mysql;
#[cfg(feature = "mysql")]
impl ToSql<SmallInt, Mysql> for OrganizationCollectionSettings
where where
i16: ToSql<SmallInt, Mysql>, DB: diesel::backend::Backend,
i16: ToSql<SmallInt, DB>,
{ {
fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, Mysql>) -> diesel::serialize::Result { fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, DB>) -> diesel::serialize::Result {
<i16 as ToSql<SmallInt, Mysql>>::to_sql(&i16::from(self), &mut out.reborrow()) self.0.to_sql(out)
} }
} }
@ -98,12 +72,7 @@ where
{ {
fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> { fn from_sql(bytes: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
let val = i16::from_sql(bytes)?; let val = i16::from_sql(bytes)?;
Ok(Self { Ok(Self(val))
allow_admin_access_to_all_collection_items: (val & 0b1) != 0,
limit_collection_creation: (val & 0b10) != 0,
limit_collection_deletion: (val & 0b100) != 0,
limit_item_deletion: (val & 0b1000) != 0,
})
} }
} }
@ -274,12 +243,11 @@ impl Organization {
billing_email, billing_email,
private_key, private_key,
public_key, public_key,
collection_settings: OrganizationCollectionSettings {
allow_admin_access_to_all_collection_items: true, // Collection Management Settings default to AllowAdminAccessToAllCollectionItems
limit_collection_creation: false, collection_settings: OrganizationCollectionSettings(
limit_collection_deletion: false, OrgCollectionSetting::AllowAdminAccessToAllCollectionItems as i16,
limit_item_deletion: false, ),
},
} }
} }
// https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs // https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs
@ -306,10 +274,10 @@ impl Organization {
"useApi": true, "useApi": true,
"hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), "hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(),
"useResetPassword": CONFIG.mail_enabled(), "useResetPassword": CONFIG.mail_enabled(),
"allowAdminAccessToAllCollectionItems": self.collection_settings.allow_admin_access_to_all_collection_items, "allowAdminAccessToAllCollectionItems": self.collection_settings.get_flag(OrgCollectionSetting::AllowAdminAccessToAllCollectionItems),
"limitCollectionCreation": self.collection_settings.limit_collection_creation, "limitCollectionCreation": self.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation),
"limitCollectionDeletion": self.collection_settings.limit_collection_deletion, "limitCollectionDeletion": self.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionDeletion),
"limitItemDeletion": self.collection_settings.limit_item_deletion, "limitItemDeletion": self.collection_settings.get_flag(OrgCollectionSetting::LimitItemDeletion),
"businessName": self.name, "businessName": self.name,
"businessAddress1": null, "businessAddress1": null,
@ -557,9 +525,9 @@ impl Membership {
"accessImportExport": false, "accessImportExport": false,
"accessReports": false, "accessReports": false,
// If the following 3 Collection roles are set to true a custom user has access all permission // If the following 3 Collection roles are set to true a custom user has access all permission
"createNewCollections": membership_type == 4 && self.access_all && !org.collection_settings.limit_collection_creation, "createNewCollections": membership_type == 4 && self.access_all && !org.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation),
"editAnyCollection": membership_type == 4 && self.access_all, "editAnyCollection": membership_type == 4 && self.access_all,
"deleteAnyCollection": membership_type == 4 && self.access_all && !org.collection_settings.limit_collection_deletion, "deleteAnyCollection": membership_type == 4 && self.access_all && !org.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation),
"manageGroups": false, "manageGroups": false,
"managePolicies": false, "managePolicies": false,
"manageSso": false, // Not supported "manageSso": false, // Not supported
@ -612,10 +580,10 @@ impl Membership {
"familySponsorshipToDelete": null, "familySponsorshipToDelete": null,
"accessSecretsManager": false, "accessSecretsManager": false,
"allowAdminAccessToAllCollectionItems": org.collection_settings.allow_admin_access_to_all_collection_items, "allowAdminAccessToAllCollectionItems": org.collection_settings.get_flag(OrgCollectionSetting::AllowAdminAccessToAllCollectionItems),
"limitCollectionCreation": org.collection_settings.limit_collection_creation, "limitCollectionCreation": org.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionCreation),
"limitCollectionDeletion": org.collection_settings.limit_collection_deletion, "limitCollectionDeletion": org.collection_settings.get_flag(OrgCollectionSetting::LimitCollectionDeletion),
"limitItemDeletion": org.collection_settings.limit_item_deletion, "limitItemDeletion": org.collection_settings.get_flag(OrgCollectionSetting::LimitItemDeletion),
"userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO "userIsManagedByOrganization": false, // Means not managed via the Members UI, like SSO
"userIsClaimedByOrganization": false, // The new key instead of the obsolete userIsManagedByOrganization "userIsClaimedByOrganization": false, // The new key instead of the obsolete userIsManagedByOrganization

Loading…
Cancel
Save