1
0
mirror of https://github.com/bitwarden/server synced 2026-01-14 06:23:46 +00:00

[PM-28747] Storage limit bypass for enforce organization ownership policy (#6759)

* [PM-28747] Bypass storage limit when enforce organization data ownership policy is enabled

* [PM-28747] Unit tests for storage limit enforcement

* [PM-28747] Add feature flag check

* [PM-28747] Simplify ignore storage limits policy enforcement

* [PM-28747] Add additional test cases
This commit is contained in:
Shane Melton
2025-12-29 09:30:22 -08:00
committed by GitHub
parent 2dc4e9a420
commit 3b5bb76800
3 changed files with 177 additions and 7 deletions

View File

@@ -72,6 +72,17 @@ public class OrganizationDataOwnershipPolicyRequirement : IPolicyRequirement
{
return _policyDetails.Any(p => p.OrganizationId == organizationId);
}
/// <summary>
/// Ignore storage limits if the organization has data ownership policy enabled.
/// Allows users to seamlessly migrate their data into the organization without being blocked by storage limits.
/// Organization admins will need to manage storage after migration should overages occur.
/// </summary>
public bool IgnoreStorageLimitsOnMigration(Guid organizationId)
{
return _policyDetails.Any(p => p.OrganizationId == organizationId &&
p.OrganizationUserStatus == OrganizationUserStatusType.Confirmed);
}
}
public record DefaultCollectionRequest(Guid OrganizationUserId, bool ShouldCreateDefaultCollection)

View File

@@ -2,6 +2,7 @@
#nullable disable
using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
@@ -999,20 +1000,43 @@ public class CipherService : ICipherService
throw new BadRequestException("Could not find organization.");
}
if (hasAttachments && !org.MaxStorageGb.HasValue)
if (!await IgnoreStorageLimitsOnMigrationAsync(sharingUserId, org))
{
throw new BadRequestException("This organization cannot use attachments.");
}
if (hasAttachments && !org.MaxStorageGb.HasValue)
{
throw new BadRequestException("This organization cannot use attachments.");
}
var storageAdjustment = attachments?.Sum(a => a.Value.Size) ?? 0;
if (org.StorageBytesRemaining() < storageAdjustment)
{
throw new BadRequestException("Not enough storage available for this organization.");
var storageAdjustment = attachments?.Sum(a => a.Value.Size) ?? 0;
if (org.StorageBytesRemaining() < storageAdjustment)
{
throw new BadRequestException("Not enough storage available for this organization.");
}
}
ValidateCipherLastKnownRevisionDate(cipher, lastKnownRevisionDate);
}
/// <summary>
/// Checks if the storage limit for the org should be ignored due to the Organization Data Ownership Policy
/// </summary>
private async Task<bool> IgnoreStorageLimitsOnMigrationAsync(Guid userId, Organization organization)
{
if (!_featureService.IsEnabled(FeatureFlagKeys.MigrateMyVaultToMyItems))
{
return false;
}
if (!organization.UsePolicies)
{
return false;
}
var requirement = await _policyRequirementQuery.GetAsync<OrganizationDataOwnershipPolicyRequirement>(userId);
return requirement.IgnoreStorageLimitsOnMigration(organization.Id);
}
private async Task ValidateViewPasswordUserAsync(Cipher cipher)
{
if (cipher.Data == null || !cipher.OrganizationId.HasValue)