1
0
mirror of https://github.com/bitwarden/server synced 2025-12-16 16:23:31 +00:00

PM-18939 refactoring send service to 'cqrs' (#5652)

* PM-18939 refactoring send service to 'cqrs'

* PM-18939 fixing import issue with sendValidationService

* PM-18939 fixing code based on PR comments

* PM-18339 reverting to previous code in test

* PM-18939 adding XMLdocs to services

* PM-18939 reverting send validation methods

* PM-18939 updating code to match main

* PM-18939 reverting validateUserCanSaveAsync to match main

* PM-18939 fill our param and return sections of XMLdocs

* PM-18939 updating XMLdocs based on PR comments

* Update src/Core/Tools/SendFeatures/Commands/Interfaces/IAnonymousSendCommand.cs

Co-authored-by:  Audrey  <ajensen@bitwarden.com>

* Update src/Core/Tools/SendFeatures/Commands/Interfaces/INonAnonymousSendCommand.cs

Co-authored-by:  Audrey  <ajensen@bitwarden.com>

* Update src/Core/Tools/SendFeatures/Commands/Interfaces/INonAnonymousSendCommand.cs

Co-authored-by:  Audrey  <ajensen@bitwarden.com>

* Update src/Core/Tools/SendFeatures/Services/Interfaces/ISendStorageService.cs

Co-authored-by:  Audrey  <ajensen@bitwarden.com>

* PM-18939 adding commits to change tuple to enum type

* PM-18939 resetting stream position to 0 when uploading file

* PM-18939 updating XMLdocs based on PR comments

* PM-18939 updating XMLdocs

* PM-18939 removing circular dependency

* PM-18939 fixing based on comments

* PM-18939 updating method name and documentation

---------

Co-authored-by:  Audrey  <ajensen@bitwarden.com>
This commit is contained in:
Graham Walker
2025-05-19 22:59:30 -05:00
committed by GitHub
parent 7b3e2a80f4
commit 818934487f
32 changed files with 2295 additions and 1368 deletions

View File

@@ -0,0 +1,180 @@
using System.Text.Json;
using Bit.Core.Context;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Tools.Entities;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.Repositories;
using Bit.Core.Tools.SendFeatures.Commands.Interfaces;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
namespace Bit.Core.Tools.SendFeatures.Commands;
public class NonAnonymousSendCommand : INonAnonymousSendCommand
{
private readonly ISendRepository _sendRepository;
private readonly ISendFileStorageService _sendFileStorageService;
private readonly IPushNotificationService _pushNotificationService;
private readonly ISendValidationService _sendValidationService;
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly ISendCoreHelperService _sendCoreHelperService;
public NonAnonymousSendCommand(ISendRepository sendRepository,
ISendFileStorageService sendFileStorageService,
IPushNotificationService pushNotificationService,
ISendAuthorizationService sendAuthorizationService,
ISendValidationService sendValidationService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
ISendCoreHelperService sendCoreHelperService)
{
_sendRepository = sendRepository;
_sendFileStorageService = sendFileStorageService;
_pushNotificationService = pushNotificationService;
_sendValidationService = sendValidationService;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_sendCoreHelperService = sendCoreHelperService;
}
public async Task SaveSendAsync(Send send)
{
// Make sure user can save Sends
await _sendValidationService.ValidateUserCanSaveAsync(send.UserId, send);
if (send.Id == default(Guid))
{
await _sendRepository.CreateAsync(send);
await _pushNotificationService.PushSyncSendCreateAsync(send);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
{
Id = send.UserId ?? default,
Type = ReferenceEventType.SendCreated,
Source = ReferenceEventSource.User,
SendType = send.Type,
MaxAccessCount = send.MaxAccessCount,
HasPassword = !string.IsNullOrWhiteSpace(send.Password),
SendHasNotes = send.Data?.Contains("Notes"),
ClientId = _currentContext.ClientId,
ClientVersion = _currentContext.ClientVersion
});
}
else
{
send.RevisionDate = DateTime.UtcNow;
await _sendRepository.UpsertAsync(send);
await _pushNotificationService.PushSyncSendUpdateAsync(send);
}
}
public async Task<string> SaveFileSendAsync(Send send, SendFileData data, long fileLength)
{
if (send.Type != SendType.File)
{
throw new BadRequestException("Send is not of type \"file\".");
}
if (fileLength < 1)
{
throw new BadRequestException("No file data.");
}
var storageBytesRemaining = await _sendValidationService.StorageRemainingForSendAsync(send);
if (storageBytesRemaining < fileLength)
{
throw new BadRequestException("Not enough storage available.");
}
var fileId = _sendCoreHelperService.SecureRandomString(32, useUpperCase: false, useSpecial: false);
try
{
data.Id = fileId;
data.Size = fileLength;
data.Validated = false;
send.Data = JsonSerializer.Serialize(data,
JsonHelpers.IgnoreWritingNull);
await SaveSendAsync(send);
return await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId);
}
catch
{
// Clean up since this is not transactional
await _sendFileStorageService.DeleteFileAsync(send, fileId);
throw;
}
}
public async Task UploadFileToExistingSendAsync(Stream stream, Send send)
{
if (stream.Position > 0)
{
stream.Position = 0;
}
if (send?.Data == null)
{
throw new BadRequestException("Send does not have file data");
}
if (send.Type != SendType.File)
{
throw new BadRequestException("Not a File Type Send.");
}
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
if (data.Validated)
{
throw new BadRequestException("File has already been uploaded.");
}
await _sendFileStorageService.UploadNewFileAsync(stream, send, data.Id);
if (!await ConfirmFileSize(send))
{
throw new BadRequestException("File received does not match expected file length.");
}
}
public async Task DeleteSendAsync(Send send)
{
await _sendRepository.DeleteAsync(send);
if (send.Type == Enums.SendType.File)
{
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
await _sendFileStorageService.DeleteFileAsync(send, data.Id);
}
await _pushNotificationService.PushSyncSendDeleteAsync(send);
}
public async Task<bool> ConfirmFileSize(Send send)
{
var fileData = JsonSerializer.Deserialize<SendFileData>(send.Data);
var (valid, realSize) = await _sendFileStorageService.ValidateFileAsync(send, fileData.Id, fileData.Size, SendFileSettingHelper.FILE_SIZE_LEEWAY);
if (!valid || realSize > SendFileSettingHelper.FILE_SIZE_LEEWAY)
{
// File reported differs in size from that promised. Must be a rogue client. Delete Send
await DeleteSendAsync(send);
return false;
}
// Update Send data if necessary
if (realSize != fileData.Size)
{
fileData.Size = realSize.Value;
}
fileData.Validated = true;
send.Data = JsonSerializer.Serialize(fileData,
JsonHelpers.IgnoreWritingNull);
await SaveSendAsync(send);
return valid;
}
}