Files
server/src/Core/Services/Implementations/AzureAttachmentStorageService.cs

180 lines
7.9 KiB
C#
Raw Normal View History

using System.Threading.Tasks;
2020-01-10 08:33:13 -05:00
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using System.IO;
2017-07-10 14:30:12 -04:00
using System;
using Bit.Core.Models.Data;
2017-07-10 17:08:50 -04:00
using Bit.Core.Models.Table;
using Bit.Core.Settings;
using System.Collections.Generic;
namespace Bit.Core.Services
{
public class AzureAttachmentStorageService : IAttachmentStorageService
{
private const string _defaultContainerName = "attachments";
private readonly static string[] _attachmentContainerName = { "attachments", "attachments-v2" };
private static readonly TimeSpan downloadLinkLiveTime = TimeSpan.FromMinutes(1);
private readonly CloudBlobClient _blobClient;
private readonly Dictionary<string, CloudBlobContainer> _attachmentContainers = new Dictionary<string, CloudBlobContainer>();
public AzureAttachmentStorageService(
GlobalSettings globalSettings)
{
2017-06-30 23:01:41 -04:00
var storageAccount = CloudStorageAccount.Parse(globalSettings.Attachment.ConnectionString);
_blobClient = storageAccount.CreateCloudBlobClient();
}
public async Task<string> GetAttachmentDownloadUrlAsync(Cipher cipher, CipherAttachment.MetaData attachmentData)
{
await InitAsync(attachmentData.ContainerName);
var blob = _attachmentContainers[attachmentData.ContainerName].GetBlockBlobReference($"{cipher.Id}/{attachmentData.AttachmentId}");
var accessPolicy = new SharedAccessBlobPolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.Add(downloadLinkLiveTime),
Permissions = SharedAccessBlobPermissions.Read
};
return blob.Uri + blob.GetSharedAccessSignature(accessPolicy);
}
public async Task UploadNewAttachmentAsync(Stream stream, Cipher cipher, CipherAttachment.MetaData attachment)
2017-07-10 14:30:12 -04:00
{
attachment.ContainerName = _defaultContainerName;
await InitAsync(_defaultContainerName);
var blob = _attachmentContainers[_defaultContainerName].GetBlockBlobReference($"{cipher.Id}/{attachment.AttachmentId}");
2017-07-10 17:08:50 -04:00
blob.Metadata.Add("cipherId", cipher.Id.ToString());
if (cipher.UserId.HasValue)
2017-07-10 17:08:50 -04:00
{
blob.Metadata.Add("userId", cipher.UserId.Value.ToString());
}
else
{
blob.Metadata.Add("organizationId", cipher.OrganizationId.Value.ToString());
}
blob.Properties.ContentDisposition = $"attachment; filename=\"{attachment.AttachmentId}\"";
2017-07-10 17:08:50 -04:00
await blob.UploadFromStreamAsync(stream);
2017-07-10 14:30:12 -04:00
}
public async Task UploadShareAttachmentAsync(Stream stream, Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData)
2017-07-10 14:30:12 -04:00
{
attachmentData.ContainerName = _defaultContainerName;
await InitAsync(_defaultContainerName);
var blob = _attachmentContainers[_defaultContainerName].GetBlockBlobReference($"temp/{cipherId}/{organizationId}/{attachmentData.AttachmentId}");
2017-07-10 17:08:50 -04:00
blob.Metadata.Add("cipherId", cipherId.ToString());
blob.Metadata.Add("organizationId", organizationId.ToString());
blob.Properties.ContentDisposition = $"attachment; filename=\"{attachmentData.AttachmentId}\"";
2017-07-10 17:08:50 -04:00
await blob.UploadFromStreamAsync(stream);
2017-07-10 14:30:12 -04:00
}
public async Task StartShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData data)
{
await InitAsync(data.ContainerName);
var source = _attachmentContainers[data.ContainerName].GetBlockBlobReference($"temp/{cipherId}/{organizationId}/{data.AttachmentId}");
if (!(await source.ExistsAsync()))
2017-07-10 14:30:12 -04:00
{
return;
}
await InitAsync(_defaultContainerName);
var dest = _attachmentContainers[_defaultContainerName].GetBlockBlobReference($"{cipherId}/{data.AttachmentId}");
if (!(await dest.ExistsAsync()))
2017-07-10 14:30:12 -04:00
{
return;
}
var original = _attachmentContainers[_defaultContainerName].GetBlockBlobReference($"temp/{cipherId}/{data.AttachmentId}");
2017-07-10 14:30:12 -04:00
await original.DeleteIfExistsAsync();
await original.StartCopyAsync(dest);
await dest.DeleteIfExistsAsync();
await dest.StartCopyAsync(source);
}
public async Task RollbackShareAttachmentAsync(Guid cipherId, Guid organizationId, CipherAttachment.MetaData attachmentData, string originalContainer)
{
await InitAsync(attachmentData.ContainerName);
var source = _attachmentContainers[attachmentData.ContainerName].GetBlockBlobReference($"temp/{cipherId}/{organizationId}/{attachmentData.AttachmentId}");
2017-07-10 16:21:18 -04:00
await source.DeleteIfExistsAsync();
await InitAsync(originalContainer);
var original = _attachmentContainers[originalContainer].GetBlockBlobReference($"temp/{cipherId}/{attachmentData.AttachmentId}");
if (!(await original.ExistsAsync()))
2017-07-10 14:30:12 -04:00
{
return;
}
var dest = _attachmentContainers[originalContainer].GetBlockBlobReference($"{cipherId}/{attachmentData.AttachmentId}");
2017-07-10 14:30:12 -04:00
await dest.DeleteIfExistsAsync();
await dest.StartCopyAsync(original);
await original.DeleteIfExistsAsync();
}
public async Task DeleteAttachmentAsync(Guid cipherId, CipherAttachment.MetaData attachment)
2017-07-10 14:30:12 -04:00
{
await InitAsync(attachment.ContainerName);
var blobName = $"{cipherId}/{attachment.AttachmentId}";
var blob = _attachmentContainers[attachment.ContainerName].GetBlockBlobReference(blobName);
await blob.DeleteIfExistsAsync();
}
private async Task DeleteAttachmentsForPathAsync(string path)
2017-07-10 14:30:12 -04:00
{
foreach (var container in _attachmentContainerName)
2017-07-10 17:08:50 -04:00
{
await InitAsync(container);
var segment = await _attachmentContainers[container].ListBlobsSegmentedAsync(path, true, BlobListingDetails.None, 100, null, null, null);
while (true)
2017-07-10 20:48:06 -04:00
{
foreach (var blob in segment.Results)
{
if (blob is CloudBlockBlob blockBlob)
{
await blockBlob.DeleteIfExistsAsync();
}
}
2017-07-10 17:08:50 -04:00
if (segment.ContinuationToken == null)
{
break;
}
segment = await _attachmentContainers[container].ListBlobsSegmentedAsync(segment.ContinuationToken);
}
2017-07-10 17:08:50 -04:00
}
}
public async Task CleanupAsync(Guid cipherId) => await DeleteAttachmentsForPathAsync($"temp/{cipherId}");
public async Task DeleteAttachmentsForCipherAsync(Guid cipherId) => await DeleteAttachmentsForPathAsync(cipherId.ToString());
2017-07-10 17:08:50 -04:00
public async Task DeleteAttachmentsForOrganizationAsync(Guid organizationId)
{
await InitAsync(_defaultContainerName);
2017-07-10 17:08:50 -04:00
}
public async Task DeleteAttachmentsForUserAsync(Guid userId)
{
await InitAsync(_defaultContainerName);
2017-07-10 14:30:12 -04:00
}
private async Task InitAsync(string containerName)
{
if (!_attachmentContainers.ContainsKey(containerName) || _attachmentContainers[containerName] == null)
{
_attachmentContainers[containerName] = _blobClient.GetContainerReference(containerName);
if (containerName == "attachments")
{
await _attachmentContainers[containerName].CreateIfNotExistsAsync(BlobContainerPublicAccessType.Blob, null, null);
}
else
{
await _attachmentContainers[containerName].CreateIfNotExistsAsync(BlobContainerPublicAccessType.Off, null, null);
}
}
}
}
}