1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-23 04:21:05 -05:00

Use sas token for send downloads (#1157)

* Remove Url from SendFileModel

Url is now generated on the fly with limited lifetime.

New model houses the download url generated

* Create API endpoint for getting Send file download url

* Generate limited-life Azure download urls

* Lint fix
This commit is contained in:
Matt Gibson 2021-02-24 13:03:16 -06:00 committed by GitHub
parent f8940e4be5
commit e350daeeee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 3 deletions

View File

@ -11,6 +11,7 @@ using Bit.Api.Utilities;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Models.Api.Response;
namespace Bit.Api.Controllers namespace Bit.Api.Controllers
{ {
@ -21,17 +22,20 @@ namespace Bit.Api.Controllers
private readonly ISendRepository _sendRepository; private readonly ISendRepository _sendRepository;
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly ISendService _sendService; private readonly ISendService _sendService;
private readonly ISendFileStorageService _sendFileStorageService;
private readonly GlobalSettings _globalSettings; private readonly GlobalSettings _globalSettings;
public SendsController( public SendsController(
ISendRepository sendRepository, ISendRepository sendRepository,
IUserService userService, IUserService userService,
ISendService sendService, ISendService sendService,
ISendFileStorageService sendFileStorageService,
GlobalSettings globalSettings) GlobalSettings globalSettings)
{ {
_sendRepository = sendRepository; _sendRepository = sendRepository;
_userService = userService; _userService = userService;
_sendService = sendService; _sendService = sendService;
_sendFileStorageService = sendFileStorageService;
_globalSettings = globalSettings; _globalSettings = globalSettings;
} }
@ -59,6 +63,17 @@ namespace Bit.Api.Controllers
return new ObjectResult(new SendAccessResponseModel(send, _globalSettings)); return new ObjectResult(new SendAccessResponseModel(send, _globalSettings));
} }
[AllowAnonymous]
[HttpGet("access/file/{id}")]
public async Task<SendFileDownloadDataResponseModel> GetSendFileDownloadData(string id)
{
return new SendFileDownloadDataResponseModel()
{
Id = id,
Url = await _sendFileStorageService.GetSendFileDownloadUrlAsync(id),
};
}
[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<SendResponseModel> Get(string id) public async Task<SendResponseModel> Get(string id)
{ {

View File

@ -0,0 +1,10 @@
namespace Bit.Core.Models.Api.Response
{
public class SendFileDownloadDataResponseModel : ResponseModel
{
public string Id { get; set; }
public string Url { get; set; }
public SendFileDownloadDataResponseModel() : base("send-fileDownload") { }
}
}

View File

@ -11,14 +11,12 @@ namespace Bit.Core.Models.Api
public SendFileModel(SendFileData data, GlobalSettings globalSettings) public SendFileModel(SendFileData data, GlobalSettings globalSettings)
{ {
Id = data.Id; Id = data.Id;
Url = $"{globalSettings.Send.BaseUrl}/{data.Id}";
FileName = data.FileName; FileName = data.FileName;
Size = data.SizeString; Size = data.SizeString;
SizeName = CoreHelpers.ReadableBytesSize(data.Size); SizeName = CoreHelpers.ReadableBytesSize(data.Size);
} }
public string Id { get; set; } public string Id { get; set; }
public string Url { get; set; }
[EncryptedString] [EncryptedString]
[EncryptedStringLength(1000)] [EncryptedStringLength(1000)]
public string FileName { get; set; } public string FileName { get; set; }

View File

@ -11,5 +11,6 @@ namespace Bit.Core.Services
Task DeleteFileAsync(string fileId); Task DeleteFileAsync(string fileId);
Task DeleteFilesForOrganizationAsync(Guid organizationId); Task DeleteFilesForOrganizationAsync(Guid organizationId);
Task DeleteFilesForUserAsync(Guid userId); Task DeleteFilesForUserAsync(Guid userId);
Task<string> GetSendFileDownloadUrlAsync(string fileId);
} }
} }

View File

@ -12,6 +12,7 @@ namespace Bit.Core.Services
{ {
private const string FilesContainerName = "sendfiles"; private const string FilesContainerName = "sendfiles";
private static readonly TimeSpan _downloadLinkLiveTime = TimeSpan.FromMinutes(1);
private readonly CloudBlobClient _blobClient; private readonly CloudBlobClient _blobClient;
private CloudBlobContainer _sendFilesContainer; private CloudBlobContainer _sendFilesContainer;
@ -55,12 +56,25 @@ namespace Bit.Core.Services
await InitAsync(); await InitAsync();
} }
public async Task<string> GetSendFileDownloadUrlAsync(string fileId)
{
await InitAsync();
var blob = _sendFilesContainer.GetBlockBlobReference(fileId);
var accessPolicy = new SharedAccessBlobPolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.Add(_downloadLinkLiveTime),
Permissions = SharedAccessBlobPermissions.Read
};
return blob.Uri + blob.GetSharedAccessSignature(accessPolicy);
}
private async Task InitAsync() private async Task InitAsync()
{ {
if (_sendFilesContainer == null) if (_sendFilesContainer == null)
{ {
_sendFilesContainer = _blobClient.GetContainerReference(FilesContainerName); _sendFilesContainer = _blobClient.GetContainerReference(FilesContainerName);
await _sendFilesContainer.CreateIfNotExistsAsync(BlobContainerPublicAccessType.Blob, null, null); await _sendFilesContainer.CreateIfNotExistsAsync(BlobContainerPublicAccessType.Off, null, null);
} }
} }
} }

View File

@ -9,11 +9,13 @@ namespace Bit.Core.Services
public class LocalSendStorageService : ISendFileStorageService public class LocalSendStorageService : ISendFileStorageService
{ {
private readonly string _baseDirPath; private readonly string _baseDirPath;
private readonly string _baseSendUrl;
public LocalSendStorageService( public LocalSendStorageService(
GlobalSettings globalSettings) GlobalSettings globalSettings)
{ {
_baseDirPath = globalSettings.Send.BaseDirectory; _baseDirPath = globalSettings.Send.BaseDirectory;
_baseSendUrl = globalSettings.Send.BaseUrl;
} }
public async Task UploadNewFileAsync(Stream stream, Send send, string fileId) public async Task UploadNewFileAsync(Stream stream, Send send, string fileId)
@ -42,6 +44,12 @@ namespace Bit.Core.Services
await InitAsync(); await InitAsync();
} }
public async Task<string> GetSendFileDownloadUrlAsync(string fileId)
{
await InitAsync();
return $"{_baseSendUrl}/{fileId}";
}
private void DeleteFileIfExists(string path) private void DeleteFileIfExists(string path)
{ {
if (File.Exists(path)) if (File.Exists(path))

View File

@ -26,5 +26,10 @@ namespace Bit.Core.Services
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task<string> GetSendFileDownloadUrlAsync(string fileId)
{
return Task.FromResult((string)null);
}
} }
} }