diff --git a/.azure/modules/containerAppEnvironment/main.bicep b/.azure/modules/containerAppEnvironment/main.bicep index 462b597b..c8735919 100644 --- a/.azure/modules/containerAppEnvironment/main.bicep +++ b/.azure/modules/containerAppEnvironment/main.bicep @@ -57,7 +57,7 @@ resource application_insights_action 'Microsoft.Insights/actionGroups@2023-01-01 ] } } -resource exceptionOccuredAlertRule 'Microsoft.Insights/scheduledQueryRules@2023-12-01' = +resource exceptionOccuredAlertRule 'Microsoft.Insights/scheduledQueryRules@2023-03-15-preview' = if (emailReceiver != null && emailReceiver != '') { name: '${namePrefix}-500-exception-occured' location: location diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index edb14e0d..27265d56 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -110,7 +110,7 @@ jobs: hasMigrationChanges: ${{ needs.check-for-changes.outputs.hasMigrationChanges }} release-to-git: - name: release to git + name: Release to git runs-on: ubuntu-latest needs: [check-for-changes, deploy-production] if: ${{ needs.check-for-changes.outputs.hasBackendChanges == 'true' && !failure() && !cancelled()}} diff --git a/Test/Altinn.Broker.Tests/Helpers/CustomWebApplicationFactory.cs b/Test/Altinn.Broker.Tests/Helpers/CustomWebApplicationFactory.cs index 536d0cf8..5528076f 100644 --- a/Test/Altinn.Broker.Tests/Helpers/CustomWebApplicationFactory.cs +++ b/Test/Altinn.Broker.Tests/Helpers/CustomWebApplicationFactory.cs @@ -40,7 +40,7 @@ protected override void ConfigureWebHost( ((IList)o.Schemes).Clear(); }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(async options => + .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.SaveToken = true; diff --git a/Test/Altinn.Broker.Tests/LegacyFileControllerTests.cs b/Test/Altinn.Broker.Tests/LegacyFileControllerTests.cs index 5a991f1b..a02ddbb8 100644 --- a/Test/Altinn.Broker.Tests/LegacyFileControllerTests.cs +++ b/Test/Altinn.Broker.Tests/LegacyFileControllerTests.cs @@ -10,7 +10,6 @@ using Altinn.Broker.Models; using Altinn.Broker.Tests.Factories; using Altinn.Broker.Tests.Helpers; - using Xunit; namespace Altinn.Broker.Tests; @@ -90,10 +89,11 @@ public async Task GetFiles_GetByMultipleRecipient_Success() var textResponse = await getResponse.Content.ReadAsStringAsync(); - var result = await getResponse.Content.ReadAsAsync>(); + var result = await getResponse.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + Assert.NotNull(result); Assert.Contains(Guid.Parse(fileId), result); } @@ -154,10 +154,11 @@ public async Task GetFiles_GetByMultipleRecipient_Recipient1_Success() + $"&resourceId={file.ResourceId}" + $"&recipients={file.Recipients[0]}"); - var result = await getResponse.Content.ReadAsAsync>(); + var result = await getResponse.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(System.Net.HttpStatusCode.OK, getResponse.StatusCode); + Assert.NotNull(result); Assert.Contains(Guid.Parse(fileId), result); } @@ -188,10 +189,11 @@ public async Task GetFiles_GetByMultipleRecipient_Recipient2_Success() + $"&resourceId={file.ResourceId}" + $"&recipients={file.Recipients[1]}"); - var result = await getResponse.Content.ReadAsAsync>(); + var result = await getResponse.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + Assert.NotNull(result); Assert.Contains(Guid.Parse(fileId), result); } @@ -221,11 +223,11 @@ public async Task GetFiles_GetBySingleRecipient_Success() + $"&from={HttpUtility.UrlEncode(from.UtcDateTime.ToString("o"))}&to={HttpUtility.UrlEncode(to.UtcDateTime.ToString("o"))}" + $"&resourceId={file.ResourceId}" + $"&onBehalfOfConsumer={file.Recipients[0]}"); - var result = await getResponse.Content.ReadAsAsync>(); + var result = await getResponse.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); - Assert.Contains(Guid.Parse(fileId), result); + Assert.Contains(Guid.Parse(fileId), result!); } [Fact] @@ -262,13 +264,13 @@ public async Task GetFiles_GetInitializedAndDownloadStarted_Success() + $"&resourceId={file.ResourceId}" + $"&onBehalfOfConsumer={file.Recipients[1]}"); - var result_recip1 = await getResponse_rep1.Content.ReadAsAsync>(); - var result_recip2 = await getResponse_rep2.Content.ReadAsAsync>(); + var result_recip1 = await getResponse_rep1.Content.ReadFromJsonAsync>(_responseSerializerOptions); + var result_recip2 = await getResponse_rep2.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(HttpStatusCode.OK, getResponse_rep1.StatusCode); - Assert.Contains(Guid.Parse(fileId), result_recip1); - Assert.Contains(Guid.Parse(fileId), result_recip2); + Assert.Contains(Guid.Parse(fileId), result_recip1!); + Assert.Contains(Guid.Parse(fileId), result_recip2!); } [Fact] @@ -291,10 +293,11 @@ public async Task GetFileOverview_SentByA3Sender_Success() // Act var getResponse = await _legacyClient.GetAsync($"broker/api/legacy/v1/file/{fileId}?onBehalfOfConsumer={file.Recipients[0]}"); - var fileData = await getResponse.Content.ReadAsAsync(); + var fileData = await getResponse.Content.ReadFromJsonAsync(_responseSerializerOptions); // Assert Assert.Equal(System.Net.HttpStatusCode.OK, getResponse.StatusCode); + Assert.NotNull(fileData); Assert.Equal(fileId, fileData.FileId.ToString()); } @@ -339,10 +342,11 @@ public async Task GetFileOverview_2FilesInitiated_1Published_StandardRequestRetr + $"&resourceId={file.ResourceId}" + $"&onBehalfOfConsumer={file.Recipients[0]}"); string s = await getResponse.Content.ReadAsStringAsync(); - List fileData = await getResponse.Content.ReadAsAsync>(); + var fileData = await getResponse.Content.ReadFromJsonAsync>(_responseSerializerOptions); // Assert Assert.Equal(System.Net.HttpStatusCode.OK, getResponse.StatusCode); + Assert.NotNull(fileData); Assert.Contains(fileData, g => g == Guid.Parse(fileId1)); Assert.DoesNotContain(fileData, g => g == Guid.Parse(fileId2)); } @@ -413,7 +417,7 @@ public async Task Download_ConfirmDownloaded_Success() // Act var getResponse = await _legacyClient.PostAsync($"broker/api/legacy/v1/file/{fileId}/confirmdownload?onBehalfOfConsumer={file.Recipients[0]}", null); var statusResponse = await _legacyClient.GetAsync($"broker/api/legacy/v1/file/{fileId}?onBehalfOfConsumer={file.Recipients[0]}"); - var result = await statusResponse.Content.ReadAsAsync(); + var result = await statusResponse.Content.ReadFromJsonAsync(_responseSerializerOptions); Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode); diff --git a/Test/Altinn.Broker.Tests/ServiceOwnerControllerTests.cs b/Test/Altinn.Broker.Tests/ServiceOwnerControllerTests.cs index c748c810..4eaf5d04 100644 --- a/Test/Altinn.Broker.Tests/ServiceOwnerControllerTests.cs +++ b/Test/Altinn.Broker.Tests/ServiceOwnerControllerTests.cs @@ -28,6 +28,6 @@ public ServiceOwnerControllerTests(CustomWebApplicationFactory factory) public async Task Get_ServiceOwner() { var response = await _serviceOwnerClient.GetFromJsonAsync($"broker/api/v1/serviceowner", _responseSerializerOptions); - Assert.Equal("Digitaliseringsdirektoratet Avd Oslo", response.Name); + Assert.Equal("Digitaliseringsdirektoratet Avd Oslo", response!.Name); } } diff --git a/src/Altinn.Broker.API/Altinn.Broker.API.csproj b/src/Altinn.Broker.API/Altinn.Broker.API.csproj index 122d5e4c..a7e0ad0e 100644 --- a/src/Altinn.Broker.API/Altinn.Broker.API.csproj +++ b/src/Altinn.Broker.API/Altinn.Broker.API.csproj @@ -12,8 +12,7 @@ - - + diff --git a/src/Altinn.Broker.API/Controllers/FileTransferController.cs b/src/Altinn.Broker.API/Controllers/FileTransferController.cs index c55ed0eb..b88af3bf 100644 --- a/src/Altinn.Broker.API/Controllers/FileTransferController.cs +++ b/src/Altinn.Broker.API/Controllers/FileTransferController.cs @@ -23,265 +23,254 @@ using OneOf; -namespace Altinn.Broker.Controllers +namespace Altinn.Broker.Controllers; + +[ApiController] +[Route("broker/api/v1/filetransfer")] +[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] +public class FileTransferController : Controller { - [ApiController] - [Route("broker/api/v1/filetransfer")] - [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] - public class FileTransferController : Controller + private readonly ILogger _logger; + private readonly IIdempotencyEventRepository _idempotencyEventRepository; + + public FileTransferController(ILogger logger, IIdempotencyEventRepository idempotencyEventRepository) { - private readonly ILogger _logger; - private readonly IIdempotencyEventRepository _idempotencyEventRepository; - private readonly IResourceRepository _resourceRepository; - private readonly IFileTransferRepository _fileTransferRepository; + _logger = logger; + _idempotencyEventRepository = idempotencyEventRepository; + } - public FileTransferController(ILogger logger, IIdempotencyEventRepository idempotencyEventRepository, IResourceRepository resourceRepository, IFileTransferRepository fileTransferRepository) - { - _logger = logger; - _idempotencyEventRepository = idempotencyEventRepository; - _resourceRepository = resourceRepository; - _fileTransferRepository = fileTransferRepository; - } + /// + /// Initialize a file transfer and file upload + /// + /// + [HttpPost] + [Authorize(Policy = AuthorizationConstants.Sender)] + public async Task> InitializeFileTransfer(FileTransferInitalizeExt initializeExt, [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, [FromServices] InitializeFileTransferCommandHandler handler, CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithInitializeFile(initializeExt); + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Initializing file transfer"); + var commandRequest = InitializeFileTransferMapper.MapToRequest(initializeExt, token); - /// - /// Initialize a file transfer and file upload - /// - /// - [HttpPost] - [Authorize(Policy = AuthorizationConstants.Sender)] - public async Task> InitializeFileTransfer(FileTransferInitalizeExt initializeExt, [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, [FromServices] InitializeFileTransferCommandHandler handler, CancellationToken cancellationToken) - { - LogContextHelpers.EnrichLogsWithInitializeFile(initializeExt); - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Initializing file transfer"); - var commandRequest = InitializeFileTransferMapper.MapToRequest(initializeExt, token); + var commandResult = await handler.Process(commandRequest, cancellationToken); + return commandResult.Match( + fileTransferId => Ok(fileTransferId.ToString()), + Problem + ); + } - var commandResult = await handler.Process(commandRequest, cancellationToken); - return commandResult.Match( - fileTransferId => Ok(fileTransferId.ToString()), - Problem - ); - } + /// + /// Upload to an initialized file using a binary stream. + /// + /// + [HttpPost] + [Route("{fileTransferId}/upload")] + [Consumes("application/octet-stream")] + [Authorize(Policy = AuthorizationConstants.Sender)] + public async Task UploadStreamed( + Guid fileTransferId, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] UploadFileCommandHandler handler, + CancellationToken cancellationToken + ) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Uploading file for file transfer {fileTransferId}", fileTransferId.ToString()); + Request.EnableBuffering(); - /// - /// Upload to an initialized file using a binary stream. - /// - /// - [HttpPost] - [Route("{fileTransferId}/upload")] - [Consumes("application/octet-stream")] - [Authorize(Policy = AuthorizationConstants.Sender)] - public async Task UploadStreamed( - Guid fileTransferId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] UploadFileCommandHandler handler, - CancellationToken cancellationToken - ) + var commandResult = await handler.Process(new UploadFileCommandRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Uploading file for file transfer {fileTransferId}", fileTransferId.ToString()); - Request.EnableBuffering(); + FileTransferId = fileTransferId, + Token = token, + UploadStream = Request.Body, + ContentLength = Request.ContentLength ?? Request.Body.Length + }, cancellationToken); + return commandResult.Match( + fileId => Ok(fileId.ToString()), + Problem + ); + } - var fileTransfer = await _fileTransferRepository.GetFileTransfer(fileTransferId, cancellationToken); - var resource = await _resourceRepository.GetResource(fileTransfer.ResourceId, cancellationToken); - var max_upload_size = resource?.MaxFileTransferSize ?? long.Parse(Environment.GetEnvironmentVariable("MAX_FILE_UPLOAD_SIZE")); - if (Request.ContentLength > max_upload_size || Request.Body.Length > max_upload_size) - { - return BadRequest($"File size exceeds maximum allowed size of {max_upload_size} bytes"); - } - var commandResult = await handler.Process(new UploadFileCommandRequest() - { - FileTransferId = fileTransferId, - Token = token, - UploadStream = Request.Body - }, cancellationToken); - return commandResult.Match( - fileId => Ok(fileId.ToString()), - Problem - ); + /// + /// Initialize and upload a file using form-data + /// + /// + [HttpPost] + [Route("upload")] + [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)] + [Authorize(Policy = AuthorizationConstants.Sender)] + public async Task InitializeAndUpload( + [FromForm] FileTransferInitializeAndUploadExt form, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] InitializeFileTransferCommandHandler initializeFileTransferCommandHandler, + [FromServices] UploadFileCommandHandler UploadFileCommandHandler, + CancellationToken cancellationToken + ) + { + LogContextHelpers.EnrichLogsWithInitializeFile(form.Metadata); + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Initializing and uploading fileTransfer"); + var initializeRequest = InitializeFileTransferMapper.MapToRequest(form.Metadata, token); + var initializeResult = await initializeFileTransferCommandHandler.Process(initializeRequest, cancellationToken); + if (initializeResult.IsT1) + { + Problem(initializeResult.AsT1); } + var fileTransferId = initializeResult.AsT0; - /// - /// Initialize and upload a file using form-data - /// - /// - [HttpPost] - [Route("upload")] - [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)] - [Authorize(Policy = AuthorizationConstants.Sender)] - public async Task InitializeAndUpload( - [FromForm] FileTransferInitializeAndUploadExt form, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] InitializeFileTransferCommandHandler initializeFileTransferCommandHandler, - [FromServices] UploadFileCommandHandler UploadFileCommandHandler, - CancellationToken cancellationToken - ) + Request.EnableBuffering(); + var uploadResult = await UploadFileCommandHandler.Process(new UploadFileCommandRequest() { - LogContextHelpers.EnrichLogsWithInitializeFile(form.Metadata); - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Initializing and uploading fileTransfer"); - var initializeRequest = InitializeFileTransferMapper.MapToRequest(form.Metadata, token); - var initializeResult = await initializeFileTransferCommandHandler.Process(initializeRequest, cancellationToken); - if (initializeResult.IsT1) - { - Problem(initializeResult.AsT1); - } - var fileTransferId = initializeResult.AsT0; - - Request.EnableBuffering(); - var uploadResult = await UploadFileCommandHandler.Process(new UploadFileCommandRequest() - { - FileTransferId = fileTransferId, - Token = token, - UploadStream = Request.Body - }, cancellationToken); - return uploadResult.Match( - FileId => Ok(FileId.ToString()), - Problem - ); - } + FileTransferId = fileTransferId, + Token = token, + UploadStream = Request.Body + }, cancellationToken); + return uploadResult.Match( + FileId => Ok(FileId.ToString()), + Problem + ); + } - /// - /// Get information about the file and its current status - /// - /// - [HttpGet] - [Route("{fileTransferId}")] - [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] - public async Task> GetFileTransferOverview( - Guid fileTransferId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] GetFileTransferOverviewQueryHandler handler, - CancellationToken cancellationToken) + /// + /// Get information about the file and its current status + /// + /// + [HttpGet] + [Route("{fileTransferId}")] + [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] + public async Task> GetFileTransferOverview( + Guid fileTransferId, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] GetFileTransferOverviewQueryHandler handler, + CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Getting filetransfer overview for {fileTransferId}", fileTransferId.ToString()); + var queryResult = await handler.Process(new GetFileTransferOverviewQueryRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Getting filetransfer overview for {fileTransferId}", fileTransferId.ToString()); - var queryResult = await handler.Process(new GetFileTransferOverviewQueryRequest() - { - FileTransferId = fileTransferId, - Token = token - }, cancellationToken); - return queryResult.Match( - result => Ok(FileTransferStatusOverviewExtMapper.MapToExternalModel(result.FileTransfer)), - Problem - ); - } + FileTransferId = fileTransferId, + Token = token + }, cancellationToken); + return queryResult.Match( + result => Ok(FileTransferStatusOverviewExtMapper.MapToExternalModel(result.FileTransfer)), + Problem + ); + } - /// - /// Get more detailed information about the file upload for auditing and troubleshooting purposes - /// - /// - [HttpGet] - [Route("{fileTransferId}/details")] - [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] - public async Task> GetFileTransferDetails( - Guid fileTransferId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] GetFileTransferDetailsQueryHandler handler, - CancellationToken cancellationToken) + /// + /// Get more detailed information about the file upload for auditing and troubleshooting purposes + /// + /// + [HttpGet] + [Route("{fileTransferId}/details")] + [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] + public async Task> GetFileTransferDetails( + Guid fileTransferId, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] GetFileTransferDetailsQueryHandler handler, + CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Getting fileTransfer details for {fileTransferId}", fileTransferId.ToString()); + var queryResult = await handler.Process(new GetFileTransferDetailsQueryRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Getting fileTransfer details for {fileTransferId}", fileTransferId.ToString()); - var queryResult = await handler.Process(new GetFileTransferDetailsQueryRequest() - { - FileTransferId = fileTransferId, - Token = token - }, cancellationToken); - return queryResult.Match( - result => Ok(FileTransferStatusDetailsExtMapper.MapToExternalModel(result.FileTransfer, result.FileTransferEvents, result.ActorEvents)), - Problem - ); + FileTransferId = fileTransferId, + Token = token + }, cancellationToken); + return queryResult.Match( + result => Ok(FileTransferStatusDetailsExtMapper.MapToExternalModel(result.FileTransfer, result.FileTransferEvents, result.ActorEvents)), + Problem + ); - } + } - /// - /// Get files that can be accessed by the caller according to specified filters. Result set is limited to 100 files. - /// - /// - [HttpGet] - [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] - public async Task>> GetFileTransfers( - [FromQuery] string resourceId, - [FromQuery] FileTransferStatusExt? status, - [FromQuery] RecipientFileTransferStatusExt? recipientStatus, - [FromQuery] DateTimeOffset? from, - [FromQuery] DateTimeOffset? to, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] GetFileTransfersQueryHandler handler, - CancellationToken cancellationToken) + /// + /// Get files that can be accessed by the caller according to specified filters. Result set is limited to 100 files. + /// + /// + [HttpGet] + [Authorize(Policy = AuthorizationConstants.SenderOrRecipient)] + public async Task>> GetFileTransfers( + [FromQuery] string resourceId, + [FromQuery] FileTransferStatusExt? status, + [FromQuery] RecipientFileTransferStatusExt? recipientStatus, + [FromQuery] DateTimeOffset? from, + [FromQuery] DateTimeOffset? to, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] GetFileTransfersQueryHandler handler, + CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Getting fileTransfers with status {status} created {from} to {to}", status?.ToString(), from?.ToString(), to?.ToString()); + var queryResult = await handler.Process(new GetFileTransfersQueryRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Getting fileTransfers with status {status} created {from} to {to}", status?.ToString(), from?.ToString(), to?.ToString()); - var queryResult = await handler.Process(new GetFileTransfersQueryRequest() - { - Token = token, - ResourceId = resourceId, - Status = status is not null ? (FileTransferStatus)status : null, - RecipientStatus = recipientStatus is not null ? (ActorFileTransferStatus)recipientStatus : null, - From = from, - To = to - }, cancellationToken); - return queryResult.Match( - Ok, - Problem - ); - } + Token = token, + ResourceId = resourceId, + Status = status is not null ? (FileTransferStatus)status : null, + RecipientStatus = recipientStatus is not null ? (ActorFileTransferStatus)recipientStatus : null, + From = from, + To = to + }, cancellationToken); + return queryResult.Match( + Ok, + Problem + ); + } - /// - /// Downloads the file - /// - /// - [HttpGet] - [Route("{fileTransferId}/download")] - [Authorize(Policy = AuthorizationConstants.Recipient)] - public async Task DownloadFile( - Guid fileTransferId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] DownloadFileQueryHandler handler, - CancellationToken cancellationToken) + /// + /// Downloads the file + /// + /// + [HttpGet] + [Route("{fileTransferId}/download")] + [Authorize(Policy = AuthorizationConstants.Recipient)] + public async Task DownloadFile( + Guid fileTransferId, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] DownloadFileQueryHandler handler, + CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Downloading file for file transfer {fileTransferId}", fileTransferId.ToString()); + var queryResult = await handler.Process(new DownloadFileQueryRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Downloading file for file transfer {fileTransferId}", fileTransferId.ToString()); - var queryResult = await handler.Process(new DownloadFileQueryRequest() - { - FileTransferId = fileTransferId, - Token = token - }, cancellationToken); - return queryResult.Match( - result => File(result.DownloadStream, "application/octet-stream", result.FileName), - Problem - ); - } + FileTransferId = fileTransferId, + Token = token + }, cancellationToken); + return queryResult.Match( + result => File(result.DownloadStream, "application/octet-stream", result.FileName), + Problem + ); + } - /// - /// Confirms that the file has been downloaded - /// - /// - [HttpPost] - [Route("{fileTransferId}/confirmdownload")] - [Authorize(Policy = AuthorizationConstants.Recipient)] - public async Task ConfirmDownload( - Guid fileTransferId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] ConfirmDownloadCommandHandler handler, - CancellationToken cancellationToken) + /// + /// Confirms that the file has been downloaded + /// + /// + [HttpPost] + [Route("{fileTransferId}/confirmdownload")] + [Authorize(Policy = AuthorizationConstants.Recipient)] + public async Task ConfirmDownload( + Guid fileTransferId, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] ConfirmDownloadCommandHandler handler, + CancellationToken cancellationToken) + { + LogContextHelpers.EnrichLogsWithToken(token); + _logger.LogInformation("Confirming download for fileTransfer {fileTransferId}", fileTransferId.ToString()); + var requestData = new ConfirmDownloadCommandRequest() { - LogContextHelpers.EnrichLogsWithToken(token); - _logger.LogInformation("Confirming download for fileTransfer {fileTransferId}", fileTransferId.ToString()); - var requestData = new ConfirmDownloadCommandRequest() - { - FileTransferId = fileTransferId, - Token = token - }; - var proccessingFunction = new Func>>(() => handler.Process(requestData, cancellationToken)); - var uniqueString = $"confirmDownload_{fileTransferId}_{token.Consumer}"; - var commandResult = await IdempotencyEventHelper.ProcessEvent(uniqueString, proccessingFunction, _idempotencyEventRepository, cancellationToken); - return commandResult.Match( - (_) => Ok(null), - Problem - ); - } - - private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); + FileTransferId = fileTransferId, + Token = token + }; + var proccessingFunction = new Func>>(() => handler.Process(requestData, cancellationToken)); + var uniqueString = $"confirmDownload_{fileTransferId}_{token.Consumer}"; + var commandResult = await IdempotencyEventHelper.ProcessEvent(uniqueString, proccessingFunction, _idempotencyEventRepository, cancellationToken); + return commandResult.Match( + (_) => Ok(null), + Problem + ); } + + private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); } diff --git a/src/Altinn.Broker.API/Controllers/HealthController.cs b/src/Altinn.Broker.API/Controllers/HealthController.cs index bfc2d154..d317ac62 100644 --- a/src/Altinn.Broker.API/Controllers/HealthController.cs +++ b/src/Altinn.Broker.API/Controllers/HealthController.cs @@ -9,54 +9,53 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; -namespace Altinn.Broker.Controllers +namespace Altinn.Broker.Controllers; + +[ApiController] +[Route("health")] +public class HealthController : ControllerBase { - [ApiController] - [Route("health")] - public class HealthController : ControllerBase - { - private readonly DatabaseConnectionProvider _databaseConnectionProvider; - private readonly AzureResourceManagerOptions _azureResourceManagerOptions; + private readonly DatabaseConnectionProvider _databaseConnectionProvider; + private readonly AzureResourceManagerOptions _azureResourceManagerOptions; - public HealthController(DatabaseConnectionProvider databaseConnectionProvider, IOptions azureResourceManagerOptions) - { - _databaseConnectionProvider = databaseConnectionProvider; - _azureResourceManagerOptions = azureResourceManagerOptions.Value; - } + public HealthController(DatabaseConnectionProvider databaseConnectionProvider, IOptions azureResourceManagerOptions) + { + _databaseConnectionProvider = databaseConnectionProvider; + _azureResourceManagerOptions = azureResourceManagerOptions.Value; + } - [HttpGet] - public async Task HealthCheckAsync() + [HttpGet] + public async Task HealthCheckAsync() + { + try { - try - { - using var connection = await _databaseConnectionProvider.GetConnectionAsync(); - using var command = new Npgsql.NpgsqlCommand("SELECT COUNT(*) FROM broker.file_transfer_status_description", connection); - var count = (long)(command.ExecuteScalar() ?? 0); - if (count == 0) - { - Console.Error.WriteLine("Health: Unable to query database. Is DatabaseOptions__ConnectionString set and is the database migrated"); - return BadRequest("Unable to query database. Is DatabaseOptions__ConnectionString set and is the database migrated?"); - } - } - catch (Exception e) + using var connection = await _databaseConnectionProvider.GetConnectionAsync(); + using var command = new Npgsql.NpgsqlCommand("SELECT COUNT(*) FROM broker.file_transfer_status_description", connection); + var count = (long)(command.ExecuteScalar() ?? 0); + if (count == 0) { - Console.Error.WriteLine("Health: Exception thrown while trying to query database: {exception}", e); - return BadRequest("Exception thrown while trying to query database"); + Console.Error.WriteLine("Health: Unable to query database. Is DatabaseOptions__ConnectionString set and is the database migrated"); + return BadRequest("Unable to query database. Is DatabaseOptions__ConnectionString set and is the database migrated?"); } + } + catch (Exception e) + { + Console.Error.WriteLine("Health: Exception thrown while trying to query database: {exception}", e); + return BadRequest("Exception thrown while trying to query database"); + } - // Verify that resource manager has access to our subscription - var credentials = new DefaultAzureCredential(); - var armClient = new ArmClient(credentials); - var subscription = armClient.GetSubscriptionResource(new ResourceIdentifier($"/subscriptions/{_azureResourceManagerOptions.SubscriptionId}")); - var resourceGroupCollection = subscription.GetResourceGroups(); - var resourceGroupCount = resourceGroupCollection.Count(); - if (resourceGroupCount < 1) - { - return BadRequest($"Resource groups not found under subscription with id: {subscription.Id}. Are the service principal environment variables set?"); - } - await resourceGroupCollection.GetAsync(_azureResourceManagerOptions.ApplicationResourceGroupName); - - return Ok("Environment properly configured"); + // Verify that resource manager has access to our subscription + var credentials = new DefaultAzureCredential(); + var armClient = new ArmClient(credentials); + var subscription = armClient.GetSubscriptionResource(new ResourceIdentifier($"/subscriptions/{_azureResourceManagerOptions.SubscriptionId}")); + var resourceGroupCollection = subscription.GetResourceGroups(); + var resourceGroupCount = resourceGroupCollection.Count(); + if (resourceGroupCount < 1) + { + return BadRequest($"Resource groups not found under subscription with id: {subscription.Id}. Are the service principal environment variables set?"); } + await resourceGroupCollection.GetAsync(_azureResourceManagerOptions.ApplicationResourceGroupName); + + return Ok("Environment properly configured"); } } diff --git a/src/Altinn.Broker.API/Controllers/LegacyFileController.cs b/src/Altinn.Broker.API/Controllers/LegacyFileController.cs index 5223d84f..e53c1641 100644 --- a/src/Altinn.Broker.API/Controllers/LegacyFileController.cs +++ b/src/Altinn.Broker.API/Controllers/LegacyFileController.cs @@ -2,14 +2,12 @@ using Altinn.Broker.Application; using Altinn.Broker.Application.ConfirmDownloadCommand; using Altinn.Broker.Application.DownloadFileQuery; -using Altinn.Broker.Application.GetFileTransferDetailsQuery; using Altinn.Broker.Application.GetFileTransferOverviewQuery; using Altinn.Broker.Application.GetFileTransfersQuery; using Altinn.Broker.Application.InitializeFileTransferCommand; using Altinn.Broker.Application.UploadFileCommand; using Altinn.Broker.Core.Domain; using Altinn.Broker.Core.Domain.Enums; -using Altinn.Broker.Core.Models; using Altinn.Broker.Enums; using Altinn.Broker.Helpers; using Altinn.Broker.Mappers; @@ -19,235 +17,219 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace Altinn.Broker.Controllers +namespace Altinn.Broker.Controllers; + +/// +/// The LegacyFileController allows integration from the Altinn 2 BrokerBridge component to allow legacy users access to Altinn 3 Broker +/// +[ApiController] +[Route("broker/api/legacy/v1/file")] +[Authorize(AuthenticationSchemes = AuthorizationConstants.Legacy)] +[Authorize(Policy = AuthorizationConstants.Legacy)] +public class LegacyFileController : Controller { + private readonly ILogger _logger; + + public LegacyFileController(ILogger logger) + { + _logger = logger; + } + + /// + /// Initialize a file upload + /// + /// + [HttpPost] + public async Task> InitializeFile(LegacyFileInitalizeExt initializeExt, [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, [FromServices] InitializeFileTransferCommandHandler handler, CancellationToken cancellationToken) + { + CallerIdentity legacyToken = CreateLegacyToken(initializeExt.Sender, token); + + LogContextHelpers.EnrichLogsWithLegacyInitializeFile(initializeExt); + LogContextHelpers.EnrichLogsWithToken(legacyToken); + _logger.LogInformation("Legacy - Initializing file"); + var commandRequest = LegacyInitializeFileMapper.MapToRequest(initializeExt, token); + var commandResult = await handler.Process(commandRequest, cancellationToken); + return commandResult.Match( + fileId => Ok(fileId.ToString()), + Problem + ); + } + /// - /// The LegacyFileController allows integration from the Altinn 2 BrokerBridge component to allow legacy users access to Altinn 3 Broker + /// Upload to an initialized file using a binary stream. /// - [ApiController] - [Route("broker/api/legacy/v1/file")] - [Authorize(AuthenticationSchemes = AuthorizationConstants.Legacy)] - [Authorize(Policy = AuthorizationConstants.Legacy)] - public class LegacyFileController : Controller + /// + [HttpPost] + [Route("{fileTransferId}/upload")] + [Consumes("application/octet-stream")] + public async Task UploadFileStreamed( + Guid fileTransferId, + [FromQuery] string onBehalfOfConsumer, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] UploadFileCommandHandler handler, + CancellationToken cancellationToken + ) { - private readonly ILogger _logger; + CallerIdentity legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - public LegacyFileController(ILogger logger) + LogContextHelpers.EnrichLogsWithToken(legacyToken); + _logger.LogInformation("Legacy - Uploading file for file transfer {fileId}", fileTransferId.ToString()); + Request.EnableBuffering(); + var commandResult = await handler.Process(new UploadFileCommandRequest() { - _logger = logger; - } + FileTransferId = fileTransferId, + Token = legacyToken, + UploadStream = Request.Body, + IsLegacy = true + }, cancellationToken); + return commandResult.Match( + fileId => Ok(fileId.ToString()), + Problem + ); + } - /// - /// Initialize a file upload - /// - /// - [HttpPost] - public async Task> InitializeFile(LegacyFileInitalizeExt initializeExt, [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, [FromServices] InitializeFileTransferCommandHandler handler, CancellationToken cancellationToken) + /// + /// Get information about the file and its current status + /// + /// + [HttpGet] + [Route("{fileId}")] + public async Task> GetFileOverview( + Guid fileId, + [FromQuery] string onBehalfOfConsumer, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] GetFileTransferOverviewQueryHandler handler, + CancellationToken cancellationToken) + { + CallerIdentity legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); + + LogContextHelpers.EnrichLogsWithToken(legacyToken); + _logger.LogInformation("Legacy - Getting file overview for {fileId}", fileId.ToString()); + var queryResult = await handler.Process(new GetFileTransferOverviewQueryRequest() { - CallerIdentity legacyToken = CreateLegacyToken(initializeExt.Sender, token); - - LogContextHelpers.EnrichLogsWithLegacyInitializeFile(initializeExt); - LogContextHelpers.EnrichLogsWithToken(legacyToken); - _logger.LogInformation("Legacy - Initializing file"); - var commandRequest = LegacyInitializeFileMapper.MapToRequest(initializeExt, token); - var commandResult = await handler.Process(commandRequest, cancellationToken); - return commandResult.Match( - fileId => Ok(fileId.ToString()), - Problem - ); - } + FileTransferId = fileId, + Token = legacyToken, + IsLegacy = true + }, cancellationToken); + return queryResult.Match( + result => Ok(LegacyFileStatusOverviewExtMapper.MapToExternalModel(result.FileTransfer)), + Problem + ); + } - /// - /// Upload to an initialized file using a binary stream. - /// - /// - [HttpPost] - [Route("{fileTransferId}/upload")] - [Consumes("application/octet-stream")] - public async Task UploadFileStreamed( - Guid fileTransferId, - [FromQuery] string onBehalfOfConsumer, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] UploadFileCommandHandler handler, - CancellationToken cancellationToken - ) + /// + /// Get files that can be accessed by the caller according to specified filters. + /// + /// + [HttpGet] + public async Task>> GetFiles( + [FromQuery] FileTransferStatusExt? status, + [FromQuery] RecipientFileTransferStatusExt? recipientStatus, + [FromQuery] DateTimeOffset? from, + [FromQuery] DateTimeOffset? to, + [FromQuery] string? resourceId, + [FromQuery] string? onBehalfOfConsumer, + [FromQuery] string[]? recipients, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] LegacyGetFilesQueryHandler handler, + CancellationToken cancellationToken) + { + // HasAvailableFiles calls are not made on behalf of any consumer. + CallerIdentity? legacyToken = null; + if (!string.IsNullOrWhiteSpace(onBehalfOfConsumer)) { - CallerIdentity legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - - LogContextHelpers.EnrichLogsWithToken(legacyToken); - _logger.LogInformation("Legacy - Uploading file for file transfer {fileId}", fileTransferId.ToString()); - Request.EnableBuffering(); - var commandResult = await handler.Process(new UploadFileCommandRequest() - { - FileTransferId = fileTransferId, - Token = legacyToken, - UploadStream = Request.Body, - IsLegacy = true - }, cancellationToken); - return commandResult.Match( - fileId => Ok(fileId.ToString()), - Problem - ); + legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); } - /// - /// Get information about the file and its current status - /// - /// - [HttpGet] - [Route("{fileId}")] - public async Task> GetFileOverview( - Guid fileId, - [FromQuery] string onBehalfOfConsumer, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] GetFileTransferOverviewQueryHandler handler, - CancellationToken cancellationToken) + LogContextHelpers.EnrichLogsWithToken(legacyToken ?? token); + string recipientsString = string.Empty; + if (recipients?.Length > 0) { - CallerIdentity legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - - LogContextHelpers.EnrichLogsWithToken(legacyToken); - _logger.LogInformation("Legacy - Getting file overview for {fileId}", fileId.ToString()); - var queryResult = await handler.Process(new GetFileTransferOverviewQueryRequest() - { - FileTransferId = fileId, - Token = legacyToken, - IsLegacy = true - }, cancellationToken); - return queryResult.Match( - result => Ok(LegacyFileStatusOverviewExtMapper.MapToExternalModel(result.FileTransfer)), - Problem - ); + recipientsString = string.Join(',', recipients); + _logger.LogInformation("Getting files with status {status} created {from} to {to} for recipients {recipients}", recipientStatus?.ToString(), from?.ToString(), to?.ToString(), recipientsString); } - - /// - /// Get more detailed information about the file upload for auditing and troubleshooting purposes - /// - /// - [HttpGet] - [Route("{fileId}/details")] - public async Task> GetFileTransferDetails( - Guid fileId, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] GetFileTransferDetailsQueryHandler handler, - CancellationToken cancellationToken) + else { - throw new NotImplementedException(); + _logger.LogInformation("Getting files with status {status} created {from} to {to} for consumer {consumer}", recipientStatus?.ToString(), from?.ToString(), to?.ToString(), onBehalfOfConsumer); } - /// - /// Get files that can be accessed by the caller according to specified filters. - /// - /// - [HttpGet] - public async Task>> GetFiles( - [FromQuery] FileTransferStatusExt? status, - [FromQuery] RecipientFileTransferStatusExt? recipientStatus, - [FromQuery] DateTimeOffset? from, - [FromQuery] DateTimeOffset? to, - [FromQuery] string? resourceId, - [FromQuery] string? onBehalfOfConsumer, - [FromQuery] string[]? recipients, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] LegacyGetFilesQueryHandler handler, - CancellationToken cancellationToken) + var queryResult = await handler.Process(new LegacyGetFilesQueryRequest() { - // HasAvailableFiles calls are not made on behalf of any consumer. - CallerIdentity? legacyToken = null; - if (!string.IsNullOrWhiteSpace(onBehalfOfConsumer)) - { - legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - } - - LogContextHelpers.EnrichLogsWithToken(legacyToken ?? token); - string recipientsString = string.Empty; - if (recipients?.Length > 0) - { - recipientsString = string.Join(',', recipients); - _logger.LogInformation("Getting files with status {status} created {from} to {to} for recipients {recipients}", recipientStatus?.ToString(), from?.ToString(), to?.ToString(), recipientsString); - } - else - { - _logger.LogInformation("Getting files with status {status} created {from} to {to} for consumer {consumer}", recipientStatus?.ToString(), from?.ToString(), to?.ToString(), onBehalfOfConsumer); - } - - var queryResult = await handler.Process(new LegacyGetFilesQueryRequest() - { - Token = legacyToken ?? token, - ResourceId = resourceId ?? string.Empty, - RecipientFileTransferStatus = recipientStatus is not null ? (ActorFileTransferStatus)recipientStatus : null, - FileTransferStatus = status is not null ? (FileTransferStatus)status : null, - OnBehalfOfConsumer = onBehalfOfConsumer, - From = from, - To = to, - Recipients = recipients - }, cancellationToken); - return queryResult.Match( - Ok, - Problem - ); - } + Token = legacyToken ?? token, + ResourceId = resourceId ?? string.Empty, + RecipientFileTransferStatus = recipientStatus is not null ? (ActorFileTransferStatus)recipientStatus : null, + FileTransferStatus = status is not null ? (FileTransferStatus)status : null, + OnBehalfOfConsumer = onBehalfOfConsumer, + From = from, + To = to, + Recipients = recipients + }, cancellationToken); + return queryResult.Match( + Ok, + Problem + ); + } - /// - /// Downloads the file - /// - /// - [HttpGet] - [Route("{fileId}/download")] - public async Task DownloadFile( - Guid fileId, - [FromQuery] string onBehalfOfConsumer, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] DownloadFileQueryHandler handler, - CancellationToken cancellationToken) + /// + /// Downloads the file + /// + /// + [HttpGet] + [Route("{fileId}/download")] + public async Task DownloadFile( + Guid fileId, + [FromQuery] string onBehalfOfConsumer, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] DownloadFileQueryHandler handler, + CancellationToken cancellationToken) + { + CallerIdentity? legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); + LogContextHelpers.EnrichLogsWithToken(legacyToken); + _logger.LogInformation("Downloading file {fileId}", fileId.ToString()); + var queryResult = await handler.Process(new DownloadFileQueryRequest() { - CallerIdentity? legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - LogContextHelpers.EnrichLogsWithToken(legacyToken); - _logger.LogInformation("Downloading file {fileId}", fileId.ToString()); - var queryResult = await handler.Process(new DownloadFileQueryRequest() - { - FileTransferId = fileId, - Token = legacyToken, - IsLegacy = true - }, cancellationToken); - return queryResult.Match( - result => File(result.DownloadStream, "application/octet-stream", result.FileName), - Problem - ); - } + FileTransferId = fileId, + Token = legacyToken, + IsLegacy = true + }, cancellationToken); + return queryResult.Match( + result => File(result.DownloadStream, "application/octet-stream", result.FileName), + Problem + ); + } - /// - /// Confirms that the file has been downloaded - /// - /// - [HttpPost] - [Route("{fileId}/confirmdownload")] - public async Task ConfirmDownload( - Guid fileId, - [FromQuery] string onBehalfOfConsumer, - [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, - [FromServices] ConfirmDownloadCommandHandler handler, - CancellationToken cancellationToken) + /// + /// Confirms that the file has been downloaded + /// + /// + [HttpPost] + [Route("{fileId}/confirmdownload")] + public async Task ConfirmDownload( + Guid fileId, + [FromQuery] string onBehalfOfConsumer, + [ModelBinder(typeof(MaskinportenModelBinder))] CallerIdentity token, + [FromServices] ConfirmDownloadCommandHandler handler, + CancellationToken cancellationToken) + { + CallerIdentity? legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); + LogContextHelpers.EnrichLogsWithToken(legacyToken); + _logger.LogInformation("Confirming download for file {fileId}", fileId.ToString()); + var commandResult = await handler.Process(new ConfirmDownloadCommandRequest() { - CallerIdentity? legacyToken = CreateLegacyToken(onBehalfOfConsumer, token); - LogContextHelpers.EnrichLogsWithToken(legacyToken); - _logger.LogInformation("Confirming download for file {fileId}", fileId.ToString()); - var commandResult = await handler.Process(new ConfirmDownloadCommandRequest() - { - FileTransferId = fileId, - Token = legacyToken, - IsLegacy = true - }, cancellationToken); - return commandResult.Match( - Ok, - Problem - ); - } + FileTransferId = fileId, + Token = legacyToken, + IsLegacy = true + }, cancellationToken); + return commandResult.Match( + Ok, + Problem + ); + } - private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); + private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); - private static CallerIdentity CreateLegacyToken(string onBehalfOfConsumer, CallerIdentity callingToken) - { - return new CallerIdentity(callingToken.Scope, onBehalfOfConsumer, callingToken.ClientId); - } + private static CallerIdentity CreateLegacyToken(string onBehalfOfConsumer, CallerIdentity callingToken) + { + return new CallerIdentity(callingToken.Scope, onBehalfOfConsumer, callingToken.ClientId); } } diff --git a/src/Altinn.Broker.API/Controllers/MalwareScanResultsController.cs b/src/Altinn.Broker.API/Controllers/MalwareScanResultsController.cs index adac4d51..77a33840 100644 --- a/src/Altinn.Broker.API/Controllers/MalwareScanResultsController.cs +++ b/src/Altinn.Broker.API/Controllers/MalwareScanResultsController.cs @@ -13,58 +13,61 @@ using OneOf; -namespace Altinn.Broker.Webhooks.Controllers +namespace Altinn.Broker.Webhooks.Controllers; + +[ApiController] +[Route("broker/api/v1/webhooks/malwarescanresults")] +public class MalwareScanResultsController : Controller { - [ApiController] - [Route("broker/api/v1/webhooks/malwarescanresults")] - public class MalwareScanResultsController : Controller - { - private readonly IIdempotencyEventRepository _idempotencyEventRepository; - private readonly ILogger _logger; + private readonly IIdempotencyEventRepository _idempotencyEventRepository; + private readonly ILogger _logger; - public MalwareScanResultsController(IIdempotencyEventRepository idempotencyEventRepository, ILogger logger) - { - _idempotencyEventRepository = idempotencyEventRepository; - _logger = logger; - } + public MalwareScanResultsController(IIdempotencyEventRepository idempotencyEventRepository, ILogger logger) + { + _idempotencyEventRepository = idempotencyEventRepository; + _logger = logger; + } - [HttpPost] - [Consumes("application/json")] - public async Task ProcessMalwareScanResult([FromServices] MalwareScanningResultHandler handler, CancellationToken cancellationToken) + [HttpPost] + [Consumes("application/json")] + public async Task ProcessMalwareScanResult([FromServices] MalwareScanningResultHandler handler, CancellationToken cancellationToken) + { + BinaryData events = await BinaryData.FromStreamAsync(this.Request.Body); + EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events); + foreach (EventGridEvent eventGridEvent in eventGridEvents) { - BinaryData events = await BinaryData.FromStreamAsync(this.Request.Body); - EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events); - foreach (EventGridEvent eventGridEvent in eventGridEvents) + _logger.LogInformation("Got malware scan event of type {EventType} with data: {eventData}", eventGridEvent.EventType, eventGridEvent.Data.ToString()); + if (eventGridEvent.TryGetSystemEventData(out object eventData)) { - _logger.LogInformation("Got malware scan event of type {EventType} with data: {eventData}", eventGridEvent.EventType, eventGridEvent.Data.ToString()); - if (eventGridEvent.TryGetSystemEventData(out object eventData)) + if (eventData is SubscriptionValidationEventData subscriptionValidationEventData) { - if (eventData is SubscriptionValidationEventData subscriptionValidationEventData) + // TODO: validate that eventGridEvent WebHook subscription is actually from an Altinn Azure Defender EventGrid + var responseData = new { - // TODO: validate that eventGridEvent WebHook subscription is actually from an Altinn Azure Defender EventGrid - var responseData = new - { - ValidationResponse = subscriptionValidationEventData.ValidationCode - }; - return new OkObjectResult(responseData); - } + ValidationResponse = subscriptionValidationEventData.ValidationCode + }; + return new OkObjectResult(responseData); } - else if (eventGridEvent.EventType == "Microsoft.Security.MalwareScanningResult") + } + else if (eventGridEvent.EventType == "Microsoft.Security.MalwareScanningResult") + { + string jsonString = eventGridEvent.Data.ToString(); + ScanResultData? result = JsonConvert.DeserializeObject(jsonString); + if (result is null) { - string jsonString = eventGridEvent.Data.ToString(); - ScanResultData result = JsonConvert.DeserializeObject(jsonString); - var processFunction = new Func>>(() => handler.Process(result, cancellationToken)); - var commandResult = await IdempotencyEventHelper.ProcessEvent(result.ETag, processFunction, _idempotencyEventRepository, cancellationToken); - return commandResult.Match( - Ok, - Problem - ); - + throw new InvalidOperationException("Failed to deserialize malware scan result data"); } - } + var processFunction = new Func>>(() => handler.Process(result, cancellationToken)); + var commandResult = await IdempotencyEventHelper.ProcessEvent(result.ETag, processFunction, _idempotencyEventRepository, cancellationToken); + return commandResult.Match( + Ok, + Problem + ); - return Ok(); + } } - private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); + + return Ok(); } + private ObjectResult Problem(Error error) => Problem(detail: error.Message, statusCode: (int)error.StatusCode); } diff --git a/src/Altinn.Broker.API/Enums/FileTransferStatusExt.cs b/src/Altinn.Broker.API/Enums/FileTransferStatusExt.cs index ca267af9..d13fb972 100644 --- a/src/Altinn.Broker.API/Enums/FileTransferStatusExt.cs +++ b/src/Altinn.Broker.API/Enums/FileTransferStatusExt.cs @@ -1,14 +1,13 @@ -namespace Altinn.Broker.Enums +namespace Altinn.Broker.Enums; + +public enum FileTransferStatusExt { - public enum FileTransferStatusExt - { - Initialized, - UploadStarted, - UploadProcessing, - Published, - Cancelled, - AllConfirmedDownloaded, - Purged, - Failed - } + Initialized, + UploadStarted, + UploadProcessing, + Published, + Cancelled, + AllConfirmedDownloaded, + Purged, + Failed } diff --git a/src/Altinn.Broker.API/Enums/LegacyFileStatusExt.cs b/src/Altinn.Broker.API/Enums/LegacyFileStatusExt.cs index ab316717..e1b958db 100644 --- a/src/Altinn.Broker.API/Enums/LegacyFileStatusExt.cs +++ b/src/Altinn.Broker.API/Enums/LegacyFileStatusExt.cs @@ -1,14 +1,13 @@ -namespace Altinn.Broker.Enums +namespace Altinn.Broker.Enums; + +public enum LegacyFileStatusExt { - public enum LegacyFileStatusExt - { - Initialized, - UploadStarted, - UploadProcessing, - Published, - Cancelled, - AllConfirmedDownloaded, - Deleted, - Failed - } + Initialized, + UploadStarted, + UploadProcessing, + Published, + Cancelled, + AllConfirmedDownloaded, + Deleted, + Failed } diff --git a/src/Altinn.Broker.API/Enums/LegacyRecipientFileStatusExt.cs b/src/Altinn.Broker.API/Enums/LegacyRecipientFileStatusExt.cs index b4ddd960..ce73c364 100644 --- a/src/Altinn.Broker.API/Enums/LegacyRecipientFileStatusExt.cs +++ b/src/Altinn.Broker.API/Enums/LegacyRecipientFileStatusExt.cs @@ -1,9 +1,8 @@ -namespace Altinn.Broker.Enums +namespace Altinn.Broker.Enums; + +public enum LegacyRecipientFileStatusExt { - public enum LegacyRecipientFileStatusExt - { - Initialized, - DownloadStarted, - DownloadConfirmed - } + Initialized, + DownloadStarted, + DownloadConfirmed } diff --git a/src/Altinn.Broker.API/Enums/RecipientFileTransferStatusExt.cs b/src/Altinn.Broker.API/Enums/RecipientFileTransferStatusExt.cs index 7e4745c6..a00e25f3 100644 --- a/src/Altinn.Broker.API/Enums/RecipientFileTransferStatusExt.cs +++ b/src/Altinn.Broker.API/Enums/RecipientFileTransferStatusExt.cs @@ -1,9 +1,8 @@ -namespace Altinn.Broker.Enums +namespace Altinn.Broker.Enums; + +public enum RecipientFileTransferStatusExt { - public enum RecipientFileTransferStatusExt - { - Initialized, - DownloadStarted, - DownloadConfirmed - } + Initialized, + DownloadStarted, + DownloadConfirmed } diff --git a/src/Altinn.Broker.API/Helpers/IdempotencyEventHelper.cs b/src/Altinn.Broker.API/Helpers/IdempotencyEventHelper.cs index 511fc2a9..7e205678 100644 --- a/src/Altinn.Broker.API/Helpers/IdempotencyEventHelper.cs +++ b/src/Altinn.Broker.API/Helpers/IdempotencyEventHelper.cs @@ -19,7 +19,7 @@ public static async Task> ProcessEvent(string uniqueString, F // Call you method return await process(); } - catch (Exception e) + catch (Exception) { // Delete the entry on error to make sure the next one isn't ignored await idempotencyEventRepository.DeleteIdempotencyEventAsync(uniqueString, cancellationToken); diff --git a/src/Altinn.Broker.API/Helpers/ValidateElementsInList.cs b/src/Altinn.Broker.API/Helpers/ValidateElementsInList.cs index 7312f043..adbfd7e5 100644 --- a/src/Altinn.Broker.API/Helpers/ValidateElementsInList.cs +++ b/src/Altinn.Broker.API/Helpers/ValidateElementsInList.cs @@ -13,12 +13,12 @@ public ValidateElementsInList(Type attributeType, params object[] attributeArgs) _attributeArgs = attributeArgs; } - protected override ValidationResult IsValid(object value, ValidationContext validationContext) + protected override ValidationResult IsValid(object? value, ValidationContext validationContext) { var list = value as IEnumerable; if (list != null) { - var attributeInstance = (ValidationAttribute)Activator.CreateInstance(_attributeType, _attributeArgs); + var attributeInstance = (ValidationAttribute)Activator.CreateInstance(_attributeType, _attributeArgs)!; foreach (var item in list) { if (!attributeInstance.IsValid(item)) @@ -28,6 +28,6 @@ protected override ValidationResult IsValid(object value, ValidationContext vali } } - return ValidationResult.Success; + return ValidationResult.Success!; } } diff --git a/src/Altinn.Broker.API/Mappers/FileStatusOverviewExtMapper.cs b/src/Altinn.Broker.API/Mappers/FileStatusOverviewExtMapper.cs index c07f8d12..1571ffe8 100644 --- a/src/Altinn.Broker.API/Mappers/FileStatusOverviewExtMapper.cs +++ b/src/Altinn.Broker.API/Mappers/FileStatusOverviewExtMapper.cs @@ -72,7 +72,8 @@ internal static List MapToRecipients(List .GroupBy(receipt => receipt.Actor.ActorExternalId) .Select(receiptsForRecipient => receiptsForRecipient.MaxBy(receipt => receipt.Date)) - .ToList(); + .Where(receipt => receipt is not null) + .ToList().OfType(); return lastStatusForEveryRecipient.Select(statusEvent => new RecipientFileTransferStatusDetailsExt() { Recipient = statusEvent.Actor.ActorExternalId, diff --git a/src/Altinn.Broker.API/Mappers/LegacyFileStatusOverviewExtMapper.cs b/src/Altinn.Broker.API/Mappers/LegacyFileStatusOverviewExtMapper.cs index 456dfb6e..89e00f02 100644 --- a/src/Altinn.Broker.API/Mappers/LegacyFileStatusOverviewExtMapper.cs +++ b/src/Altinn.Broker.API/Mappers/LegacyFileStatusOverviewExtMapper.cs @@ -68,7 +68,8 @@ internal static List MapToRecipients(List receipt.Actor.ActorExternalId) .Select(receiptsForRecipient => receiptsForRecipient.MaxBy(receipt => receipt.Date)) - .ToList(); + .Where(receipt => receipt is not null) + .ToList().OfType(); return lastStatusForEveryRecipient.Select(statusEvent => new LegacyRecipientFileStatusDetailsExt() { Recipient = statusEvent.Actor.ActorExternalId, diff --git a/src/Altinn.Broker.API/Middlewares/RequestLoggingMiddleware.cs b/src/Altinn.Broker.API/Middlewares/RequestLoggingMiddleware.cs index d8c5839d..7d48d254 100644 --- a/src/Altinn.Broker.API/Middlewares/RequestLoggingMiddleware.cs +++ b/src/Altinn.Broker.API/Middlewares/RequestLoggingMiddleware.cs @@ -1,73 +1,72 @@ -namespace Altinn.Broker.Middlewares +namespace Altinn.Broker.Middlewares; + +public class RequestLoggingMiddleware { - public class RequestLoggingMiddleware + private readonly RequestDelegate _next; + + private readonly ILogger _logger; + + public RequestLoggingMiddleware( + RequestDelegate next, + ILogger logger + ) { - private readonly RequestDelegate _next; + _next = next; + _logger = logger; + } - private readonly ILogger _logger; + private static readonly string[] IgnoredPaths = + { + "/health", + "/hangfire" + }; - public RequestLoggingMiddleware( - RequestDelegate next, - ILogger logger - ) + public async Task Invoke(HttpContext httpContext) + { + // Log request + var requestMethod = httpContext.Request.Method; + var requestPath = httpContext.Request.PathBase.Add(httpContext.Request.Path).ToString(); + if (!IgnoredPaths.Any(path => requestPath.ToLowerInvariant().StartsWith(path))) { - _next = next; - _logger = logger; + _logger.LogInformation( + "Request for method {RequestMethod} at {RequestPath}", + requestMethod, + requestPath + ); } - private static readonly string[] IgnoredPaths = - { - "/health", - "/hangfire" - }; + await _next(httpContext); - public async Task Invoke(HttpContext httpContext) + // Log response + var statusCode = httpContext.Response.StatusCode; + if (!IgnoredPaths.Any(path => requestPath.ToLowerInvariant().StartsWith(path))) { - // Log request - var requestMethod = httpContext.Request.Method; - var requestPath = httpContext.Request.PathBase.Add(httpContext.Request.Path).ToString(); - if (!IgnoredPaths.Any(path => requestPath.ToLowerInvariant().StartsWith(path))) + if (statusCode >= 200 && statusCode < 400) { _logger.LogInformation( - "Request for method {RequestMethod} at {RequestPath}", + "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", requestMethod, - requestPath + requestPath, + httpContext.Response.StatusCode ); } - - await _next(httpContext); - - // Log response - var statusCode = httpContext.Response.StatusCode; - if (!IgnoredPaths.Any(path => requestPath.ToLowerInvariant().StartsWith(path))) + else if (statusCode >= 400 && statusCode < 500) { - if (statusCode >= 200 && statusCode < 400) - { - _logger.LogInformation( - "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", - requestMethod, - requestPath, - httpContext.Response.StatusCode - ); - } - else if (statusCode >= 400 && statusCode < 500) - { - _logger.LogWarning( - "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", - requestMethod, - requestPath, - httpContext.Response.StatusCode - ); - } - else if (statusCode >= 500) - { - _logger.LogError( - "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", - requestMethod, - requestPath, - httpContext.Response.StatusCode - ); - } + _logger.LogWarning( + "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", + requestMethod, + requestPath, + httpContext.Response.StatusCode + ); + } + else if (statusCode >= 500) + { + _logger.LogError( + "Response for method {RequestMethod} at {RequestPath} with status code {ResponseStatusCode}", + requestMethod, + requestPath, + httpContext.Response.StatusCode + ); } } } diff --git a/src/Altinn.Broker.API/Models/FileTransferInitializeAndUploadExt.cs b/src/Altinn.Broker.API/Models/FileTransferInitializeAndUploadExt.cs index 5e946e88..d2334df4 100644 --- a/src/Altinn.Broker.API/Models/FileTransferInitializeAndUploadExt.cs +++ b/src/Altinn.Broker.API/Models/FileTransferInitializeAndUploadExt.cs @@ -2,7 +2,7 @@ public class FileTransferInitializeAndUploadExt { - public FileTransferInitalizeExt Metadata { get; set; } + public required FileTransferInitalizeExt Metadata { get; set; } - public IFormFile FileTransfer { get; set; } + public required IFormFile FileTransfer { get; set; } } diff --git a/src/Altinn.Broker.API/Models/FileTransferInitializeExt.cs b/src/Altinn.Broker.API/Models/FileTransferInitializeExt.cs index 1b123a5b..41e7a7da 100644 --- a/src/Altinn.Broker.API/Models/FileTransferInitializeExt.cs +++ b/src/Altinn.Broker.API/Models/FileTransferInitializeExt.cs @@ -3,89 +3,88 @@ using Altinn.Broker.Helpers; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +/// +/// API input model for file initialization. +/// +public class FileTransferInitalizeExt { /// - /// API input model for file initialization. + /// The filename including extension /// - public class FileTransferInitalizeExt - { - /// - /// The filename including extension - /// - [JsonPropertyName("fileName")] - [StringLength(255, MinimumLength = 1)] - [Required] - public string FileName { get; set; } = string.Empty; + [JsonPropertyName("fileName")] + [StringLength(255, MinimumLength = 1)] + [Required] + public string FileName { get; set; } = string.Empty; - /// - /// The Altinn resource ID - /// - [JsonPropertyName("resourceId")] - [StringLength(255, MinimumLength = 1)] - [Required] - public string ResourceId { get; set; } = string.Empty; + /// + /// The Altinn resource ID + /// + [JsonPropertyName("resourceId")] + [StringLength(255, MinimumLength = 1)] + [Required] + public string ResourceId { get; set; } = string.Empty; - /// - /// Used by senders and receivers to identify specific file using external identification methods. - /// - [JsonPropertyName("sendersFileTransferReference")] - [StringLength(4096, MinimumLength = 1)] - public string SendersFileTransferReference { get; set; } = string.Empty; + /// + /// Used by senders and receivers to identify specific file using external identification methods. + /// + [JsonPropertyName("sendersFileTransferReference")] + [StringLength(4096, MinimumLength = 1)] + public string SendersFileTransferReference { get; set; } = string.Empty; - /// - /// The sender organization of the file - /// - [JsonPropertyName("sender")] - [RegularExpressionAttribute(@"^\d{4}:\d{9}$", ErrorMessage = "Organization numbers should be on the form countrycode:organizationnumber, for instance 0192:910753614")] - [Required] - public string Sender { get; set; } = string.Empty; + /// + /// The sender organization of the file + /// + [JsonPropertyName("sender")] + [RegularExpressionAttribute(@"^\d{4}:\d{9}$", ErrorMessage = "Organization numbers should be on the form countrycode:organizationnumber, for instance 0192:910753614")] + [Required] + public string Sender { get; set; } = string.Empty; - /// - /// The recipient organizations of the broker fileTransfer - /// - [JsonPropertyName("recipients")] - [ValidateElementsInList(typeof(RegularExpressionAttribute), @"^\d{4}:\d{9}$", ErrorMessage = "Each recipient should be on the form countrycode:organizationnumber, for instance 0192:910753614")] - [Required] - [MinLength(1, ErrorMessage = "One or more recipients are required")] - public List Recipients { get; set; } = new List(); + /// + /// The recipient organizations of the broker fileTransfer + /// + [JsonPropertyName("recipients")] + [ValidateElementsInList(typeof(RegularExpressionAttribute), @"^\d{4}:\d{9}$", ErrorMessage = "Each recipient should be on the form countrycode:organizationnumber, for instance 0192:910753614")] + [Required] + [MinLength(1, ErrorMessage = "One or more recipients are required")] + public List Recipients { get; set; } = new List(); - /// - /// User-defined properties related to the file - /// - [JsonPropertyName("propertyList")] - [MaxLength(10, ErrorMessage = "propertyList can contain at most 10 properties")] - public Dictionary PropertyList { get; set; } = new Dictionary(); + /// + /// User-defined properties related to the file + /// + [JsonPropertyName("propertyList")] + [MaxLength(10, ErrorMessage = "propertyList can contain at most 10 properties")] + public Dictionary PropertyList { get; set; } = new Dictionary(); - /// - /// MD5 checksum for file data. - /// - [JsonPropertyName("checksum")] - [MD5Checksum] - public string? Checksum { get; set; } = string.Empty; + /// + /// MD5 checksum for file data. + /// + [JsonPropertyName("checksum")] + [MD5Checksum] + public string? Checksum { get; set; } = string.Empty; +} +internal class MD5ChecksumAttribute : ValidationAttribute +{ + public MD5ChecksumAttribute() + { } - internal class MD5ChecksumAttribute : ValidationAttribute + + protected override ValidationResult IsValid(object? value, ValidationContext validationContext) { - public MD5ChecksumAttribute() + var stringValue = value as string; + if (string.IsNullOrWhiteSpace(stringValue)) { + return ValidationResult.Success!; } - - protected override ValidationResult IsValid(object value, ValidationContext validationContext) + if (stringValue.Length != 32) + { + return new ValidationResult("The checksum, if used, must be a MD5 hash with a length of 32 characters"); + } + if (stringValue.ToLowerInvariant() != stringValue) { - var stringValue = value as string; - if (string.IsNullOrWhiteSpace(stringValue)) - { - return ValidationResult.Success; - } - if (stringValue.Length != 32) - { - return new ValidationResult("The checksum, if used, must be a MD5 hash with a length of 32 characters"); - } - if (stringValue.ToLowerInvariant() != stringValue) - { - return new ValidationResult("The checksum, if used, must be a MD5 hash in lower case"); - } - return ValidationResult.Success; + return new ValidationResult("The checksum, if used, must be a MD5 hash in lower case"); } + return ValidationResult.Success!; } } diff --git a/src/Altinn.Broker.API/Models/FileTransferOverviewExt.cs b/src/Altinn.Broker.API/Models/FileTransferOverviewExt.cs index d6cf3994..671e455e 100644 --- a/src/Altinn.Broker.API/Models/FileTransferOverviewExt.cs +++ b/src/Altinn.Broker.API/Models/FileTransferOverviewExt.cs @@ -2,85 +2,84 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +/// +/// Overview of a broker file +/// +public class FileTransferOverviewExt { + public Guid FileTransferId { get; set; } + + /// + /// The Altinn resource ID for the broker service + /// + [JsonPropertyName("resourceId")] + public string ResourceId { get; set; } = string.Empty; + + /// + /// The filename including extension + /// + public string FileName { get; set; } = string.Empty; + + /// + /// Used by senders and receivers to identify specific file using external identification methods. + /// + public string? SendersFileTransferReference { get; set; } = string.Empty; + + /// + /// MD5 checksum for file data. + /// + public string? Checksum { get; set; } = string.Empty; + + /// + /// File size in bytes + /// + public long FileTransferSize { get; set; } + + /// + /// Current file status + /// + public FileTransferStatusExt FileTransferStatus { get; set; } + + /// + /// Current file status text description + /// + public string FileTransferStatusText { get; set; } = string.Empty; + + /// + /// Timestamp for when the Current File Status was changed + /// + [JsonPropertyName("fileTransferStatusChanged")] + public DateTimeOffset FileTransferStatusChanged { get; set; } + + /// + /// Date/Time in UTC for when the file was created + /// + [JsonPropertyName("created")] + public DateTimeOffset Created { get; set; } + + /// + /// Date/Time in UTC for when the file will expire + /// + [JsonPropertyName("expirationTime")] + public DateTimeOffset ExpirationTime { get; set; } + + /// + /// Sender of the fileTransfer + /// + [JsonPropertyName("sender")] + public string Sender { get; set; } = string.Empty; + + /// + /// Recipients of the file + /// + [JsonPropertyName("recipients")] + public List Recipients { get; set; } = new List(); + /// - /// Overview of a broker file + /// Up to ten arbitrary key value pairs /// - public class FileTransferOverviewExt - { - public Guid FileTransferId { get; set; } - - /// - /// The Altinn resource ID for the broker service - /// - [JsonPropertyName("resourceId")] - public string ResourceId { get; set; } = string.Empty; - - /// - /// The filename including extension - /// - public string FileName { get; set; } = string.Empty; - - /// - /// Used by senders and receivers to identify specific file using external identification methods. - /// - public string SendersFileTransferReference { get; set; } = string.Empty; - - /// - /// MD5 checksum for file data. - /// - public string? Checksum { get; set; } = string.Empty; - - /// - /// File size in bytes - /// - public long FileTransferSize { get; set; } - - /// - /// Current file status - /// - public FileTransferStatusExt FileTransferStatus { get; set; } - - /// - /// Current file status text description - /// - public string FileTransferStatusText { get; set; } = string.Empty; - - /// - /// Timestamp for when the Current File Status was changed - /// - [JsonPropertyName("fileTransferStatusChanged")] - public DateTimeOffset FileTransferStatusChanged { get; set; } - - /// - /// Date/Time in UTC for when the file was created - /// - [JsonPropertyName("created")] - public DateTimeOffset Created { get; set; } - - /// - /// Date/Time in UTC for when the file will expire - /// - [JsonPropertyName("expirationTime")] - public DateTimeOffset ExpirationTime { get; set; } - - /// - /// Sender of the fileTransfer - /// - [JsonPropertyName("sender")] - public string Sender { get; set; } = string.Empty; - - /// - /// Recipients of the file - /// - [JsonPropertyName("recipients")] - public List Recipients { get; set; } = new List(); - - /// - /// Up to ten arbitrary key value pairs - /// - [JsonPropertyName("propertyList")] - public Dictionary PropertyList { get; set; } = new Dictionary(); - } + [JsonPropertyName("propertyList")] + public Dictionary PropertyList { get; set; } = new Dictionary(); } diff --git a/src/Altinn.Broker.API/Models/FileTransferStatusDetailsExt.cs b/src/Altinn.Broker.API/Models/FileTransferStatusDetailsExt.cs index 1491ea84..fd36ebe1 100644 --- a/src/Altinn.Broker.API/Models/FileTransferStatusDetailsExt.cs +++ b/src/Altinn.Broker.API/Models/FileTransferStatusDetailsExt.cs @@ -1,10 +1,9 @@ using Altinn.Broker.Models; -namespace Altinn.Broker.Core.Models +namespace Altinn.Broker.Core.Models; + +public class FileTransferStatusDetailsExt : FileTransferOverviewExt { - public class FileTransferStatusDetailsExt : FileTransferOverviewExt - { - public List FileTransferStatusHistory { get; set; } = new List(); - public List RecipientFileTransferStatusHistory { get; set; } = new List(); - } + public List FileTransferStatusHistory { get; set; } = new List(); + public List RecipientFileTransferStatusHistory { get; set; } = new List(); } diff --git a/src/Altinn.Broker.API/Models/FileTransferStatusEventExt.cs b/src/Altinn.Broker.API/Models/FileTransferStatusEventExt.cs index 43c5f49c..5244b52e 100644 --- a/src/Altinn.Broker.API/Models/FileTransferStatusEventExt.cs +++ b/src/Altinn.Broker.API/Models/FileTransferStatusEventExt.cs @@ -1,11 +1,10 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Core.Models +namespace Altinn.Broker.Core.Models; + +public class FileTransferStatusEventExt { - public class FileTransferStatusEventExt - { - public FileTransferStatusExt FileTransferStatus { get; set; } - public string FileTransferStatusText { get; set; } = string.Empty; - public DateTimeOffset FileTransferStatusChanged { get; set; } - } + public FileTransferStatusExt FileTransferStatus { get; set; } + public string FileTransferStatusText { get; set; } = string.Empty; + public DateTimeOffset FileTransferStatusChanged { get; set; } } diff --git a/src/Altinn.Broker.API/Models/LegacyFileInitializeExt.cs b/src/Altinn.Broker.API/Models/LegacyFileInitializeExt.cs index 5b611053..66b1c171 100644 --- a/src/Altinn.Broker.API/Models/LegacyFileInitializeExt.cs +++ b/src/Altinn.Broker.API/Models/LegacyFileInitializeExt.cs @@ -3,65 +3,64 @@ using Altinn.Broker.Helpers; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +/// +/// API input model for file initialization. +/// +public class LegacyFileInitalizeExt { /// - /// API input model for file initialization. + /// The filename including extension /// - public class LegacyFileInitalizeExt - { - /// - /// The filename including extension - /// - [JsonPropertyName("filename")] - [StringLength(255, MinimumLength = 1)] - [Required] - public string FileName { get; set; } = string.Empty; + [JsonPropertyName("filename")] + [StringLength(255, MinimumLength = 1)] + [Required] + public string FileName { get; set; } = string.Empty; - /// - /// The Altinn resource ID - /// - [JsonPropertyName("resourceId")] - [StringLength(255, MinimumLength = 1)] - [Required] - public string ResourceId { get; set; } = string.Empty; + /// + /// The Altinn resource ID + /// + [JsonPropertyName("resourceId")] + [StringLength(255, MinimumLength = 1)] + [Required] + public string ResourceId { get; set; } = string.Empty; - /// - /// Used by senders and receivers to identify specific file using external identification methods. - /// - [JsonPropertyName("sendersFileTransferReference")] - [StringLength(4096, MinimumLength = 1)] - public string SendersFileTransferReference { get; set; } = string.Empty; + /// + /// Used by senders and receivers to identify specific file using external identification methods. + /// + [JsonPropertyName("sendersFileTransferReference")] + [StringLength(4096, MinimumLength = 1)] + public string SendersFileTransferReference { get; set; } = string.Empty; - /// - /// The sender organization of the file - /// - [JsonPropertyName("sender")] - [RegularExpressionAttribute(@"^\d{4}:\d{9}$", ErrorMessage = "Organization numbers should be on the form countrycode:organizationnumber, for instance 0192:910753614")] - [Required] - public string Sender { get; set; } = string.Empty; + /// + /// The sender organization of the file + /// + [JsonPropertyName("sender")] + [RegularExpressionAttribute(@"^\d{4}:\d{9}$", ErrorMessage = "Organization numbers should be on the form countrycode:organizationnumber, for instance 0192:910753614")] + [Required] + public string Sender { get; set; } = string.Empty; - /// - /// The recipient organizations of the broker file. - /// - [JsonPropertyName("recipients")] - [ValidateElementsInList(typeof(RegularExpressionAttribute), @"^\d{4}:\d{9}$", ErrorMessage = "Each recipient should be on the form countrycode:organizationnumber, for instance 0192:910753614")] - [Required] - [MinLength(1, ErrorMessage = "One or more recipients are required")] - public List Recipients { get; set; } = new List(); + /// + /// The recipient organizations of the broker file. + /// + [JsonPropertyName("recipients")] + [ValidateElementsInList(typeof(RegularExpressionAttribute), @"^\d{4}:\d{9}$", ErrorMessage = "Each recipient should be on the form countrycode:organizationnumber, for instance 0192:910753614")] + [Required] + [MinLength(1, ErrorMessage = "One or more recipients are required")] + public List Recipients { get; set; } = new List(); - /// - /// User-defined properties related to the file - /// - [JsonPropertyName("propertyList")] - [MaxLength(10, ErrorMessage = "propertyList can contain at most 10 properties")] - public Dictionary PropertyList { get; set; } = new Dictionary(); + /// + /// User-defined properties related to the file + /// + [JsonPropertyName("propertyList")] + [MaxLength(10, ErrorMessage = "propertyList can contain at most 10 properties")] + public Dictionary PropertyList { get; set; } = new Dictionary(); - /// - /// MD5 checksum for file data. - /// - [JsonPropertyName("checksum")] - [MD5Checksum] - public string? Checksum { get; set; } = string.Empty; - } + /// + /// MD5 checksum for file data. + /// + [JsonPropertyName("checksum")] + [MD5Checksum] + public string? Checksum { get; set; } = string.Empty; } diff --git a/src/Altinn.Broker.API/Models/LegacyFileOverviewExt.cs b/src/Altinn.Broker.API/Models/LegacyFileOverviewExt.cs index 5f7f8127..14495d93 100644 --- a/src/Altinn.Broker.API/Models/LegacyFileOverviewExt.cs +++ b/src/Altinn.Broker.API/Models/LegacyFileOverviewExt.cs @@ -2,95 +2,94 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +/// +/// Overview of a broker file for use by the LegacyFileController +/// +public class LegacyFileOverviewExt { /// - /// Overview of a broker file for use by the LegacyFileController + /// The filename including extension + /// + [JsonPropertyName("fileId")] + public Guid FileId { get; set; } + + /// + /// Used by senders and receivers to identify specific file using external identification methods. + /// + [JsonPropertyName("filename")] + public string FileName { get; set; } = string.Empty; + + /// + /// The Altinn resource ID for the broker service + /// + [JsonPropertyName("resourceId")] + public string ResourceId { get; set; } = string.Empty; + + /// + /// Used by senders and receivers to identify specific file using external identification methods. + /// + [JsonPropertyName("sendersFileReference")] + public string? SendersFileReference { get; set; } = string.Empty; + + /// + /// MD5 checksum for file data. + /// + [JsonPropertyName("checksum")] + public string? Checksum { get; set; } = string.Empty; + + /// + /// File size in bytes + /// + [JsonPropertyName("filesize")] + public long FileSize { get; set; } + + /// + /// Current overall File Status + /// + [JsonPropertyName("fileStatus")] + public LegacyFileStatusExt FileStatus { get; set; } + + /// + /// Current overall File Status Text + /// + [JsonPropertyName("fileStatusText")] + public string FileStatusText { get; set; } = string.Empty; + + /// + /// Timestamp for when the Current File Status was changed + /// + [JsonPropertyName("fileStatusChanged")] + public DateTimeOffset FileStatusChanged { get; set; } + + /// + /// Date/Time in UTC for when the file was created + /// + [JsonPropertyName("created")] + public DateTimeOffset Created { get; set; } + + /// + /// Date/Time in UTC for when the file will expire + /// + [JsonPropertyName("expirationTime")] + public DateTimeOffset ExpirationTime { get; set; } + + /// + /// Sender of the file. + /// + [JsonPropertyName("sender")] + public string Sender { get; set; } = string.Empty; + + /// + /// Rcipients of the file + /// + [JsonPropertyName("recipients")] + public List Recipients { get; set; } = new List(); + + /// + /// Up to ten arbitrary key value pairs /// - public class LegacyFileOverviewExt - { - /// - /// The filename including extension - /// - [JsonPropertyName("fileId")] - public Guid FileId { get; set; } - - /// - /// Used by senders and receivers to identify specific file using external identification methods. - /// - [JsonPropertyName("filename")] - public string FileName { get; set; } = string.Empty; - - /// - /// The Altinn resource ID for the broker service - /// - [JsonPropertyName("resourceId")] - public string ResourceId { get; set; } = string.Empty; - - /// - /// Used by senders and receivers to identify specific file using external identification methods. - /// - [JsonPropertyName("sendersFileReference")] - public string SendersFileReference { get; set; } = string.Empty; - - /// - /// MD5 checksum for file data. - /// - [JsonPropertyName("checksum")] - public string? Checksum { get; set; } = string.Empty; - - /// - /// File size in bytes - /// - [JsonPropertyName("filesize")] - public long FileSize { get; set; } - - /// - /// Current overall File Status - /// - [JsonPropertyName("fileStatus")] - public LegacyFileStatusExt FileStatus { get; set; } - - /// - /// Current overall File Status Text - /// - [JsonPropertyName("fileStatusText")] - public string FileStatusText { get; set; } = string.Empty; - - /// - /// Timestamp for when the Current File Status was changed - /// - [JsonPropertyName("fileStatusChanged")] - public DateTimeOffset FileStatusChanged { get; set; } - - /// - /// Date/Time in UTC for when the file was created - /// - [JsonPropertyName("created")] - public DateTimeOffset Created { get; set; } - - /// - /// Date/Time in UTC for when the file will expire - /// - [JsonPropertyName("expirationTime")] - public DateTimeOffset ExpirationTime { get; set; } - - /// - /// Sender of the file. - /// - [JsonPropertyName("sender")] - public string Sender { get; set; } = string.Empty; - - /// - /// Rcipients of the file - /// - [JsonPropertyName("recipients")] - public List Recipients { get; set; } = new List(); - - /// - /// Up to ten arbitrary key value pairs - /// - [JsonPropertyName("propertyList")] - public Dictionary PropertyList { get; set; } = new Dictionary(); - } + [JsonPropertyName("propertyList")] + public Dictionary PropertyList { get; set; } = new Dictionary(); } diff --git a/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusDetailsExt.cs b/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusDetailsExt.cs index e917428b..3d445ed1 100644 --- a/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusDetailsExt.cs +++ b/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusDetailsExt.cs @@ -1,12 +1,11 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +public class LegacyRecipientFileStatusDetailsExt { - public class LegacyRecipientFileStatusDetailsExt - { - public string Recipient { get; set; } = string.Empty; - public LegacyRecipientFileStatusExt CurrentRecipientFileStatusCode { get; set; } - public string CurrentRecipientFileStatusText { get; set; } = string.Empty; - public DateTimeOffset CurrentRecipientFileStatusChanged { get; set; } - } + public string Recipient { get; set; } = string.Empty; + public LegacyRecipientFileStatusExt CurrentRecipientFileStatusCode { get; set; } + public string CurrentRecipientFileStatusText { get; set; } = string.Empty; + public DateTimeOffset CurrentRecipientFileStatusChanged { get; set; } } diff --git a/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusEventExt.cs b/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusEventExt.cs index db00e1b0..2d7519a5 100644 --- a/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusEventExt.cs +++ b/src/Altinn.Broker.API/Models/Recipient/LegacyRecipientFileStatusEventExt.cs @@ -1,12 +1,11 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +public class LegacyRecipientFileStatusEventExt { - public class LegacyRecipientFileStatusEventExt - { - public string Recipient { get; set; } = string.Empty; - public LegacyRecipientFileStatusExt RecipientFileStatusCode { get; set; } - public string RecipientFileStatusText { get; set; } = string.Empty; - public DateTimeOffset RecipientFileStatusChanged { get; set; } - } + public string Recipient { get; set; } = string.Empty; + public LegacyRecipientFileStatusExt RecipientFileStatusCode { get; set; } + public string RecipientFileStatusText { get; set; } = string.Empty; + public DateTimeOffset RecipientFileStatusChanged { get; set; } } diff --git a/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusDetailsExt.cs b/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusDetailsExt.cs index 1b4d4f47..71430b61 100644 --- a/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusDetailsExt.cs +++ b/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusDetailsExt.cs @@ -1,12 +1,11 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +public class RecipientFileTransferStatusDetailsExt { - public class RecipientFileTransferStatusDetailsExt - { - public string Recipient { get; set; } = string.Empty; - public RecipientFileTransferStatusExt CurrentRecipientFileTransferStatusCode { get; set; } - public string CurrentRecipientFileTransferStatusText { get; set; } = string.Empty; - public DateTimeOffset CurrentRecipientFileTransferStatusChanged { get; set; } - } + public string Recipient { get; set; } = string.Empty; + public RecipientFileTransferStatusExt CurrentRecipientFileTransferStatusCode { get; set; } + public string CurrentRecipientFileTransferStatusText { get; set; } = string.Empty; + public DateTimeOffset CurrentRecipientFileTransferStatusChanged { get; set; } } diff --git a/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusEventExt.cs b/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusEventExt.cs index dbef7ff3..89564d64 100644 --- a/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusEventExt.cs +++ b/src/Altinn.Broker.API/Models/Recipient/RecipientFileStatusEventExt.cs @@ -1,12 +1,11 @@ using Altinn.Broker.Enums; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +public class RecipientFileTransferStatusEventExt { - public class RecipientFileTransferStatusEventExt - { - public string Recipient { get; set; } = string.Empty; - public RecipientFileTransferStatusExt RecipientFileTransferStatusCode { get; set; } - public string RecipientFileTransferStatusText { get; set; } = string.Empty; - public DateTimeOffset RecipientFileTransferStatusChanged { get; set; } - } + public string Recipient { get; set; } = string.Empty; + public RecipientFileTransferStatusExt RecipientFileTransferStatusCode { get; set; } + public string RecipientFileTransferStatusText { get; set; } = string.Empty; + public DateTimeOffset RecipientFileTransferStatusChanged { get; set; } } diff --git a/src/Altinn.Broker.API/Models/ResourceExt.cs b/src/Altinn.Broker.API/Models/ResourceExt.cs index 2fb8077c..a7a5497f 100644 --- a/src/Altinn.Broker.API/Models/ResourceExt.cs +++ b/src/Altinn.Broker.API/Models/ResourceExt.cs @@ -1,23 +1,22 @@ using System.Text.Json.Serialization; -namespace Altinn.Broker.Models +namespace Altinn.Broker.Models; + +/// +/// API input model for file initialization. +/// +public class ResourceExt { + /// - /// API input model for file initialization. + /// The max upload size for the resource in bytes /// - public class ResourceExt - { - - /// - /// The max upload size for the resource in bytes - /// - [JsonPropertyName("maxFileTransferSize")] - public long? MaxFileTransferSize { get; set; } + [JsonPropertyName("maxFileTransferSize")] + public long? MaxFileTransferSize { get; set; } - /// - /// The time before a file transfer expires (ISO8601 Duration format) - /// - [JsonPropertyName("fileTransferTimeToLive")] - public string? FileTransferTimeToLive { get; set; } - } + /// + /// The time before a file transfer expires (ISO8601 Duration format) + /// + [JsonPropertyName("fileTransferTimeToLive")] + public string? FileTransferTimeToLive { get; set; } } diff --git a/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerInitializeExt.cs b/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerInitializeExt.cs index 4f46f362..628e599f 100644 --- a/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerInitializeExt.cs +++ b/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerInitializeExt.cs @@ -2,6 +2,6 @@ public class ServiceOwnerInitializeExt { - public string Name { get; set; } - public string DeletionTime { get; set; } // ISO8601 Duration + public required string Name { get; set; } + public required string DeletionTime { get; set; } // ISO8601 Duration } diff --git a/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerOverviewExt.cs b/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerOverviewExt.cs index c12f484d..906b1c24 100644 --- a/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerOverviewExt.cs +++ b/src/Altinn.Broker.API/Models/ServiceOwner/ServiceOwnerOverviewExt.cs @@ -4,7 +4,7 @@ public class ServiceOwnerOverviewExt { public ServiceOwnerOverviewExt() { } - public string Name { get; set; } + public required string Name { get; set; } - public DeploymentStatusExt DeploymentStatus { get; set; } + public required DeploymentStatusExt DeploymentStatus { get; set; } } diff --git a/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandHandler.cs b/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandHandler.cs index 97fedaa1..fd3b3452 100644 --- a/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandHandler.cs +++ b/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandHandler.cs @@ -59,7 +59,7 @@ private async Task> UpdateMaxFileTransferSize(ResourceEntity { return Errors.MaxUploadSizeCannotBeZero; } - long globalMaxFileTransferSize = long.Parse(Environment.GetEnvironmentVariable("MAX_FILE_UPLOAD_SIZE")); + long globalMaxFileTransferSize = long.Parse(Environment.GetEnvironmentVariable("MAX_FILE_UPLOAD_SIZE") ?? int.MaxValue.ToString()); if (maxFileTransferSize > globalMaxFileTransferSize) { return Errors.MaxUploadSizeOverGlobal; diff --git a/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandRequest.cs b/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandRequest.cs index 687d32d0..058dab79 100644 --- a/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandRequest.cs +++ b/src/Altinn.Broker.Application/ConfigureResourceCommand/ConfigureResourceCommandRequest.cs @@ -3,7 +3,7 @@ namespace Altinn.Broker.Application.ConfigureResourceCommand; public class ConfigureResourceCommandRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public required string ResourceId { get; set; } public long? MaxFileTransferSize { get; set; } public string? FileTransferTimeToLive { get; set; } diff --git a/src/Altinn.Broker.Application/ConfirmDownloadCommand/ConfirmDownloadCommandRequest.cs b/src/Altinn.Broker.Application/ConfirmDownloadCommand/ConfirmDownloadCommandRequest.cs index 433f83f8..922e74d7 100644 --- a/src/Altinn.Broker.Application/ConfirmDownloadCommand/ConfirmDownloadCommandRequest.cs +++ b/src/Altinn.Broker.Application/ConfirmDownloadCommand/ConfirmDownloadCommandRequest.cs @@ -4,7 +4,7 @@ namespace Altinn.Broker.Application.ConfirmDownloadCommand; public class ConfirmDownloadCommandRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public Guid FileTransferId { get; set; } public bool IsLegacy { get; set; } } diff --git a/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryRequest.cs b/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryRequest.cs index 6b0e17cf..9e603052 100644 --- a/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryRequest.cs +++ b/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryRequest.cs @@ -4,7 +4,7 @@ namespace Altinn.Broker.Application.DownloadFileQuery; public class DownloadFileQueryRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public Guid FileTransferId { get; set; } public bool IsLegacy { get; set; } } diff --git a/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryResponse.cs b/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryResponse.cs index bebf0e90..9d94f8a6 100644 --- a/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryResponse.cs +++ b/src/Altinn.Broker.Application/DownloadFileQuery/DownloadFileQueryResponse.cs @@ -1,6 +1,6 @@ namespace Altinn.Broker.Application.DownloadFileQuery; public class DownloadFileQueryResponse { - public string FileName { get; set; } - public Stream DownloadStream { get; set; } + public required string FileName { get; set; } + public required Stream DownloadStream { get; set; } } diff --git a/src/Altinn.Broker.Application/Errors.cs b/src/Altinn.Broker.Application/Errors.cs index 1460c3d2..7ddccd17 100644 --- a/src/Altinn.Broker.Application/Errors.cs +++ b/src/Altinn.Broker.Application/Errors.cs @@ -25,4 +25,5 @@ public static class Errors public static Error MaxUploadSizeOverGlobal = new Error(17, "Max file transfer size cannot be set higher than the global max file transfer size", HttpStatusCode.BadRequest); public static Error InvalidTimeToLiveFormat = new Error(18, "Invalid file transfer time to live format. Should follow ISO8601 standard for duration. Example: 'P30D' for 30 days.", HttpStatusCode.BadRequest); public static Error TimeToLiveCannotExceed365Days = new Error(19, "Time to live cannot exceed 365 days", HttpStatusCode.BadRequest); + public static Error FileSizeTooBig = new Error(20, "File size exceeds maximum", HttpStatusCode.BadRequest); } diff --git a/src/Altinn.Broker.Application/ExpireFileTransferCommand/ExpireFileTransferCommandHandler.cs b/src/Altinn.Broker.Application/ExpireFileTransferCommand/ExpireFileTransferCommandHandler.cs index f82dc2dc..2da59eba 100644 --- a/src/Altinn.Broker.Application/ExpireFileTransferCommand/ExpireFileTransferCommandHandler.cs +++ b/src/Altinn.Broker.Application/ExpireFileTransferCommand/ExpireFileTransferCommandHandler.cs @@ -36,9 +36,9 @@ public ExpireFileTransferCommandHandler(IFileTransferRepository fileTransferRepo public async Task> Process(ExpireFileTransferCommandRequest request, CancellationToken cancellationToken) { _logger.LogInformation("Deleting file transfer with id {fileTransferId}", request.FileTransferId.ToString()); - var fileTransfer = await GetFileTransfer(request.FileTransferId, cancellationToken); + var fileTransfer = await GetFileTransferAsync(request.FileTransferId, cancellationToken); var resource = await GetResource(fileTransfer.ResourceId, cancellationToken); - var serviceOwner = await GetServiceOwner(resource.ServiceOwnerId); + var serviceOwner = await GetServiceOwnerAsync(resource.ServiceOwnerId); if (fileTransfer.FileTransferStatusEntity.Status == Core.Domain.Enums.FileTransferStatus.Purged) { @@ -67,27 +67,27 @@ public async Task> Process(ExpireFileTransferCommandRequest r } [AutomaticRetry(Attempts = 0)] - private Task GetFileTransfer(Guid fileTransferId, CancellationToken cancellationToken) + private async Task GetFileTransferAsync(Guid fileTransferId, CancellationToken cancellationToken) { - var fileTransfer = _fileTransferRepository.GetFileTransfer(fileTransferId, cancellationToken); + var fileTransfer = await _fileTransferRepository.GetFileTransfer(fileTransferId, cancellationToken); if (fileTransfer is null) { throw new Exception("FileTransfer not found"); } return fileTransfer; } - private Task GetServiceOwner(string serviceOwnerId) + private async Task GetServiceOwnerAsync(string serviceOwnerId) { - var serviceOwner = _serviceOwnerRepository.GetServiceOwner(serviceOwnerId); + var serviceOwner = await _serviceOwnerRepository.GetServiceOwner(serviceOwnerId); if (serviceOwner is null) { throw new Exception("ServiceOwner not found"); } return serviceOwner; } - private Task GetResource(string resourceId, CancellationToken cancellationToken) + private async Task GetResource(string resourceId, CancellationToken cancellationToken) { - var resource = _resourceRepository.GetResource(resourceId, cancellationToken); + var resource = await _resourceRepository.GetResource(resourceId, cancellationToken); if (resource is null) { throw new Exception("Resource not found"); diff --git a/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryRequest.cs b/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryRequest.cs index 52759677..7239c5cf 100644 --- a/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryRequest.cs +++ b/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryRequest.cs @@ -5,6 +5,6 @@ namespace Altinn.Broker.Application.GetFileTransferDetailsQuery; public class GetFileTransferDetailsQueryRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public Guid FileTransferId { get; set; } } diff --git a/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryResponse.cs b/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryResponse.cs index 227ca1d9..767bb2b0 100644 --- a/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryResponse.cs +++ b/src/Altinn.Broker.Application/GetFileTransferDetailsQuery/GetFileTransferDetailsQueryResponse.cs @@ -4,7 +4,7 @@ namespace Altinn.Broker.Application.GetFileTransferDetailsQuery; public class GetFileTransferDetailsQueryResponse { - public List ActorEvents { get; set; } - public List FileTransferEvents { get; set; } - public FileTransferEntity FileTransfer { get; internal set; } + public required List ActorEvents { get; set; } + public required List FileTransferEvents { get; set; } + public required FileTransferEntity FileTransfer { get; set; } } diff --git a/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryRequest.cs b/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryRequest.cs index edb0e447..10adc2c2 100644 --- a/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryRequest.cs +++ b/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryRequest.cs @@ -4,7 +4,7 @@ namespace Altinn.Broker.Application.GetFileTransferOverviewQuery; public class GetFileTransferOverviewQueryRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public Guid FileTransferId { get; set; } public bool IsLegacy { get; set; } } diff --git a/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryResponse.cs b/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryResponse.cs index 559a123b..52b4f795 100644 --- a/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryResponse.cs +++ b/src/Altinn.Broker.Application/GetFileTransferOverviewQuery/GetFileTransferOverviewQueryResponse.cs @@ -4,5 +4,5 @@ namespace Altinn.Broker.Application.GetFileTransferOverviewQuery; public class GetFileTransferOverviewQueryResponse { - public FileTransferEntity FileTransfer { get; set; } + public required FileTransferEntity FileTransfer { get; set; } } diff --git a/src/Altinn.Broker.Application/GetFileTransfersQuery/GetFileTransfersQueryRequest.cs b/src/Altinn.Broker.Application/GetFileTransfersQuery/GetFileTransfersQueryRequest.cs index 6db795fd..5951c162 100644 --- a/src/Altinn.Broker.Application/GetFileTransfersQuery/GetFileTransfersQueryRequest.cs +++ b/src/Altinn.Broker.Application/GetFileTransfersQuery/GetFileTransfersQueryRequest.cs @@ -5,8 +5,8 @@ namespace Altinn.Broker.Application.GetFileTransfersQuery; public class GetFileTransfersQueryRequest { - public CallerIdentity Token { get; set; } - public string ResourceId { get; set; } + public required CallerIdentity Token { get; set; } + public required string ResourceId { get; set; } public FileTransferStatus? Status { get; set; } public ActorFileTransferStatus? RecipientStatus { get; set; } public DateTimeOffset? From { get; set; } diff --git a/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryHandler.cs b/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryHandler.cs index 2f55c742..542cab75 100644 --- a/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryHandler.cs +++ b/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryHandler.cs @@ -30,8 +30,11 @@ private async Task> GetActors(string[] recipients, Cancellatio List actors = new(); foreach (string recipient in recipients) { - ActorEntity entity = await _actorRepository.GetActorAsync(recipient, cancellationToken); - actors.Add(entity); + var entity = await _actorRepository.GetActorAsync(recipient, cancellationToken); + if (entity is not null) + { + actors.Add(entity); + } } return actors; diff --git a/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryRequest.cs b/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryRequest.cs index debd3adc..b13f6db5 100644 --- a/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryRequest.cs +++ b/src/Altinn.Broker.Application/GetFileTransfersQuery/LegacyGetFilesQueryRequest.cs @@ -5,7 +5,7 @@ namespace Altinn.Broker.Application.GetFileTransfersQuery; public class LegacyGetFilesQueryRequest { - public CallerIdentity Token { get; set; } + public required CallerIdentity Token { get; set; } public string? ResourceId { get; set; } public FileTransferStatus? FileTransferStatus { get; set; } public ActorFileTransferStatus? RecipientFileTransferStatus { get; set; } diff --git a/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandHandler.cs b/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandHandler.cs index bc8db20f..b8774126 100644 --- a/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandHandler.cs +++ b/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandHandler.cs @@ -1,6 +1,4 @@ -using System; - -using Altinn.Broker.Application.ExpireFileTransferCommand; +using Altinn.Broker.Application.ExpireFileTransferCommand; using Altinn.Broker.Core.Application; using Altinn.Broker.Core.Domain.Enums; using Altinn.Broker.Core.Repositories; @@ -78,7 +76,8 @@ public async Task> Process(InitializeFileTransferCommandReque } catch (Exception ex) { - _logger.LogError("Failed when adding recipient initialized events."); + _logger.LogError("Failed when adding recipient initialized events: {message}\n{stackTrace}", ex.Message, ex.StackTrace); + throw; } var jobId = _backgroundJobClient.Schedule((ExpireFileTransferCommandHandler) => ExpireFileTransferCommandHandler.Process(new ExpireFileTransferCommandRequest { diff --git a/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandRequest.cs b/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandRequest.cs index 5c5255c2..baf065e2 100644 --- a/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandRequest.cs +++ b/src/Altinn.Broker.Application/InitializeFileTransferCommand/InitializeFileTransferCommandRequest.cs @@ -4,13 +4,13 @@ namespace Altinn.Broker.Application.InitializeFileTransferCommand; public class InitializeFileTransferCommandRequest { - public CallerIdentity Token { get; set; } - public string ResourceId { get; set; } - public string FileName { get; set; } - public string SendersFileTransferReference { get; set; } - public string SenderExternalId { get; set; } - public List RecipientExternalIds { get; set; } - public Dictionary PropertyList { get; set; } + public required CallerIdentity Token { get; set; } + public required string ResourceId { get; set; } + public required string FileName { get; set; } + public required string SendersFileTransferReference { get; set; } + public required string SenderExternalId { get; set; } + public required List RecipientExternalIds { get; set; } + public required Dictionary PropertyList { get; set; } public string? Checksum { get; set; } public bool IsLegacy { get; set; } } diff --git a/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandHandler.cs b/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandHandler.cs index 9da3bb68..33ee1c5e 100644 --- a/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandHandler.cs +++ b/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandHandler.cs @@ -67,6 +67,11 @@ public async Task> Process(UploadFileCommandRequest request, { return Errors.ServiceOwnerNotConfigured; }; + var maxUploadSize = resource?.MaxFileTransferSize ?? long.Parse(Environment.GetEnvironmentVariable("MAX_FILE_UPLOAD_SIZE") ?? int.MaxValue.ToString()); + if (request.ContentLength > maxUploadSize) + { + return Errors.FileSizeTooBig; + } await _fileTransferStatusRepository.InsertFileTransferStatus(request.FileTransferId, FileTransferStatus.UploadStarted, cancellationToken: cancellationToken); try diff --git a/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandRequest.cs b/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandRequest.cs index 968aae7e..ba00a78b 100644 --- a/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandRequest.cs +++ b/src/Altinn.Broker.Application/UploadFileCommand/UploadFileCommandRequest.cs @@ -6,7 +6,8 @@ namespace Altinn.Broker.Application.UploadFileCommand; public class UploadFileCommandRequest { public Guid FileTransferId { get; set; } - public CallerIdentity Token { get; set; } - public Stream UploadStream { get; set; } + public required CallerIdentity Token { get; set; } + public required Stream UploadStream { get; set; } public bool IsLegacy { get; set; } + public long ContentLength { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/ActorEntity.cs b/src/Altinn.Broker.Core/Domain/ActorEntity.cs index 39ab1f72..54b59e4f 100644 --- a/src/Altinn.Broker.Core/Domain/ActorEntity.cs +++ b/src/Altinn.Broker.Core/Domain/ActorEntity.cs @@ -3,5 +3,5 @@ namespace Altinn.Broker.Core.Domain; public class ActorEntity { public long ActorId { get; set; } - public string ActorExternalId { get; set; } + public required string ActorExternalId { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/ActorFileTransferStatusEntity.cs b/src/Altinn.Broker.Core/Domain/ActorFileTransferStatusEntity.cs index a0a48f0f..6ec40163 100644 --- a/src/Altinn.Broker.Core/Domain/ActorFileTransferStatusEntity.cs +++ b/src/Altinn.Broker.Core/Domain/ActorFileTransferStatusEntity.cs @@ -3,7 +3,7 @@ namespace Altinn.Broker.Core.Domain; public class ActorFileTransferStatusEntity { public Guid FileTransferId { get; set; } - public ActorEntity Actor { get; set; } + public required ActorEntity Actor { get; set; } public Enums.ActorFileTransferStatus Status { get; set; } public DateTimeOffset Date { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/FileTransferEntity.cs b/src/Altinn.Broker.Core/Domain/FileTransferEntity.cs index afe62f0d..1af67be7 100644 --- a/src/Altinn.Broker.Core/Domain/FileTransferEntity.cs +++ b/src/Altinn.Broker.Core/Domain/FileTransferEntity.cs @@ -2,19 +2,19 @@ namespace Altinn.Broker.Core.Domain; public class FileTransferEntity { - public Guid FileTransferId { get; set; } - public string ResourceId { get; set; } - public ActorEntity Sender { get; set; } // Joined in - public string SendersFileTransferReference { get; set; } - public FileTransferStatusEntity FileTransferStatusEntity { get; set; } // Joined in + public required Guid FileTransferId { get; set; } + public required string ResourceId { get; set; } + public required ActorEntity Sender { get; set; } // Joined in + public string? SendersFileTransferReference { get; set; } + public required FileTransferStatusEntity FileTransferStatusEntity { get; set; } // Joined in public DateTimeOffset FileTransferStatusChanged { get; set; } - public DateTimeOffset Created { get; set; } - public DateTimeOffset ExpirationTime { get; set; } - public List RecipientCurrentStatuses { get; set; } // Joined in + public required DateTimeOffset Created { get; set; } + public required DateTimeOffset ExpirationTime { get; set; } + public required List RecipientCurrentStatuses { get; set; } // Joined in public string? FileLocation { get; set; } public string? HangfireJobId { get; set; } - public string FileName { get; set; } - public long FileTransferSize { get; set; } + public required string FileName { get; set; } + public long FileTransferSize { get; set; } = 0; public string? Checksum { get; set; } - public Dictionary PropertyList { get; set; } + public Dictionary PropertyList { get; set; } = new Dictionary(); } diff --git a/src/Altinn.Broker.Core/Domain/FileTransferSearchEntity.cs b/src/Altinn.Broker.Core/Domain/FileTransferSearchEntity.cs index 4b2da6c5..894c72d5 100644 --- a/src/Altinn.Broker.Core/Domain/FileTransferSearchEntity.cs +++ b/src/Altinn.Broker.Core/Domain/FileTransferSearchEntity.cs @@ -4,10 +4,10 @@ namespace Altinn.Broker.Core.Domain; public class FileTransferSearchEntity { - public ActorEntity Actor { get; set; } + public required ActorEntity Actor { get; set; } public FileTransferStatus? Status { get; set; } public ActorFileTransferStatus? RecipientStatus { get; set; } public DateTimeOffset? From { get; set; } public DateTimeOffset? To { get; set; } - public string ResourceId { get; set; } + public required string ResourceId { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/PartyEntity.cs b/src/Altinn.Broker.Core/Domain/PartyEntity.cs index b8495154..4691459f 100644 --- a/src/Altinn.Broker.Core/Domain/PartyEntity.cs +++ b/src/Altinn.Broker.Core/Domain/PartyEntity.cs @@ -3,6 +3,6 @@ namespace Altinn.Broker.Core.Domain; public class PartyEntity { public DateTimeOffset Created { get; set; } - public string OrganizationNumber { get; set; } - public string PartyId { get; set; } + public required string OrganizationNumber { get; set; } + public required string PartyId { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/ResourceEntity.cs b/src/Altinn.Broker.Core/Domain/ResourceEntity.cs index d94d479b..0e9a47d9 100644 --- a/src/Altinn.Broker.Core/Domain/ResourceEntity.cs +++ b/src/Altinn.Broker.Core/Domain/ResourceEntity.cs @@ -2,10 +2,10 @@ public class ResourceEntity { - public string Id { get; set; } + public required string Id { get; set; } public DateTimeOffset? Created { get; set; } public string? OrganizationNumber { get; set; } - public string ServiceOwnerId { get; set; } + public required string ServiceOwnerId { get; set; } public long? MaxFileTransferSize { get; set; } public TimeSpan? FileTransferTimeToLive { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/ServiceOwnerEntity.cs b/src/Altinn.Broker.Core/Domain/ServiceOwnerEntity.cs index c01852e2..09ca8df3 100644 --- a/src/Altinn.Broker.Core/Domain/ServiceOwnerEntity.cs +++ b/src/Altinn.Broker.Core/Domain/ServiceOwnerEntity.cs @@ -2,8 +2,7 @@ public class ServiceOwnerEntity { - public string Id { get; set; } - public string Name { get; set; } + public required string Id { get; set; } + public required string Name { get; set; } public StorageProviderEntity? StorageProvider { get; set; } - public TimeSpan FileTransferTimeToLive { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/StorageProviderEntity.cs b/src/Altinn.Broker.Core/Domain/StorageProviderEntity.cs index 3dab10d2..6764c6ed 100644 --- a/src/Altinn.Broker.Core/Domain/StorageProviderEntity.cs +++ b/src/Altinn.Broker.Core/Domain/StorageProviderEntity.cs @@ -5,5 +5,5 @@ public StorageProviderType Type { get; set; } - public string ResourceName { get; set; } + public required string ResourceName { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/StorageReferenceEntity.cs b/src/Altinn.Broker.Core/Domain/StorageReferenceEntity.cs deleted file mode 100644 index 08b6288e..00000000 --- a/src/Altinn.Broker.Core/Domain/StorageReferenceEntity.cs +++ /dev/null @@ -1,6 +0,0 @@ -public class StorageReferenceEntity -{ - public long Id { get; set; } - public StorageProviderEntity StorageProvider { get; set; } - public string FileLocation { get; set; } -} diff --git a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/EventMessage.cs b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/EventMessage.cs index 4e7932db..b3cf081b 100644 --- a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/EventMessage.cs +++ b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/EventMessage.cs @@ -1,14 +1,13 @@ -namespace Altinn.Broker.Core.Domain +namespace Altinn.Broker.Core.Domain; + +public class EventMessage { - public class EventMessage - { - public string Id { get; set; } = string.Empty; - public string Subject { get; set; } = string.Empty; - public ScanResultData Data { get; set; } = new ScanResultData(); - public string EventType { get; set; } = string.Empty; - public string DataVersion { get; set; } = string.Empty; - public string MetadataVersion { get; set; } = string.Empty; - public DateTime EventTime { get; set; } - public string Topic { get; set; } = string.Empty; - } + public string Id { get; set; } = string.Empty; + public string Subject { get; set; } = string.Empty; + public ScanResultData Data { get; set; } = new ScanResultData(); + public string EventType { get; set; } = string.Empty; + public string DataVersion { get; set; } = string.Empty; + public string MetadataVersion { get; set; } = string.Empty; + public DateTime EventTime { get; set; } + public string Topic { get; set; } = string.Empty; } diff --git a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultData.cs b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultData.cs index da95eb06..02637fd3 100644 --- a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultData.cs +++ b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultData.cs @@ -1,12 +1,11 @@ -namespace Altinn.Broker.Core.Domain +namespace Altinn.Broker.Core.Domain; + +public class ScanResultData { - public class ScanResultData - { - public Guid CorrelationId { get; set; } - public string BlobUri { get; set; } = string.Empty; - public string ETag { get; set; } = string.Empty; - public DateTime ScanFinishedTimeUtc { get; set; } - public string ScanResultType { get; set; } = string.Empty; - public ScanResultDetails? ScanResultDetails { get; set; } - } + public Guid CorrelationId { get; set; } + public string BlobUri { get; set; } = string.Empty; + public string ETag { get; set; } = string.Empty; + public DateTime ScanFinishedTimeUtc { get; set; } + public string ScanResultType { get; set; } = string.Empty; + public ScanResultDetails? ScanResultDetails { get; set; } } diff --git a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultDetails.cs b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultDetails.cs index 5688d810..91e7def6 100644 --- a/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultDetails.cs +++ b/src/Altinn.Broker.Core/Domain/Webhooks/MalwareScan/ScanResultDetails.cs @@ -1,9 +1,8 @@ -namespace Altinn.Broker.Core.Domain +namespace Altinn.Broker.Core.Domain; + +public class ScanResultDetails { - public class ScanResultDetails - { - public List MalwareNamesFound { get; set; } = new List(); - public string Sha256 { get; set; } = string.Empty; - public string NotScannedReason { get; set; } = string.Empty; - } + public List MalwareNamesFound { get; set; } = new List(); + public string Sha256 { get; set; } = string.Empty; + public string NotScannedReason { get; set; } = string.Empty; } diff --git a/src/Altinn.Broker.Core/Options/AltinnOptions.cs b/src/Altinn.Broker.Core/Options/AltinnOptions.cs index 08412183..5035545f 100644 --- a/src/Altinn.Broker.Core/Options/AltinnOptions.cs +++ b/src/Altinn.Broker.Core/Options/AltinnOptions.cs @@ -2,8 +2,7 @@ public class AltinnOptions { - public string OpenIdWellKnown { get; set; } - public string LegacyOpenIdWellKnown { get; set; } - public string PlatformGatewayUrl { get; set; } - public string PlatformSubscriptionKey { get; set; } + public string OpenIdWellKnown { get; set; } = string.Empty; + public string PlatformGatewayUrl { get; set; } = string.Empty; + public string PlatformSubscriptionKey { get; set; } = string.Empty; } diff --git a/src/Altinn.Broker.Core/Repositories/IIdempotencyEventRepository.cs b/src/Altinn.Broker.Core/Repositories/IIdempotencyEventRepository.cs index ece1e1d3..1c3465c9 100644 --- a/src/Altinn.Broker.Core/Repositories/IIdempotencyEventRepository.cs +++ b/src/Altinn.Broker.Core/Repositories/IIdempotencyEventRepository.cs @@ -1,9 +1,8 @@ -namespace Altinn.Broker.Core.Repositories +namespace Altinn.Broker.Core.Repositories; + +public interface IIdempotencyEventRepository { - public interface IIdempotencyEventRepository - { - Task AddIdempotencyEventAsync(string id, CancellationToken cancellationToken); - Task DeleteIdempotencyEventAsync(string id, CancellationToken cancellationToken); - Task DeleteOldIdempotencyEvents(); - } + Task AddIdempotencyEventAsync(string id, CancellationToken cancellationToken); + Task DeleteIdempotencyEventAsync(string id, CancellationToken cancellationToken); + Task DeleteOldIdempotencyEvents(); } diff --git a/src/Altinn.Broker.Core/Services/IAltinnRegisterService.cs b/src/Altinn.Broker.Core/Services/IAltinnRegisterService.cs index 0d820c5a..f341c2e4 100644 --- a/src/Altinn.Broker.Core/Services/IAltinnRegisterService.cs +++ b/src/Altinn.Broker.Core/Services/IAltinnRegisterService.cs @@ -1,5 +1,5 @@ namespace Altinn.Broker.Core.Services; public interface IAltinnRegisterService { - Task LookUpOrganizationId(string organizationId, CancellationToken cancellationToken); + Task LookUpOrganizationId(string organizationId, CancellationToken cancellationToken); } diff --git a/src/Altinn.Broker.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs b/src/Altinn.Broker.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs index 5a3d28df..7e2f5009 100644 --- a/src/Altinn.Broker.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs +++ b/src/Altinn.Broker.Integrations/Altinn/Authorization/AltinnAuthorizationService.cs @@ -58,7 +58,7 @@ public async Task CheckUserAccess(string resourceId, string userId, List(cancellationToken: cancellationToken); - if (user is null) + if (responseContent is null) { _logger.LogError("Unexpected null or invalid json response from Authorization."); return false; diff --git a/src/Altinn.Broker.Integrations/Altinn/ResourceRegistry/GetResourceResponse.cs b/src/Altinn.Broker.Integrations/Altinn/ResourceRegistry/GetResourceResponse.cs index 982bcd83..eb765683 100644 --- a/src/Altinn.Broker.Integrations/Altinn/ResourceRegistry/GetResourceResponse.cs +++ b/src/Altinn.Broker.Integrations/Altinn/ResourceRegistry/GetResourceResponse.cs @@ -5,31 +5,31 @@ namespace Altinn.Broker.Integrations.Altinn.ResourceRegistry; internal class GetResourceResponse { [JsonPropertyName("identifier")] - public string Identifier { get; set; } + public required string Identifier { get; set; } [JsonPropertyName("title")] - public Title Title { get; set; } + public required Dictionary Title { get; set; } [JsonPropertyName("description")] - public Description Description { get; set; } + public required Dictionary Description { get; set; } [JsonPropertyName("rightDescription")] - public RightDescription RightDescription { get; set; } + public Dictionary? RightDescription { get; set; } [JsonPropertyName("homepage")] - public string Homepage { get; set; } + public string? Homepage { get; set; } [JsonPropertyName("status")] - public string Status { get; set; } + public string? Status { get; set; } [JsonPropertyName("contactPoints")] - public List ContactPoints { get; set; } + public List? ContactPoints { get; set; } [JsonPropertyName("isPartOf")] - public string IsPartOf { get; set; } + public string? IsPartOf { get; set; } [JsonPropertyName("resourceReferences")] - public List ResourceReferences { get; set; } + public List? ResourceReferences { get; set; } [JsonPropertyName("delegable")] public bool? Delegable { get; set; } @@ -38,10 +38,10 @@ internal class GetResourceResponse public bool? Visible { get; set; } [JsonPropertyName("hasCompetentAuthority")] - public HasCompetentAuthority HasCompetentAuthority { get; set; } + public required HasCompetentAuthority HasCompetentAuthority { get; set; } [JsonPropertyName("keywords")] - public List Keywords { get; set; } + public List? Keywords { get; set; } [JsonPropertyName("limitedByRRR")] public bool? LimitedByRRR { get; set; } @@ -53,67 +53,23 @@ internal class GetResourceResponse public bool? EnterpriseUserEnabled { get; set; } [JsonPropertyName("resourceType")] - public string ResourceType { get; set; } -} -internal class Description -{ - [JsonPropertyName("en")] - public string En { get; set; } - - [JsonPropertyName("nb-no")] - public string NbNo { get; set; } - - [JsonPropertyName("nn-no")] - public string NnNo { get; set; } + public required string ResourceType { get; set; } } internal class HasCompetentAuthority { [JsonPropertyName("organization")] - public string Organization { get; set; } + public required string Organization { get; set; } [JsonPropertyName("orgcode")] - public string Orgcode { get; set; } + public required string Orgcode { get; set; } [JsonPropertyName("name")] - public Name Name { get; set; } + public Dictionary? Name { get; set; } } -internal class Name +public class Keyword { - [JsonPropertyName("en")] - public string En { get; set; } - - [JsonPropertyName("nb-no")] - public string NbNo { get; set; } - - [JsonPropertyName("nn-no")] - public string NnNo { get; set; } + public required string Word { get; set; } + public required string Language { get; set; } } - -internal class RightDescription -{ - [JsonPropertyName("en")] - public string En { get; set; } - - [JsonPropertyName("nb-no")] - public string NbNo { get; set; } - - [JsonPropertyName("nn-no")] - public string NnNo { get; set; } -} - - -internal class Title -{ - [JsonPropertyName("en")] - public string En { get; set; } - - [JsonPropertyName("nb-no")] - public string NbNo { get; set; } - - [JsonPropertyName("nn-no")] - public string NnNo { get; set; } -} - - diff --git a/src/Altinn.Broker.Integrations/Azure/AzureResourceManagerOptions.cs b/src/Altinn.Broker.Integrations/Azure/AzureResourceManagerOptions.cs index 60d65db3..46ed0fab 100644 --- a/src/Altinn.Broker.Integrations/Azure/AzureResourceManagerOptions.cs +++ b/src/Altinn.Broker.Integrations/Azure/AzureResourceManagerOptions.cs @@ -4,10 +4,10 @@ namespace Altinn.Broker.Integrations.Azure; public class AzureResourceManagerOptions { - public string Location { get; set; } + public string Location { get; set; } = string.Empty; [StringLength(7, ErrorMessage = "The environment can only be 7 characters long because of constraint on length of Azure storage account name")] - public string Environment { get; set; } - public string? SubscriptionId { get; set; } - public string? ApplicationResourceGroupName { get; set; } - public string? MalwareScanEventGridTopicName { get; set; } + public string Environment { get; set; } = string.Empty; + public string SubscriptionId { get; set; } = string.Empty; + public string ApplicationResourceGroupName { get; set; } = string.Empty; + public string MalwareScanEventGridTopicName { get; set; } = string.Empty; } diff --git a/src/Altinn.Broker.Integrations/Azure/MalwareScanConfiguration.cs b/src/Altinn.Broker.Integrations/Azure/MalwareScanConfiguration.cs index adb7e18d..5f4fad21 100644 --- a/src/Altinn.Broker.Integrations/Azure/MalwareScanConfiguration.cs +++ b/src/Altinn.Broker.Integrations/Azure/MalwareScanConfiguration.cs @@ -2,32 +2,32 @@ internal class MalwareScanConfiguration { - public Properties Properties { get; set; } - public string Scope { get; set; } + public required Properties Properties { get; set; } + public required string Scope { get; set; } } internal class MalwareScanning { - public OnUpload OnUpload { get; set; } - public string ScanResultsEventGridTopicResourceId { get; set; } + public required OnUpload OnUpload { get; set; } + public required string ScanResultsEventGridTopicResourceId { get; set; } } internal class OnUpload { - public bool IsEnabled { get; set; } - public int CapGBPerMonth { get; set; } + public required bool IsEnabled { get; set; } + public required int CapGBPerMonth { get; set; } } internal class Properties { - public bool IsEnabled { get; set; } - public MalwareScanning MalwareScanning { get; set; } - public SensitiveDataDiscovery SensitiveDataDiscovery { get; set; } - public bool OverrideSubscriptionLevelSettings { get; set; } + public required bool IsEnabled { get; set; } + public required MalwareScanning MalwareScanning { get; set; } + public required SensitiveDataDiscovery SensitiveDataDiscovery { get; set; } + public required bool OverrideSubscriptionLevelSettings { get; set; } } internal class SensitiveDataDiscovery { - public bool IsEnabled { get; set; } + public required bool IsEnabled { get; set; } } diff --git a/src/Altinn.Broker.Persistence/Repositories/FileTransferRepository.cs b/src/Altinn.Broker.Persistence/Repositories/FileTransferRepository.cs index 4f3bf1f9..c24fcc25 100644 --- a/src/Altinn.Broker.Persistence/Repositories/FileTransferRepository.cs +++ b/src/Altinn.Broker.Persistence/Repositories/FileTransferRepository.cs @@ -25,7 +25,7 @@ public FileTransferRepository(DatabaseConnectionProvider connectionProvider, IAc public async Task GetFileTransfer(Guid fileTransferId, CancellationToken cancellationToken) { - var fileTransfer = new FileTransferEntity(); + FileTransferEntity fileTransfer; await using var command = await _connectionProvider.CreateCommand( @" @@ -102,16 +102,16 @@ GROUP BY { ActorId = reader.GetInt64(reader.GetOrdinal("sender_actor_id_fk")), ActorExternalId = reader.GetString(reader.GetOrdinal("senderActorExternalReference")) - } - }; + }, + RecipientCurrentStatuses = await GetLatestRecipientFileTransferStatuses(fileTransferId, cancellationToken), + PropertyList = await GetMetadata(fileTransferId, cancellationToken) + }; } else { return null; } } - fileTransfer.RecipientCurrentStatuses = await GetLatestRecipientFileTransferStatuses(fileTransferId, cancellationToken); - fileTransfer.PropertyList = await GetMetadata(fileTransferId, cancellationToken); EnrichLogs(fileTransfer); return fileTransfer; } diff --git a/src/Altinn.Broker.Persistence/Repositories/ResourceRepository.cs b/src/Altinn.Broker.Persistence/Repositories/ResourceRepository.cs index 60fb116f..65383438 100644 --- a/src/Altinn.Broker.Persistence/Repositories/ResourceRepository.cs +++ b/src/Altinn.Broker.Persistence/Repositories/ResourceRepository.cs @@ -59,7 +59,7 @@ public async Task CreateResource(ResourceEntity resource, CancellationToken canc command.Parameters.AddWithValue("@resourceId", resource.Id); command.Parameters.AddWithValue("@organizationNumber", resource.OrganizationNumber ?? ""); command.Parameters.AddWithValue("@maxFileTransferSize", resource.MaxFileTransferSize == null ? DBNull.Value : resource.MaxFileTransferSize); - command.Parameters.AddWithValue("@fileTransferTimeToLive", resource.FileTransferTimeToLive is null ? DBNull.Value : resource.MaxFileTransferSize.Value); + command.Parameters.AddWithValue("@fileTransferTimeToLive", resource.FileTransferTimeToLive is null ? DBNull.Value : resource.FileTransferTimeToLive.Value); command.Parameters.AddWithValue("@serviceOwnerId", resource.ServiceOwnerId); command.ExecuteNonQuery(); }