OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
853 stars 476 forks source link

The unescaping of the url leads to entities, which can't be accessed by URL anymore #2309

Open ckuetbach opened 3 years ago

ckuetbach commented 3 years ago

There are some issues with the URL parsing.

Lets assume you have an entity with a key of type string. The value for this id is some%30value. This leads to an uri like odata/model/entity(%27some%2530value%27)/ or odata/model/entity('some%30value')/. The Url seems to be unescaped multiple times, until the key is unescaped to "some0value" (%30 is an escaped zero) which is not present in the database.

I think that the issue have been present before the fix of https://github.com/OData/WebApi/issues/2240, but since that fix I wasn't able create an entity with such an id, because the link generation thrown an error..

Assemblies affected

  <PackageReference Include="Microsoft.AspNetCore.OData" Version="7.5.0" />
  <PackageReference Include="Microsoft.OData.Core" Version="7.7.2" />

Reproduce steps

I've created a fork where I changed two of the sample projects to show the bevaivior: https://github.com/ckuetbach/WebApi/tree/bugfix/special_chars_in_key

In the project AspNetCoreODataSample.Web you load the Movies (http://localhost:5912/efcore/Movies) and then one of the movies by ID, like

In the project AspNetCore3xODataSample.Web you can load the Customer (http://localhost:5000/odata/Customers) If you load the customer with the ID "a%30b" you will get an internal server error http://localhost:5000/odata/Customers(%27a%2530b%27) with the message InvalidOperationException: Request URI 'http://localhost:5000/odata/Customers('a0b')' does not contain OData path 'Customers('a%30b')'.

System.InvalidOperationException: Request URI 'http://localhost:5000/odata/Customers('a0b')' does not contain OData path 'Customers('a%30b')'.
   at Microsoft.AspNet.OData.Routing.ODataPathRouteConstraint.RemoveODataPath(String uriString, String oDataPathString) in C:\Projekte\OSS\WebApi\src\Microsoft.AspNet.OData.Shared\Routing\ODataPathRouteConstraint.cs:line 160
   at Microsoft.AspNet.OData.Routing.ODataPathRouteConstraint.GetODataPath(String oDataPathString, String uriPathString, String queryString, Func`1 requestContainerFactory) in C:\Projekte\OSS\WebApi\src\Microsoft.AspNet.OData.Shared\Routing\ODataPathRouteConstraint.cs:line 74
   at Microsoft.AspNet.OData.Routing.ODataPathRouteConstraint.Match(HttpContext httpContext, IRouter route, String routeKey, RouteValueDictionary values, RouteDirection routeDirection) in C:\Projekte\OSS\WebApi\src\Microsoft.AspNetCore.OData\Routing\ODataPathRouteConstraint.cs:line 70
   at Microsoft.AspNetCore.Routing.RouteConstraintMatcher.Match(IDictionary`2 constraints, RouteValueDictionary routeValues, HttpContext httpContext, IRouter route, RouteDirection routeDirection, ILogger logger)
   at Microsoft.AspNetCore.Routing.RouteBase.RouteAsync(RouteContext context)
   at Microsoft.AspNetCore.Routing.RouteCollection.RouteAsync(RouteContext context)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Expected result

Actual result

Additional detail

If I request the complete list of elements, with full metadata, the generated URLs point so the non-functioning locations:

"@odata.context": "http://localhost:5000/odata/$metadata#Customers",
    "value": [
        {
            "@odata.type": "#AspNetCore3xODataSample.Web.Models.Customer",
            "@odata.id": "http://localhost:5000/odata/Customers('a%2530b')",
            "@odata.editLink": "http://localhost:5000/odata/Customers('a%2530b')",
            "Id": "a%30b",
            "Name": "Jonier",
            "HomeAddress": {
                "@odata.type": "#AspNetCore3xODataSample.Web.Models.Address",
                "City": "Redmond",
                "Street": "156 AVE NE"
            },
[...]

There is another issue, given there are two entitites with the IDs a/b and a%2Fb the response will be (correctly, I assume):

{
    "@odata.context": "http://localhost:5000/odata/string/$metadata#tt",
    "value": [
        {
            "@odata.type": "#Default.t",
            "@odata.id": "http://localhost:5000/odata/string/tt(%27a%2Fb%2Fc%27)/",
            "@odata.editLink": "http://localhost:5000/odata/string/tt(%27a%2Fb%2Fc%27)/",
            "id": "a/b/c"
        },
        {
            "@odata.type": "#Default.t",
            "@odata.id": "http://localhost:5000/odata/string/tt(%27a%252fb%252fc%27)/",
            "@odata.editLink": "http://localhost:5000/odata/string/tt(%27a%252fb%252fc%27)/",
            "id": "a%2fb%2fc"
        }
    ]
}

But both links will lead to the element with the unescaped ID a/b/c, the other one is not accessable. It looks like the issue is present because of this line, but I'm not entirely sure: https://github.com/OData/WebApi/blob/master/src/Microsoft.AspNet.OData.Shared/Routing/ODataPathRouteConstraint.cs#L165

ckuetbach commented 3 years ago

Are there any updates?

The stored entities aren't accessable. They also can't be deleted anymore.