Skip to content

Commit

Permalink
Adding validation on $export type filters. (#4617)
Browse files Browse the repository at this point in the history
* Adding validation on $export type filters.

* Addressing reviewer's comment.
  • Loading branch information
v-iyamauchi authored Sep 19, 2024
1 parent ebe218f commit 1c09493
Show file tree
Hide file tree
Showing 2 changed files with 299 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using EnsureThat;
using Hl7.Fhir.Utility;
using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Health.Core.Extensions;
using Microsoft.Health.Core.Features.Context;
Expand All @@ -18,10 +23,16 @@
using Microsoft.Health.Fhir.Core.Configs;
using Microsoft.Health.Fhir.Core.Exceptions;
using Microsoft.Health.Fhir.Core.Features.Context;
using Microsoft.Health.Fhir.Core.Features.Definition;
using Microsoft.Health.Fhir.Core.Features.Operations.Export.Models;
using Microsoft.Health.Fhir.Core.Features.Persistence;
using Microsoft.Health.Fhir.Core.Features.Search;
using Microsoft.Health.Fhir.Core.Features.Search.Registry;
using Microsoft.Health.Fhir.Core.Features.Security;
using Microsoft.Health.Fhir.Core.Messages.Export;
using Microsoft.Health.Fhir.Core.Models;
using Microsoft.Health.Fhir.ValueSets;
using StringExtensions = Microsoft.Health.Core.Extensions.StringExtensions;

namespace Microsoft.Health.Fhir.Core.Features.Operations.Export
{
Expand All @@ -35,25 +46,36 @@ public class CreateExportRequestHandler : IRequestHandler<CreateExportRequest, C
private readonly IAuthorizationService<DataActions> _authorizationService;
private readonly ExportJobConfiguration _exportJobConfiguration;
private readonly RequestContextAccessor<IFhirRequestContext> _contextAccessor;
private readonly ISearchOptionsFactory _searchOptionsFactory;
private readonly ILogger<CreateExportRequestHandler> _logger;
private readonly bool _includeValidateTypeFiltersValidationDetails;

public CreateExportRequestHandler(
IClaimsExtractor claimsExtractor,
IFhirOperationDataStore fhirOperationDataStore,
IAuthorizationService<DataActions> authorizationService,
IOptions<ExportJobConfiguration> exportJobConfiguration,
RequestContextAccessor<IFhirRequestContext> fhirRequestContextAccessor)
RequestContextAccessor<IFhirRequestContext> fhirRequestContextAccessor,
ISearchOptionsFactory searchOptionsFactory,
ILogger<CreateExportRequestHandler> logger,
bool includeValidateTypeFiltersValidationDetails = false)
{
EnsureArg.IsNotNull(claimsExtractor, nameof(claimsExtractor));
EnsureArg.IsNotNull(fhirOperationDataStore, nameof(fhirOperationDataStore));
EnsureArg.IsNotNull(authorizationService, nameof(authorizationService));
EnsureArg.IsNotNull(exportJobConfiguration?.Value, nameof(exportJobConfiguration));
EnsureArg.IsNotNull(exportJobConfiguration?.Value, nameof(fhirRequestContextAccessor));
EnsureArg.IsNotNull(searchOptionsFactory, nameof(searchOptionsFactory));
EnsureArg.IsNotNull(logger, nameof(logger));

_claimsExtractor = claimsExtractor;
_fhirOperationDataStore = fhirOperationDataStore;
_authorizationService = authorizationService;
_exportJobConfiguration = exportJobConfiguration.Value;
_contextAccessor = fhirRequestContextAccessor;
_searchOptionsFactory = searchOptionsFactory;
_logger = logger;
_includeValidateTypeFiltersValidationDetails = includeValidateTypeFiltersValidationDetails;
}

public async Task<CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
Expand All @@ -72,6 +94,7 @@ public async Task<CreateExportResponse> Handle(CreateExportRequest request, Canc
StringExtensions.ComputeHash(_exportJobConfiguration.StorageAccountConnection);

var filters = ParseFilter(request.Filters);
ValidateTypeFilters(filters);

ExportJobFormatConfiguration formatConfiguration = ParseFormat(request.FormatName, request.ContainerName != null);

Expand Down Expand Up @@ -179,5 +202,54 @@ private ExportJobFormatConfiguration ParseFormat(string formatName, bool useCont

return formatConfiguration;
}

private void ValidateTypeFilters(IList<ExportJobFilter> filters)
{
if (filters == null || filters.Count == 0)
{
_logger.LogInformation("No type filters to validate.");
return;
}

var errors = new List<string[]>();
foreach (var filter in filters)
{
if (filter.Parameters == null || filter.Parameters.Count == 0)
{
continue;
}

var searchOptions = _searchOptionsFactory.Create(
filter.ResourceType,
new ReadOnlyCollection<Tuple<string, string>>(filter.Parameters));
foreach (var parameter in searchOptions.UnsupportedSearchParams)
{
errors.Add(new string[]
{
filter.ResourceType,
parameter.Item1,
});
}
}

if (errors.Count > 0)
{
var errorMessage = new StringBuilder($"{errors.Count} invalid search parameter(s) found:{Environment.NewLine}");
errors.ForEach(e => errorMessage.AppendLine(
string.Format(CultureInfo.InvariantCulture, "[type: {0}, parameter: {1}]", e[0], e[1])));

var message = errorMessage.ToString();
_logger.LogError(message);

var ex = new BadRequestException(message);
if (_includeValidateTypeFiltersValidationDetails)
{
// Note: Test purpose only
ex.Data.Add(nameof(ValidateTypeFilters), errors);
}

throw ex;
}
}
}
}
Loading

0 comments on commit 1c09493

Please sign in to comment.