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. 110
      src/db/models/organization.rs

29
src/api/core/organizations.rs

@ -6,9 +6,9 @@ use serde_json::Value;
use crate::{
CONFIG,
api::admin::FAKE_ADMIN_UUID,
api::{
EmptyResult, JsonResult, Notify, PasswordOrOtpData, UpdateType,
admin::FAKE_ADMIN_UUID,
core::{CipherSyncData, CipherSyncType, accept_org_invite, log_event, two_factor},
},
auth::{AdminHeaders, Headers, ManagerHeaders, ManagerHeadersLoose, OrgMemberHeaders, OwnerHeaders, decode_invite},
@ -17,7 +17,8 @@ use crate::{
models::{
Cipher, CipherId, Collection, CollectionCipher, CollectionGroup, CollectionId, CollectionUser, EventType,
Group, GroupId, GroupUser, Invitation, Membership, MembershipId, MembershipStatus, MembershipType,
OrgPolicy, OrgPolicyType, Organization, OrganizationApiKey, OrganizationId, User, UserId,
OrgCollectionSetting, OrgPolicy, OrgPolicyType, Organization, OrganizationApiKey, OrganizationId, User,
UserId,
},
},
mail,
@ -367,11 +368,16 @@ async fn put_organization_collection_management(
err!("Organization not found")
};
org.collection_settings.allow_admin_access_to_all_collection_items =
data.allow_admin_access_to_all_collection_items.unwrap_or(false);
org.collection_settings.limit_collection_creation = data.limit_collection_creation.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::AllowAdminAccessToAllCollectionItems,
data.allow_admin_access_to_all_collection_items.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?;
@ -560,7 +566,7 @@ async fn post_organization_collections(
};
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")
}
@ -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")
};
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")
}
@ -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
// If there is any collection other than an existing import collection, abort the import.
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")
}

3
src/db/models/cipher.rs

@ -13,6 +13,7 @@ use crate::{
},
db::{
DbConn,
models::OrgCollectionSetting,
schema::{
ciphers, ciphers_collections, collections, collections_groups, folders, folders_ciphers, groups,
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 {
let manage_required = match self.organization_uuid {
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,

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::org_policy::{OrgPolicy, OrgPolicyId, OrgPolicyType};
pub use self::organization::{
Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, Organization, OrganizationApiKey,
OrganizationId,
Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, OrgCollectionSetting, Organization,
OrganizationApiKey, OrganizationId,
};
pub use self::send::{Send, SendFileId, SendId, SendType};
pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeResponseError, SsoAuth};

110
src/db/models/organization.rs

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

Loading…
Cancel
Save