From 4191955fd4aa966dae95ee2d5682621386e524ea Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 1 Nov 2022 12:24:18 -0700 Subject: [PATCH] Enable $ compute allowed settings --- .../Abstracts/ContainerBuilderExtensions.cs | 1 + .../Microsoft.AspNetCore.OData.xml | 23 +++++++++++++ .../PublicAPI.Unshipped.txt | 6 ++++ .../Query/Query/ComputeQueryOption.cs | 25 ++++++++++++++ .../Query/Validator/ComputeQueryValidator.cs | 34 +++++++++++++++++++ .../Query/Validator/CountQueryValidator.cs | 2 -- .../Query/Validator/ODataQueryValidator.cs | 11 ++---- .../DollarCompute/DollarComputeController.cs | 7 ++++ .../DollarCompute/DollarComputeEdmModel.cs | 1 + .../DollarCompute/DollarComputeTests.cs | 20 +++++++++++ ...rosoft.AspNetCore.OData.PublicApi.Net6.bsl | 9 +++++ ...t.AspNetCore.OData.PublicApi.NetCore31.bsl | 9 +++++ 12 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs diff --git a/src/Microsoft.AspNetCore.OData/Abstracts/ContainerBuilderExtensions.cs b/src/Microsoft.AspNetCore.OData/Abstracts/ContainerBuilderExtensions.cs index 4bccad7e3..6c0e7ce85 100644 --- a/src/Microsoft.AspNetCore.OData/Abstracts/ContainerBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.OData/Abstracts/ContainerBuilderExtensions.cs @@ -69,6 +69,7 @@ public static IContainerBuilder AddDefaultWebApiServices(this IContainerBuilder builder.AddService(ServiceLifetime.Singleton); builder.AddService(ServiceLifetime.Singleton); builder.AddService(ServiceLifetime.Singleton); + builder.AddService(ServiceLifetime.Singleton); builder.AddService(ServiceLifetime.Singleton); diff --git a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml index 4d0e931a0..e8239c4db 100644 --- a/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml +++ b/src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml @@ -10531,6 +10531,17 @@ Gets the raw $compute value. + + + Gets or sets the $compute Query Validator. + + + + + Validate the $compute query based on the given . It throws an ODataException if validation failed. + + The instance which contains all the validation settings. + Represents the value of the $count query option and exposes a way to retrieve the number of entities that satisfy a query. @@ -11280,6 +11291,18 @@ The remaining segments after the first non type segment. First non-type cast segment. + + + Represents a validator used to validate a based on the . + + + + + Validates a . + + The $compute query. + The validation settings. + Represents a validator used to validate a diff --git a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt index 30f0038b1..71b5b932f 100644 --- a/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt +++ b/src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt @@ -765,6 +765,9 @@ Microsoft.AspNetCore.OData.Query.ComputeQueryOption.ComputeQueryOption(string ra Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Context.get -> Microsoft.AspNetCore.OData.Query.ODataQueryContext Microsoft.AspNetCore.OData.Query.ComputeQueryOption.RawValue.get -> string Microsoft.AspNetCore.OData.Query.ComputeQueryOption.ResultClrType.get -> System.Type +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Validate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Validator.get -> Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator +Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Validator.set -> void Microsoft.AspNetCore.OData.Query.Container.IPropertyMapper Microsoft.AspNetCore.OData.Query.Container.IPropertyMapper.MapProperty(string propertyName) -> string Microsoft.AspNetCore.OData.Query.Container.ITruncatedCollection @@ -1087,6 +1090,8 @@ Microsoft.AspNetCore.OData.Query.TopQueryOption.Validate(Microsoft.AspNetCore.OD Microsoft.AspNetCore.OData.Query.TopQueryOption.Validator.get -> Microsoft.AspNetCore.OData.Query.Validator.TopQueryValidator Microsoft.AspNetCore.OData.Query.TopQueryOption.Validator.set -> void Microsoft.AspNetCore.OData.Query.TopQueryOption.Value.get -> int +Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator +Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.ComputeQueryValidator() -> void Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.CountQueryValidator() -> void Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator @@ -1828,6 +1833,7 @@ virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.IfNoneMatch.get -> Mi virtual Microsoft.AspNetCore.OData.Query.ODataQueryOptions.Validate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void virtual Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.ApplyTo(System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) -> System.Linq.IQueryable virtual Microsoft.AspNetCore.OData.Query.SkipTokenQueryOption.ApplyTo(System.Linq.IQueryable query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) -> System.Linq.IQueryable +virtual Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void virtual Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.CountQueryOption countQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.FilterQueryOption filterQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings settings) -> void virtual Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.Validate(Microsoft.OData.UriParser.FilterClause filterClause, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings settings, Microsoft.OData.Edm.IEdmModel model) -> void diff --git a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs index 3d21db285..f4a9f663b 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.OData.Query.Validator; using Microsoft.OData.Edm; using Microsoft.OData.UriParser; @@ -48,6 +49,7 @@ public ComputeQueryOption(string rawValue, ODataQueryContext context, ODataQuery Context = context; RawValue = rawValue; + Validator = ComputeQueryValidator.GetComputeQueryValidator(context); _queryOptionParser = queryOptionParser; ResultClrType = Context.ElementClrType; } @@ -68,6 +70,7 @@ internal ComputeQueryOption(string rawValue, ODataQueryContext context) Context = context; RawValue = rawValue; + Validator = ComputeQueryValidator.GetComputeQueryValidator(context); _queryOptionParser = new ODataQueryOptionParser( context.Model, context.ElementType, @@ -106,5 +109,27 @@ public ComputeClause ComputeClause /// Gets the raw $compute value. /// public string RawValue { get; } + + /// + /// Gets or sets the $compute Query Validator. + /// + public ComputeQueryValidator Validator { get; set; } + + /// + /// Validate the $compute query based on the given . It throws an ODataException if validation failed. + /// + /// The instance which contains all the validation settings. + public void Validate(ODataValidationSettings validationSettings) + { + if (validationSettings == null) + { + throw Error.ArgumentNull(nameof(validationSettings)); + } + + if (Validator != null) + { + Validator.Validate(this, validationSettings); + } + } } } diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs new file mode 100644 index 000000000..f0f629d26 --- /dev/null +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ComputeQueryValidator.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//------------------------------------------------------------------------------ + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OData; + +namespace Microsoft.AspNetCore.OData.Query.Validator +{ + /// + /// Represents a validator used to validate a based on the . + /// + public class ComputeQueryValidator + { + /// + /// Validates a . + /// + /// The $compute query. + /// The validation settings. + public virtual void Validate(ComputeQueryOption computeQueryOption, ODataValidationSettings validationSettings) + { + // so far, we don't have validation rules here for $compute + // however, customer can use this to inject the validator to add his own rules + } + + internal static ComputeQueryValidator GetComputeQueryValidator(ODataQueryContext context) + { + return context?.RequestContainer?.GetService() ?? new ComputeQueryValidator(); + } + } +} diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs index be2db156b..62d8bf2ba 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/CountQueryValidator.cs @@ -6,8 +6,6 @@ //------------------------------------------------------------------------------ using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.OData.Edm; using Microsoft.Extensions.DependencyInjection; using Microsoft.OData.Edm; diff --git a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs index 31c6373f7..c4d591886 100644 --- a/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs +++ b/src/Microsoft.AspNetCore.OData/Query/Validator/ODataQueryValidator.cs @@ -36,18 +36,13 @@ public virtual void Validate(ODataQueryOptions options, ODataValidationSettings // Validate each query options if (options.Compute != null) { - if (options.Compute.ComputeClause != null) - { - ValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions); - } + ValidateQueryOptionAllowed(AllowedQueryOptions.Compute, validationSettings.AllowedQueryOptions); + options.Compute.Validate(validationSettings); } if (options.Apply != null) { - if (options.Apply.ApplyClause != null) - { - ValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions); - } + ValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions); } if (options.Skip != null) diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeController.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeController.cs index 059d18c26..af56b6171 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeController.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeController.cs @@ -43,5 +43,12 @@ public IActionResult GetLocation(int key) return Ok(c.Location); } + + [HttpGet("odata/sales")] + [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.None)] + public IActionResult GetSales() + { + return Ok(); + } } } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeEdmModel.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeEdmModel.cs index bca9b6738..1b096563f 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeEdmModel.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeEdmModel.cs @@ -16,6 +16,7 @@ public static IEdmModel GetEdmModel() { var builder = new ODataConventionModelBuilder(); builder.EntitySet("Customers"); + builder.EntitySet("Sales"); IEdmModel model = builder.GetEdmModel(); return model; } diff --git a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeTests.cs b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeTests.cs index 4183675af..d43209401 100644 --- a/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeTests.cs +++ b/test/Microsoft.AspNetCore.OData.E2E.Tests/DollarCompute/DollarComputeTests.cs @@ -277,5 +277,25 @@ public async Task QueryForAnResource_IncludesDollarCompute_InNestedDollarExpand_ "]" + "}", payload); } + + [Fact] + public async Task QuerySales_ThrowsNotAllowed_IncludesDollarCompute_WithAllowedQueryOptionsNone() + { + // Arrange + string queryUrl = "odata/sales?$compute=Amount mul TaxRate as Tax"; + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, queryUrl); + HttpClient client = CreateClient(); + HttpResponseMessage response; + + // Act + response = await client.SendAsync(request); + + // Assert + string payload = await response.Content.ReadAsStringAsync(); + + Assert.Contains("The query specified in the URI is not valid. " + + "Query option 'Compute' is not allowed. To allow it, set the 'AllowedQueryOptions' property on EnableQueryAttribute or QueryValidationSettings", payload); + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } } } diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl index a8e204cae..32a5bb5e4 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.Net6.bsl @@ -1242,6 +1242,9 @@ public class Microsoft.AspNetCore.OData.Query.ComputeQueryOption { Microsoft.AspNetCore.OData.Query.ODataQueryContext Context { public get; } string RawValue { public get; } System.Type ResultClrType { public get; } + Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator Validator { public get; public set; } + + public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.CountQueryOption { @@ -2833,6 +2836,12 @@ public class Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder : M public virtual System.Linq.Expressions.Expression CreateTypeNameExpression (System.Linq.Expressions.Expression source, Microsoft.OData.Edm.IEdmStructuredType elementType, Microsoft.OData.Edm.IEdmModel model) } +public class Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator { + public ComputeQueryValidator () + + public virtual void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) +} + public class Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator { public CountQueryValidator () diff --git a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl index a8e204cae..32a5bb5e4 100644 --- a/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl +++ b/test/Microsoft.AspNetCore.OData.Tests/PublicApi/Microsoft.AspNetCore.OData.PublicApi.NetCore31.bsl @@ -1242,6 +1242,9 @@ public class Microsoft.AspNetCore.OData.Query.ComputeQueryOption { Microsoft.AspNetCore.OData.Query.ODataQueryContext Context { public get; } string RawValue { public get; } System.Type ResultClrType { public get; } + Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator Validator { public get; public set; } + + public void Validate (Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) } public class Microsoft.AspNetCore.OData.Query.CountQueryOption { @@ -2833,6 +2836,12 @@ public class Microsoft.AspNetCore.OData.Query.Expressions.SelectExpandBinder : M public virtual System.Linq.Expressions.Expression CreateTypeNameExpression (System.Linq.Expressions.Expression source, Microsoft.OData.Edm.IEdmStructuredType elementType, Microsoft.OData.Edm.IEdmModel model) } +public class Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator { + public ComputeQueryValidator () + + public virtual void Validate (Microsoft.AspNetCore.OData.Query.ComputeQueryOption computeQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) +} + public class Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator { public CountQueryValidator ()