Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static IContainerBuilder AddDefaultWebApiServices(this IContainerBuilder
builder.AddService<SkipQueryValidator>(ServiceLifetime.Singleton);
builder.AddService<SkipTokenQueryValidator>(ServiceLifetime.Singleton);
builder.AddService<TopQueryValidator>(ServiceLifetime.Singleton);
builder.AddService<ComputeQueryValidator>(ServiceLifetime.Singleton);

builder.AddService<SkipTokenHandler, DefaultSkipTokenHandler>(ServiceLifetime.Singleton);

Expand Down
23 changes: 23 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10531,6 +10531,17 @@
Gets the raw $compute value.
</summary>
</member>
<member name="P:Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Validator">
<summary>
Gets or sets the $compute Query Validator.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ComputeQueryOption.Validate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validate the $compute query based on the given <paramref name="validationSettings"/>. It throws an ODataException if validation failed.
</summary>
<param name="validationSettings">The <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings"/> instance which contains all the validation settings.</param>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.CountQueryOption">
<summary>
Represents the value of the $count query option and exposes a way to retrieve the number of entities that satisfy a query.
Expand Down Expand Up @@ -11280,6 +11291,18 @@
<param name="remainingSegments">The remaining segments after the first non type segment.</param>
<returns>First non-type cast segment.</returns>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator">
<summary>
Represents a validator used to validate a <see cref="T:Microsoft.AspNetCore.OData.Query.ComputeQueryOption"/> based on the <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings"/>.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.ComputeQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ComputeQueryOption,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates a <see cref="T:Microsoft.AspNetCore.OData.Query.ComputeQueryOption" />.
</summary>
<param name="computeQueryOption">The $compute query.</param>
<param name="validationSettings">The validation settings.</param>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.CountQueryValidator">
<summary>
Represents a validator used to validate a <see cref="T:Microsoft.AspNetCore.OData.Query.CountQueryOption"/>
Expand Down
6 changes: 6 additions & 0 deletions src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<T>(System.Linq.IQueryable<T> query, Microsoft.AspNetCore.OData.Query.ODataQuerySettings querySettings, Microsoft.AspNetCore.OData.Query.ODataQueryOptions queryOptions) -> System.Linq.IQueryable<T>
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
Expand Down
25 changes: 25 additions & 0 deletions src/Microsoft.AspNetCore.OData/Query/Query/ComputeQueryOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.OData.Query.Validator;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;

Expand Down Expand Up @@ -48,6 +49,7 @@ public ComputeQueryOption(string rawValue, ODataQueryContext context, ODataQuery

Context = context;
RawValue = rawValue;
Validator = ComputeQueryValidator.GetComputeQueryValidator(context);
_queryOptionParser = queryOptionParser;
ResultClrType = Context.ElementClrType;
}
Expand All @@ -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,
Expand Down Expand Up @@ -106,5 +109,27 @@ public ComputeClause ComputeClause
/// Gets the raw $compute value.
/// </summary>
public string RawValue { get; }

/// <summary>
/// Gets or sets the $compute Query Validator.
/// </summary>
public ComputeQueryValidator Validator { get; set; }

/// <summary>
/// Validate the $compute query based on the given <paramref name="validationSettings"/>. It throws an ODataException if validation failed.
/// </summary>
/// <param name="validationSettings">The <see cref="ODataValidationSettings"/> instance which contains all the validation settings.</param>
public void Validate(ODataValidationSettings validationSettings)
{
if (validationSettings == null)
{
throw Error.ArgumentNull(nameof(validationSettings));
}

if (Validator != null)
{
Validator.Validate(this, validationSettings);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// <copyright file="ComputeQueryValidator.cs" company=".NET Foundation">
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
// See License.txt in the project root for license information.
// </copyright>
//------------------------------------------------------------------------------

using Microsoft.Extensions.DependencyInjection;
using Microsoft.OData;

namespace Microsoft.AspNetCore.OData.Query.Validator
{
/// <summary>
/// Represents a validator used to validate a <see cref="ComputeQueryOption"/> based on the <see cref="ODataValidationSettings"/>.
/// </summary>
public class ComputeQueryValidator
{
/// <summary>
/// Validates a <see cref="ComputeQueryOption" />.
/// </summary>
/// <param name="computeQueryOption">The $compute query.</param>
/// <param name="validationSettings">The validation settings.</param>
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<ComputeQueryValidator>() ?? new ComputeQueryValidator();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change leads to not parsing the ComputeClause during this validation. Is that desirable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in ODataQueryValidator, we validate whether the query option is allowed or not.
In each Query validator, for example FilterQueryValidator, ComputeQueryValidator, we valid the content.

For $compute clause, what expect error message do you want to get if the $compute clause is not valid?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that is the way, other validators work as well, than that is fine with me.
If the ComputeQueryValidator will parse the string and returns error message when the expression is incorrect, than that would be good enough for me, just like the filter errors.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will merge this PR and Let me try adding the expression validation in another PR.

}

if (options.Apply != null)
{
if (options.Apply.ApplyClause != null)
{
ValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions);
}
ValidateQueryOptionAllowed(AllowedQueryOptions.Apply, validationSettings.AllowedQueryOptions);
}

if (options.Skip != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<ComputeCustomer>("Customers");
builder.EntitySet<ComputeSale>("Sales");
IEdmModel model = builder.GetEdmModel();
return model;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 ()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 ()

Expand Down