1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 08:32:50 -05:00
Files
bitwarden/src/Core/Services/Implementations/AzureSendFileStorageService.cs

142 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Sas;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Settings;
namespace Bit.Core.Services
{
public class AzureSendFileStorageService : ISendFileStorageService
{
public const string FilesContainerName = "sendfiles";
private static readonly TimeSpan _downloadLinkLiveTime = TimeSpan.FromMinutes(1);
private readonly BlobServiceClient _blobServiceClient;
private BlobContainerClient _sendFilesContainerClient;
public FileUploadType FileUploadType => FileUploadType.Azure;
public static string SendIdFromBlobName(string blobName) => blobName.Split('/')[0];
public static string BlobName(Send send, string fileId) => $"{send.Id}/{fileId}";
public AzureSendFileStorageService(
GlobalSettings globalSettings)
{
_blobServiceClient = new BlobServiceClient(globalSettings.Send.ConnectionString);
}
public async Task UploadNewFileAsync(Stream stream, Send send, string fileId)
{
await InitAsync();
var blobClient = _sendFilesContainerClient.GetBlobClient(BlobName(send, fileId));
var metadata = new Dictionary<string, string>();
if (send.UserId.HasValue)
{
metadata.Add("userId", send.UserId.Value.ToString());
}
else
{
metadata.Add("organizationId", send.OrganizationId.Value.ToString());
}
var headers = new BlobHttpHeaders
{
ContentDisposition = $"attachment; filename=\"{fileId}\""
};
await blobClient.UploadAsync(stream, new BlobUploadOptions { Metadata = metadata, HttpHeaders = headers });
}
public async Task DeleteFileAsync(Send send, string fileId) => await DeleteBlobAsync(BlobName(send, fileId));
public async Task DeleteBlobAsync(string blobName)
{
await InitAsync();
var blobClient = _sendFilesContainerClient.GetBlobClient(blobName);
await blobClient.DeleteIfExistsAsync();
}
public async Task DeleteFilesForOrganizationAsync(Guid organizationId)
{
await InitAsync();
}
public async Task DeleteFilesForUserAsync(Guid userId)
{
await InitAsync();
}
public async Task<string> GetSendFileDownloadUrlAsync(Send send, string fileId)
{
await InitAsync();
var blobClient = _sendFilesContainerClient.GetBlobClient(BlobName(send, fileId));
var sasUri = blobClient.GenerateSasUri(BlobSasPermissions.Read, DateTime.UtcNow.Add(_downloadLinkLiveTime));
return sasUri.ToString();
}
public async Task<string> GetSendFileUploadUrlAsync(Send send, string fileId)
{
await InitAsync();
var blobClient = _sendFilesContainerClient.GetBlobClient(BlobName(send, fileId));
var sasUri = blobClient.GenerateSasUri(BlobSasPermissions.Create | BlobSasPermissions.Write, DateTime.UtcNow.Add(_downloadLinkLiveTime));
return sasUri.ToString();
}
public async Task<(bool, long?)> ValidateFileAsync(Send send, string fileId, long expectedFileSize, long leeway)
{
await InitAsync();
var blobClient = _sendFilesContainerClient.GetBlobClient(BlobName(send, fileId));
try
{
var blobProperties = await blobClient.GetPropertiesAsync();
var metadata = blobProperties.Value.Metadata;
if (send.UserId.HasValue)
{
metadata["userId"] = send.UserId.Value.ToString();
}
else
{
metadata["organizationId"] = send.OrganizationId.Value.ToString();
}
await blobClient.SetMetadataAsync(metadata);
var headers = new BlobHttpHeaders
{
ContentDisposition = $"attachment; filename=\"{fileId}\""
};
await blobClient.SetHttpHeadersAsync(headers);
var length = blobProperties.Value.ContentLength;
if (length < expectedFileSize - leeway || length > expectedFileSize + leeway)
{
return (false, length);
}
return (true, length);
}
catch (Exception ex)
{
return (false, null);
}
}
private async Task InitAsync()
{
if (_sendFilesContainerClient == null)
{
_sendFilesContainerClient = _blobServiceClient.GetBlobContainerClient(FilesContainerName);
await _sendFilesContainerClient.CreateIfNotExistsAsync(PublicAccessType.None, null, null);
}
}
}
}