mirror of
https://github.com/bitwarden/server.git
synced 2025-04-16 20:26:49 +02:00
Add attachments check before moving ciphers to a free org (#1890)
This commit is contained in:
parent
19d5817f8f
commit
17b22ca5a9
@ -495,7 +495,6 @@ namespace Bit.Core.Services
|
||||
IEnumerable<Guid> collectionIds, Guid sharingUserId, DateTime? lastKnownRevisionDate)
|
||||
{
|
||||
var attachments = cipher.GetAttachments();
|
||||
var hasAttachments = attachments?.Any() ?? false;
|
||||
var hasOldAttachments = attachments?.Any(a => a.Key == null) ?? false;
|
||||
var updatedCipher = false;
|
||||
var migratedAttachments = false;
|
||||
@ -503,34 +502,7 @@ namespace Bit.Core.Services
|
||||
|
||||
try
|
||||
{
|
||||
if (cipher.Id == default(Guid))
|
||||
{
|
||||
throw new BadRequestException(nameof(cipher.Id));
|
||||
}
|
||||
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("Already belongs to an organization.");
|
||||
}
|
||||
|
||||
if (!cipher.UserId.HasValue || cipher.UserId.Value != sharingUserId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
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.");
|
||||
}
|
||||
|
||||
ValidateCipherLastKnownRevisionDateAsync(cipher, lastKnownRevisionDate);
|
||||
await ValidateCipherCanBeShared(cipher, sharingUserId, organizationId, lastKnownRevisionDate);
|
||||
|
||||
// Sproc will not save this UserId on the cipher. It is used limit scope of the collectionIds.
|
||||
cipher.UserId = sharingUserId;
|
||||
@ -597,22 +569,7 @@ namespace Bit.Core.Services
|
||||
var cipherIds = new List<Guid>();
|
||||
foreach (var (cipher, lastKnownRevisionDate) in cipherInfos)
|
||||
{
|
||||
if (cipher.Id == default(Guid))
|
||||
{
|
||||
throw new BadRequestException("All ciphers must already exist.");
|
||||
}
|
||||
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("One or more ciphers already belong to an organization.");
|
||||
}
|
||||
|
||||
if (!cipher.UserId.HasValue || cipher.UserId.Value != sharingUserId)
|
||||
{
|
||||
throw new BadRequestException("One or more ciphers do not belong to you.");
|
||||
}
|
||||
|
||||
ValidateCipherLastKnownRevisionDateAsync(cipher, lastKnownRevisionDate);
|
||||
await ValidateCipherCanBeShared(cipher, sharingUserId, organizationId, lastKnownRevisionDate);
|
||||
|
||||
cipher.UserId = null;
|
||||
cipher.OrganizationId = organizationId;
|
||||
@ -999,5 +956,49 @@ namespace Bit.Core.Services
|
||||
|
||||
return storageBytesRemaining;
|
||||
}
|
||||
|
||||
private async Task ValidateCipherCanBeShared(
|
||||
Cipher cipher,
|
||||
Guid sharingUserId,
|
||||
Guid organizationId,
|
||||
DateTime? lastKnownRevisionDate)
|
||||
{
|
||||
if (cipher.Id == default(Guid))
|
||||
{
|
||||
throw new BadRequestException("Cipher must already exist.");
|
||||
}
|
||||
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("One or more ciphers already belong to an organization.");
|
||||
}
|
||||
|
||||
if (!cipher.UserId.HasValue || cipher.UserId.Value != sharingUserId)
|
||||
{
|
||||
throw new BadRequestException("One or more ciphers do not belong to you.");
|
||||
}
|
||||
|
||||
var attachments = cipher.GetAttachments();
|
||||
var hasAttachments = attachments?.Any() ?? false;
|
||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
|
||||
if (org == null)
|
||||
{
|
||||
throw new BadRequestException("Could not find organization.");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
ValidateCipherLastKnownRevisionDateAsync(cipher, lastKnownRevisionDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,13 @@ namespace Bit.Core.Test.Services
|
||||
public async Task ShareManyAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
|
||||
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
|
||||
.Returns(new Organization
|
||||
{
|
||||
PlanType = Enums.PlanType.EnterpriseAnnually,
|
||||
MaxStorageGb = 100
|
||||
});
|
||||
|
||||
var cipherInfos = ciphers.Select(c => (c, (DateTime?)c.RevisionDate.AddDays(-1)));
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
@ -108,6 +115,13 @@ namespace Bit.Core.Test.Services
|
||||
public async Task ShareManyAsync_CorrectRevisionDate_Passes(string revisionDateString,
|
||||
SutProvider<CipherService> sutProvider, IEnumerable<Cipher> ciphers, Organization organization, List<Guid> collectionIds)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id)
|
||||
.Returns(new Organization
|
||||
{
|
||||
PlanType = Enums.PlanType.EnterpriseAnnually,
|
||||
MaxStorageGb = 100
|
||||
});
|
||||
|
||||
var cipherInfos = ciphers.Select(c => (c,
|
||||
string.IsNullOrEmpty(revisionDateString) ? null : (DateTime?)c.RevisionDate));
|
||||
var sharingUserId = ciphers.First().UserId.Value;
|
||||
@ -157,5 +171,51 @@ namespace Bit.Core.Test.Services
|
||||
Assert.Equal(revisionDate, cipher.RevisionDate);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineUserCipherAutoData]
|
||||
public async Task ShareManyAsync_FreeOrgWithAttachment_Throws(SutProvider<CipherService> sutProvider,
|
||||
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns(new Organization
|
||||
{
|
||||
PlanType = Enums.PlanType.Free
|
||||
});
|
||||
ciphers.FirstOrDefault().Attachments =
|
||||
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
|
||||
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
|
||||
|
||||
var cipherInfos = ciphers.Select(c => (c,
|
||||
(DateTime?)c.RevisionDate));
|
||||
var sharingUserId = ciphers.First().UserId.Value;
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId));
|
||||
Assert.Contains("This organization cannot use attachments", exception.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineUserCipherAutoData]
|
||||
public async Task ShareManyAsync_PaidOrgWithAttachment_Passes(SutProvider<CipherService> sutProvider,
|
||||
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
|
||||
.Returns(new Organization
|
||||
{
|
||||
PlanType = Enums.PlanType.EnterpriseAnnually,
|
||||
MaxStorageGb = 100
|
||||
});
|
||||
ciphers.FirstOrDefault().Attachments =
|
||||
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
|
||||
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
|
||||
|
||||
var cipherInfos = ciphers.Select(c => (c,
|
||||
(DateTime?)c.RevisionDate));
|
||||
var sharingUserId = ciphers.First().UserId.Value;
|
||||
|
||||
await sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId);
|
||||
await sutProvider.GetDependency<ICipherRepository>().Received(1).UpdateCiphersAsync(sharingUserId,
|
||||
Arg.Is<IEnumerable<Cipher>>(arg => arg.Except(ciphers).IsNullOrEmpty()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user