mirror of
https://github.com/bitwarden/server.git
synced 2025-05-20 19:14:32 -05: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)
|
IEnumerable<Guid> collectionIds, Guid sharingUserId, DateTime? lastKnownRevisionDate)
|
||||||
{
|
{
|
||||||
var attachments = cipher.GetAttachments();
|
var attachments = cipher.GetAttachments();
|
||||||
var hasAttachments = attachments?.Any() ?? false;
|
|
||||||
var hasOldAttachments = attachments?.Any(a => a.Key == null) ?? false;
|
var hasOldAttachments = attachments?.Any(a => a.Key == null) ?? false;
|
||||||
var updatedCipher = false;
|
var updatedCipher = false;
|
||||||
var migratedAttachments = false;
|
var migratedAttachments = false;
|
||||||
@ -503,34 +502,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (cipher.Id == default(Guid))
|
await ValidateCipherCanBeShared(cipher, sharingUserId, organizationId, lastKnownRevisionDate);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Sproc will not save this UserId on the cipher. It is used limit scope of the collectionIds.
|
// Sproc will not save this UserId on the cipher. It is used limit scope of the collectionIds.
|
||||||
cipher.UserId = sharingUserId;
|
cipher.UserId = sharingUserId;
|
||||||
@ -597,22 +569,7 @@ namespace Bit.Core.Services
|
|||||||
var cipherIds = new List<Guid>();
|
var cipherIds = new List<Guid>();
|
||||||
foreach (var (cipher, lastKnownRevisionDate) in cipherInfos)
|
foreach (var (cipher, lastKnownRevisionDate) in cipherInfos)
|
||||||
{
|
{
|
||||||
if (cipher.Id == default(Guid))
|
await ValidateCipherCanBeShared(cipher, sharingUserId, organizationId, lastKnownRevisionDate);
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
cipher.UserId = null;
|
cipher.UserId = null;
|
||||||
cipher.OrganizationId = organizationId;
|
cipher.OrganizationId = organizationId;
|
||||||
@ -999,5 +956,49 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
return storageBytesRemaining;
|
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,
|
public async Task ShareManyAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
|
||||||
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
|
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 cipherInfos = ciphers.Select(c => (c, (DateTime?)c.RevisionDate.AddDays(-1)));
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
@ -108,6 +115,13 @@ namespace Bit.Core.Test.Services
|
|||||||
public async Task ShareManyAsync_CorrectRevisionDate_Passes(string revisionDateString,
|
public async Task ShareManyAsync_CorrectRevisionDate_Passes(string revisionDateString,
|
||||||
SutProvider<CipherService> sutProvider, IEnumerable<Cipher> ciphers, Organization organization, List<Guid> collectionIds)
|
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,
|
var cipherInfos = ciphers.Select(c => (c,
|
||||||
string.IsNullOrEmpty(revisionDateString) ? null : (DateTime?)c.RevisionDate));
|
string.IsNullOrEmpty(revisionDateString) ? null : (DateTime?)c.RevisionDate));
|
||||||
var sharingUserId = ciphers.First().UserId.Value;
|
var sharingUserId = ciphers.First().UserId.Value;
|
||||||
@ -157,5 +171,51 @@ namespace Bit.Core.Test.Services
|
|||||||
Assert.Equal(revisionDate, cipher.RevisionDate);
|
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…
x
Reference in New Issue
Block a user