From a1d5e10b5162b8955070301d291032921ad7388c Mon Sep 17 00:00:00 2001 From: Roar Mjelde Date: Mon, 28 Oct 2024 18:50:36 +0100 Subject: [PATCH] Add file size restriction for legacy API --- .../Controllers/LegacyFileController.cs | 3 +- .../UploadFile/UploadFileHandler.cs | 19 ++++-- .../Helpers/FakeFileStream.cs | 59 +++++++++++++++++++ .../LegacyFileControllerTests.cs | 23 ++++++++ 4 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 tests/Altinn.Broker.Tests/Helpers/FakeFileStream.cs diff --git a/src/Altinn.Broker.API/Controllers/LegacyFileController.cs b/src/Altinn.Broker.API/Controllers/LegacyFileController.cs index 96127362..a5ca205c 100644 --- a/src/Altinn.Broker.API/Controllers/LegacyFileController.cs +++ b/src/Altinn.Broker.API/Controllers/LegacyFileController.cs @@ -77,7 +77,8 @@ CancellationToken cancellationToken FileTransferId = fileTransferId, Token = legacyToken, UploadStream = Request.Body, - IsLegacy = true + IsLegacy = true, + ContentLength = Request.ContentLength ?? Request.Body.Length }, cancellationToken); return commandResult.Match( fileId => Ok(fileId.ToString()), diff --git a/src/Altinn.Broker.Application/UploadFile/UploadFileHandler.cs b/src/Altinn.Broker.Application/UploadFile/UploadFileHandler.cs index 03a185b9..d7e5d816 100644 --- a/src/Altinn.Broker.Application/UploadFile/UploadFileHandler.cs +++ b/src/Altinn.Broker.Application/UploadFile/UploadFileHandler.cs @@ -1,5 +1,6 @@ using Altinn.Broker.Application.Settings; using Altinn.Broker.Core.Application; +using Altinn.Broker.Core.Domain; using Altinn.Broker.Core.Domain.Enums; using Altinn.Broker.Core.Helpers; using Altinn.Broker.Core.Repositories; @@ -50,14 +51,11 @@ public async Task> Process(UploadFileRequest request, Cancell { return Errors.ServiceOwnerNotConfigured; }; - var maxUploadSize = resource?.MaxFileTransferSize ?? _maxFileUploadSize; - if (request.ContentLength > maxUploadSize) + if (request.ContentLength > GetMaxUploadSize(resource, request.IsLegacy)) { return Errors.FileSizeTooBig; } - await fileTransferStatusRepository.InsertFileTransferStatus(request.FileTransferId, FileTransferStatus.UploadStarted, cancellationToken: cancellationToken); - try { var checksum = await brokerStorageService.UploadFile(serviceOwner, fileTransfer, request.UploadStream, cancellationToken); @@ -102,4 +100,17 @@ public async Task> Process(UploadFileRequest request, Cancell return fileTransfer.FileTransferId; }, logger, cancellationToken); } + + private long GetMaxUploadSize(ResourceEntity resource, bool isLegacy) + { + if (isLegacy) + { + return 1024 * 1000 * 1000; // 1 GB + } + if (resource.MaxFileTransferSize is not null) + { + return resource.MaxFileTransferSize.Value; + } + return _maxFileUploadSize; + } } diff --git a/tests/Altinn.Broker.Tests/Helpers/FakeFileStream.cs b/tests/Altinn.Broker.Tests/Helpers/FakeFileStream.cs new file mode 100644 index 00000000..66a707cc --- /dev/null +++ b/tests/Altinn.Broker.Tests/Helpers/FakeFileStream.cs @@ -0,0 +1,59 @@ +namespace Altinn.Broker.Tests.Helpers; +public class FakeFileStream : Stream +{ + private readonly long _length; + private long _position; + + public FakeFileStream(long length) + { + _length = length; + _position = 0; + } + + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => _length; + public override long Position + { + get => _position; + set => _position = value; + } + + public override int Read(byte[] buffer, int offset, int count) + { + var remainingBytes = _length - _position; + if (remainingBytes <= 0) return 0; + + var bytesToRead = (int)Math.Min(count, remainingBytes); + // Fill buffer with dummy data + for (int i = offset; i < offset + bytesToRead; i++) + { + buffer[i] = (byte)(i % 256); + } + + _position += bytesToRead; + return bytesToRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + Position = offset; + break; + case SeekOrigin.Current: + Position += offset; + break; + case SeekOrigin.End: + Position = Length + offset; + break; + } + return Position; + } + + public override void Flush() { } + public override void SetLength(long value) => throw new NotImplementedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException(); +} diff --git a/tests/Altinn.Broker.Tests/LegacyFileControllerTests.cs b/tests/Altinn.Broker.Tests/LegacyFileControllerTests.cs index 46d71479..37148bbd 100644 --- a/tests/Altinn.Broker.Tests/LegacyFileControllerTests.cs +++ b/tests/Altinn.Broker.Tests/LegacyFileControllerTests.cs @@ -443,6 +443,29 @@ public async Task Download_ConfirmDownloaded_Success() Assert.Equal(LegacyRecipientFileStatusExt.DownloadConfirmed, result?.Recipients[0]?.CurrentRecipientFileStatusCode); Assert.Equal(LegacyFileStatusExt.AllConfirmedDownloaded, result?.FileStatus); } + + [Fact] + public async Task UploadTooBigFile_ToLegacyApi_FailsWithValidationError() + { + // Initialize + var initializeFileResponse = await _legacyClient.PostAsJsonAsync("broker/api/legacy/v1/file", FileTransferInitializeExtTestFactory.BasicFileTransfer()); + string onBehalfOfConsumer = FileTransferInitializeExtTestFactory.BasicFileTransfer().Sender; + Assert.True(initializeFileResponse.IsSuccessStatusCode, await initializeFileResponse.Content.ReadAsStringAsync()); + var fileId = await initializeFileResponse.Content.ReadAsStringAsync(); + var fileAfterInitialize = await _legacyClient.GetFromJsonAsync($"broker/api/legacy/v1/file/{fileId}?onBehalfOfConsumer={onBehalfOfConsumer}", _responseSerializerOptions); + Assert.NotNull(fileAfterInitialize); + Assert.Equal(LegacyFileStatusExt.Initialized, fileAfterInitialize.FileStatus); + + // Upload + var fileSize = 1024 * 1000 * 1000 + 1; + var content = new FakeFileStream(fileSize); + var streamContent = new StreamContent(content); + streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + streamContent.Headers.ContentLength = fileSize; + var uploadResponse = await _legacyClient.PostAsync($"broker/api/legacy/v1/file/{fileId}/upload?onBehalfOfConsumer={onBehalfOfConsumer}", streamContent); + Assert.Equal(HttpStatusCode.BadRequest, uploadResponse.StatusCode); + } + private string GetMalwareScanResultJson(string filePath, string fileId) { string jsonBody = File.ReadAllText(filePath);