3
votes

I like OData and I was particularly pleased to its adoption by the ASP.NET Web API.

I've created a few services for internal applications to consume, but never for public consumption. The primary reason is that the open nature of OData seems to make it very hard to make "safe" against abuse.

Most specifically, I'm worried that given the power to run arbitrary queries, a user could express a complex query which stresses the operational system to the point where the experience is bad for all other users.

In a WebApi controller, an OData endpoint is exposed as follows:

public class OrderController
{
    [Queryable]
    public IQueryable<Orders> Get()
    {
         // Compose and return the IQueryable<Orders>
    }
}

This gives full control over the process of composition and execution of the query, but does so though the complex IQuerable<T> interface. It makes it trivial to give the user a subset of the information, e.g. append a Where to only include the records they have permission to access.

Is there an IQueryable<T> implementation that can wrap an existing IQuerable<T> instance to provide restrictions on the queries a user can run? I'm most interested in restricting the complexity of the query, but I also want to be able to prevent a user traversing associations to resources they shouldn't have access to.

2

2 Answers

3
votes

I think you'll be glad to learn that in RTM, we've added options to let you customize what kind of querying you want to expose to users. So you can do this for example:

[Queryable(
    AllowedFunctions = AllowedFunctions.AllStringFunctions,
    AllowedLogicalOperators = AllowedLogicalOperators.Equal,
    AllowedOrderByProperties = "ID")]

and restrict your query in a few common ways. If you want to restrict your query even further, there are validation hooks you can plug into, like ODataQueryValidator or by overriding the ValidateQuery method on the [Queryable] attribute.

You can use our nightly builds to get access to these features, or build the latest bits yourself.

2
votes

Instead of using the Queryable attribute (which you are missing), you can not use this attribute and instead manually accept the ODataQueryOptions parameter, which gives you access to the various filter, top, etc options to permit validation of them.

Any function being called by a OData query can be passed an argument ODataQueryOptions like:

public IQueryable<T> Get(ODataQueryOptions options)`  
{ 
//Use the following vars to fetch the values, and check if
//they are as you expect them to be. etc.  
options.Top.RawValue;
options.Filter.Value;
options.Filter.ApplyTo();
}

In this case you can skip the [Queryable] attribute and use ApplyTo to manually apply the various queries over the result. :)