1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 00:22:50 -05:00

share login with attachments

This commit is contained in:
Kyle Spearrin
2017-07-10 14:30:12 -04:00
parent fbc189544b
commit f8c749bab5
9 changed files with 264 additions and 48 deletions

View File

@ -2,6 +2,7 @@
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.IO;
using System;
namespace Bit.Core.Services
{
@ -19,20 +20,80 @@ namespace Bit.Core.Services
_blobClient = storageAccount.CreateCloudBlobClient();
}
public async Task UploadAttachmentAsync(Stream stream, string name)
public async Task UploadNewAttachmentAsync(Stream stream, Guid cipherId, string attachmentId)
{
await InitAsync();
var blob = _attachmentsContainer.GetBlockBlobReference(name);
await blob.UploadFromStreamAsync(stream);
await UploadAttachmentAsync(stream, $"{cipherId}/{attachmentId}");
}
public async Task DeleteAttachmentAsync(string name)
public async Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, string attachmentId)
{
await UploadAttachmentAsync(stream, $"{cipherId}/share/{organizationId}/{attachmentId}");
}
public async Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
var blob = _attachmentsContainer.GetBlockBlobReference(name);
var source = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/share/{organizationId}/{attachmentId}");
if(!await source.ExistsAsync())
{
return;
}
var dest = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/{attachmentId}");
if(!await dest.ExistsAsync())
{
return;
}
var original = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/temp/{attachmentId}");
await original.DeleteIfExistsAsync();
await original.StartCopyAsync(dest);
await dest.DeleteIfExistsAsync();
await dest.StartCopyAsync(source);
}
public async Task CommitShareAttachmentAsync(Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
var source = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/share/{organizationId}/{attachmentId}");
var original = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/temp/{attachmentId}");
await original.DeleteIfExistsAsync();
await source.DeleteIfExistsAsync();
}
public async Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, string attachmentId)
{
await InitAsync();
var source = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/share/{organizationId}/{attachmentId}");
var dest = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/{attachmentId}");
var original = _attachmentsContainer.GetBlockBlobReference($"{cipherId}/temp/{attachmentId}");
if(!await original.ExistsAsync())
{
return;
}
await dest.DeleteIfExistsAsync();
await dest.StartCopyAsync(original);
await original.DeleteIfExistsAsync();
await source.DeleteIfExistsAsync();
}
public async Task DeleteAttachmentAsync(Guid cipherId, string attachmentId)
{
await InitAsync();
var blobName = $"{cipherId}/{attachmentId}";
var blob = _attachmentsContainer.GetBlockBlobReference(blobName);
await blob.DeleteIfExistsAsync();
}
private async Task UploadAttachmentAsync(Stream stream, string blobName)
{
await InitAsync();
var blob = _attachmentsContainer.GetBlockBlobReference(blobName);
await blob.UploadFromStreamAsync(stream);
}
private async Task InitAsync()
{
if(_attachmentsContainer == null)

View File

@ -139,8 +139,7 @@ namespace Bit.Core.Services
}
var attachmentId = Utilities.CoreHelpers.SecureRandomString(32, upper: false, special: false);
var storageId = $"{cipher.Id}/{attachmentId}";
await _attachmentStorageService.UploadAttachmentAsync(stream, storageId);
await _attachmentStorageService.UploadNewAttachmentAsync(stream, cipher.Id, attachmentId);
try
{
@ -165,7 +164,7 @@ namespace Bit.Core.Services
catch
{
// Clean up since this is not transactional
await _attachmentStorageService.DeleteAttachmentAsync(storageId);
await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, attachmentId);
throw;
}
@ -173,6 +172,29 @@ namespace Bit.Core.Services
await _pushService.PushSyncCipherUpdateAsync(cipher);
}
public async Task CreateAttachmentShareAsync(Cipher cipher, Stream stream, string fileName, long requestLength,
string attachmentId, Guid organizationId)
{
if(requestLength < 1)
{
throw new BadRequestException("No data to attach.");
}
var org = await _organizationRepository.GetByIdAsync(organizationId);
if(!org.MaxStorageGb.HasValue)
{
throw new BadRequestException("This organization cannot use attachments.");
}
var storageBytesRemaining = org.StorageBytesRemaining();
if(storageBytesRemaining < requestLength)
{
throw new BadRequestException("Not enough storage available for this organization.");
}
await _attachmentStorageService.UploadShareAttachmentAsync(stream, cipher.Id, organizationId, attachmentId);
}
public async Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false)
{
if(!orgAdmin && !(await UserCanEditAsync(cipher, deletingUserId)))
@ -207,9 +229,7 @@ namespace Bit.Core.Services
await _cipherRepository.DeleteAttachmentAsync(cipher.Id, attachmentId);
cipher.DeleteAttachment(attachmentId);
var storedFilename = $"{cipher.Id}/{attachmentId}";
await _attachmentStorageService.DeleteAttachmentAsync(storedFilename);
await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, attachmentId);
// push
await _pushService.PushSyncCipherUpdateAsync(cipher);
@ -258,7 +278,8 @@ namespace Bit.Core.Services
await _pushService.PushSyncFolderDeleteAsync(folder);
}
public async Task ShareAsync(Cipher cipher, Guid organizationId, IEnumerable<Guid> collectionIds, Guid sharingUserId)
public async Task ShareAsync(Cipher originalCipher, Cipher cipher, Guid organizationId,
IEnumerable<Guid> collectionIds, Guid sharingUserId)
{
if(cipher.Id == default(Guid))
{
@ -275,11 +296,48 @@ namespace Bit.Core.Services
throw new NotFoundException();
}
// Sproc will not save this UserId on the cipher. It is used limit scope of the collectionIds.
cipher.UserId = sharingUserId;
cipher.OrganizationId = organizationId;
cipher.RevisionDate = DateTime.UtcNow;
await _cipherRepository.ReplaceAsync(cipher, collectionIds);
var attachments = cipher.GetAttachments();
var hasAttachments = (attachments?.Count ?? 0) > 0;
try
{
// Sproc will not save this UserId on the cipher. It is used limit scope of the collectionIds.
cipher.UserId = sharingUserId;
cipher.OrganizationId = organizationId;
cipher.RevisionDate = DateTime.UtcNow;
await _cipherRepository.ReplaceAsync(cipher, collectionIds);
if(hasAttachments)
{
// migrate attachments
foreach(var attachment in attachments)
{
await _attachmentStorageService.StartShareAttachmentAsync(cipher.Id, organizationId, attachment.Key);
}
}
}
catch
{
// roll everything back
await _cipherRepository.ReplaceAsync(originalCipher);
if(!hasAttachments)
{
throw;
}
foreach(var attachment in attachments)
{
await _attachmentStorageService.RollbackShareAttachmentAsync(cipher.Id, organizationId, attachment.Key);
}
throw;
}
// commit attachment migration
foreach(var attachment in attachments)
{
await _attachmentStorageService.CommitShareAttachmentAsync(cipher.Id, organizationId, attachment.Key);
}
// push
await _pushService.PushSyncCipherUpdateAsync(cipher);