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 @@ -74,7 +74,7 @@ public static IServiceCollection AddDefaultWebApiServices(this IServiceCollectio

// QueryValidators.
services.AddSingleton<ICountQueryValidator, CountQueryValidator>();

services.AddSingleton<IApplyQueryValidator, ApplyQueryValidator>();
services.AddSingleton<IFilterQueryValidator, FilterQueryValidator>();
services.AddSingleton<IODataQueryValidator, ODataQueryValidator>();
services.AddSingleton<IOrderByQueryValidator, OrderByQueryValidator>();
Expand Down
104 changes: 104 additions & 0 deletions src/Microsoft.AspNetCore.OData/Microsoft.AspNetCore.OData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11586,6 +11586,13 @@
<param name="context">The query context.</param>
<returns>The built <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.IODataQueryValidator"/>.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryContextExtensions.GetApplyQueryValidator(Microsoft.AspNetCore.OData.Query.ODataQueryContext)">
<summary>
Gets the <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator"/>.
</summary>
<param name="context">The query context.</param>
<returns>The built <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator"/>.</returns>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ODataQueryContextExtensions.GetComputeQueryValidator(Microsoft.AspNetCore.OData.Query.ODataQueryContext)">
<summary>
Gets the <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator"/>.
Expand Down Expand Up @@ -12568,6 +12575,17 @@
Gets the raw $apply value.
</summary>
</member>
<member name="P:Microsoft.AspNetCore.OData.Query.ApplyQueryOption.Validator">
<summary>
Gets or sets the $apply Query Validator.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.ApplyQueryOption.Validate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validate the $apply 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="M:Microsoft.AspNetCore.OData.Query.ApplyQueryOption.ApplyTo(System.Linq.IQueryable,Microsoft.AspNetCore.OData.Query.ODataQuerySettings)">
<summary>
Apply the apply query to the given IQueryable.
Expand Down Expand Up @@ -13368,6 +13386,24 @@
<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.ApplyQueryValidator">
<summary>
Represents a validator used to validate an <see cref="T:Microsoft.AspNetCore.OData.Query.ApplyQueryOption"/> based on the <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings"/>.
</summary>
<remarks>
The <c>filter</c> transformation is validated with the same <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.IFilterQueryValidator"/> used by
<c>$filter</c>, so a property marked as not filterable is rejected consistently. The <c>groupby</c>,
<c>aggregate</c> and <c>compute</c> transformations reject any referenced property that the model marks as
not filterable or configures as not selectable, consistent with <c>$filter</c> and <c>$select</c>.
</remarks>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.ApplyQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ApplyQueryOption,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates an <see cref="T:Microsoft.AspNetCore.OData.Query.ApplyQueryOption" />.
</summary>
<param name="applyQueryOption">The $apply query.</param>
<param name="validationSettings">The validation settings.</param>
</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"/>.
Expand Down Expand Up @@ -13646,6 +13682,41 @@
<param name="node">The single value node.</param>
<param name="validatorContext">The validator context.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.ValidateFunctionAllowed(System.String,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates that a function is allowed by the <see cref="P:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings.AllowedFunctions"/> allow-list.
</summary>
<remarks>
Shared with the <c>$apply</c> (groupby/aggregate/compute) and <c>$compute</c> node walk so those
transformations enforce the same function allow-list as <c>$filter</c>.
</remarks>
<param name="functionName">The function name.</param>
<param name="validationSettings">The validation settings.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.ValidateBinaryOperatorAllowed(Microsoft.OData.UriParser.BinaryOperatorNode,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates that a binary operator is allowed by the <see cref="P:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings.AllowedLogicalOperators"/>
or <see cref="P:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings.AllowedArithmeticOperators"/> allow-lists.
</summary>
<remarks>
Shared with the <c>$apply</c> (groupby/aggregate/compute) and <c>$compute</c> node walk so those
transformations enforce the same operator allow-lists as <c>$filter</c>.
</remarks>
<param name="binaryOperatorNode">The binary operator node.</param>
<param name="validationSettings">The validation settings.</param>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.FilterQueryValidator.ValidateUnaryOperatorAllowed(Microsoft.OData.UriParser.UnaryOperatorNode,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates that a unary operator ('not'/negate) is allowed by the
<see cref="P:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings.AllowedLogicalOperators"/> allow-list.
</summary>
<remarks>
Shared with the <c>$apply</c> (groupby/aggregate/compute) and <c>$compute</c> node walk so those
transformations enforce the same operator allow-list as <c>$filter</c>.
</remarks>
<param name="unaryOperatorNode">The unary operator node.</param>
<param name="validationSettings">The validation settings.</param>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.FilterValidatorContext">
<summary>
The metadata context for $filter validator.
Expand Down Expand Up @@ -13689,6 +13760,19 @@
Exit lambda expression.
</summary>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator">
<summary>
Provide the interface used to validate an <see cref="T:Microsoft.AspNetCore.OData.Query.ApplyQueryOption"/>
based on the <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings"/>.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ApplyQueryOption,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates the OData query.
</summary>
<param name="applyQueryOption">The $apply query.</param>
<param name="validationSettings">The validation settings.</param>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.IComputeQueryValidator">
<summary>
Provide the interface used to validate a <see cref="T:Microsoft.AspNetCore.OData.Query.ComputeQueryOption"/>
Expand Down Expand Up @@ -14188,6 +14272,26 @@
<exception cref="T:System.InvalidOperationException">Thrown if <paramref name="transformationExpressionType"/>
does not implement the required interface or inherit from the required base class.</exception>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.QueryNodeRestrictionValidator">
<summary>
Walks the query nodes referenced by <c>$apply</c> (groupby/aggregate/compute) and top-level
<c>$compute</c> expressions and rejects any property that the model marks as not filterable or
configures as not selectable, so those properties are enforced consistently with how they are
enforced for <c>$filter</c> and <c>$select</c>. The same walk also enforces the operator and
function allow-lists and the node-count limit from <see cref="T:Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings"/>, so those
limits apply to the groupby/aggregate/compute/$compute expressions exactly as they do to <c>$filter</c>.
</summary>
</member>
<member name="M:Microsoft.AspNetCore.OData.Query.Validator.QueryNodeRestrictionValidator.Validate(Microsoft.OData.UriParser.QueryNode,Microsoft.AspNetCore.OData.Query.ODataQueryContext,Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings)">
<summary>
Validates a single <see cref="T:Microsoft.OData.UriParser.QueryNode"/> and its descendants.
</summary>
<param name="node">The query node to validate. A <c>null</c> node is a no-op.</param>
<param name="context">The query context used to resolve the model and query configurations.</param>
<param name="validationSettings">
The validation settings whose operator/function allow-lists and node-count/depth limits are enforced.
</param>
</member>
<member name="T:Microsoft.AspNetCore.OData.Query.Validator.QueryValidatorContext">
<summary>
The base for validator context.
Expand Down
8 changes: 8 additions & 0 deletions src/Microsoft.AspNetCore.OData/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.MatchesPatternTimeoutMilli
Microsoft.AspNetCore.OData.Query.EnableQueryAttribute.MatchesPatternTimeoutMilliseconds.set -> void
Microsoft.AspNetCore.OData.Query.ODataQuerySettings.MatchesPatternTimeout.get -> System.TimeSpan?
Microsoft.AspNetCore.OData.Query.ODataQuerySettings.MatchesPatternTimeout.set -> void
Microsoft.AspNetCore.OData.Query.ApplyQueryOption.Validate(Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void
Microsoft.AspNetCore.OData.Query.ApplyQueryOption.Validator.get -> Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator
Microsoft.AspNetCore.OData.Query.ApplyQueryOption.Validator.set -> void
Microsoft.AspNetCore.OData.Query.ODataQuerySettings.MaxFunctionCallDepth.get -> int
Microsoft.AspNetCore.OData.Query.ODataQuerySettings.MaxFunctionCallDepth.set -> void
Microsoft.AspNetCore.OData.Query.Validator.ApplyQueryValidator
Microsoft.AspNetCore.OData.Query.Validator.ApplyQueryValidator.ApplyQueryValidator() -> void
Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator
Microsoft.AspNetCore.OData.Query.Validator.IApplyQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ApplyQueryOption applyQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void
Microsoft.AspNetCore.OData.ODataMiniOptions.MaxReceivedMessageSize.get -> long
Microsoft.AspNetCore.OData.ODataMiniOptions.SetMaxReceivedMessageSize(long maxReceivedMessageSize) -> Microsoft.AspNetCore.OData.ODataMiniOptions
Microsoft.AspNetCore.OData.ODataOptions.MaxReceivedMessageSize.get -> long
Microsoft.AspNetCore.OData.ODataOptions.MaxReceivedMessageSize.set -> void
virtual Microsoft.AspNetCore.OData.Query.Validator.ApplyQueryValidator.Validate(Microsoft.AspNetCore.OData.Query.ApplyQueryOption applyQueryOption, Microsoft.AspNetCore.OData.Query.Validator.ODataValidationSettings validationSettings) -> void
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ public static IODataQueryValidator GetODataQueryValidator(this ODataQueryContext
?? new ODataQueryValidator();
}

/// <summary>
/// Gets the <see cref="IApplyQueryValidator"/>.
/// </summary>
/// <param name="context">The query context.</param>
/// <returns>The built <see cref="IApplyQueryValidator"/>.</returns>
public static IApplyQueryValidator GetApplyQueryValidator(this ODataQueryContext context)
{
return context?.RequestContainer?.GetService<IApplyQueryValidator>()
?? new ApplyQueryValidator();
}

/// <summary>
/// Gets the <see cref="IComputeQueryValidator"/>.
/// </summary>
Expand Down
53 changes: 49 additions & 4 deletions src/Microsoft.AspNetCore.OData/Query/Query/ApplyQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.AspNetCore.OData.Abstracts;
using Microsoft.AspNetCore.OData.Query.Expressions;
using Microsoft.AspNetCore.OData.Query.Validator;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OData.Edm;
using Microsoft.OData.ModelBuilder;
Expand Down Expand Up @@ -53,17 +54,39 @@ public ApplyQueryOption(string rawValue, ODataQueryContext context, ODataQueryOp

RawValue = rawValue;
Context = context;
// TODO: Implement and add validator
//Validator = new FilterQueryValidator();
Validator = context.GetApplyQueryValidator();
_queryOptionParser = queryOptionParser;
ResultClrType = Context.ElementClrType;
}

// for unit test only
// This constructor is intended for unit testing only.
internal ApplyQueryOption(string rawValue, ODataQueryContext context)
{
if (string.IsNullOrEmpty(rawValue))
{
throw Error.ArgumentNullOrEmpty(nameof(rawValue));
}

if (context == null)
{
throw Error.ArgumentNull(nameof(context));
}

RawValue = rawValue;
Context = context;
Validator = context.GetApplyQueryValidator();
_queryOptionParser = new ODataQueryOptionParser(
context.Model,
context.ElementType,
context.NavigationSource,
new Dictionary<string, string> { { "$apply", rawValue } },
context.RequestContainer);

if (context.RequestContainer == null)
{
// By default, let's enable the property name case-insensitive
_queryOptionParser.Resolver = ODataQueryContext.DefaultCaseInsensitiveResolver;
}
}

/// <summary>
Expand Down Expand Up @@ -97,6 +120,28 @@ public ApplyClause ApplyClause
/// </summary>
public string RawValue { get; private set; }

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

/// <summary>
/// Validate the $apply 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);
}
}

/// <summary>
/// Apply the apply query to the given IQueryable.
/// </summary>
Expand Down
Loading