QueryInterceptors and ChangeInterceptor are needed to implement Security
One needs to write lots of code to implement query and change interception to EF context. With interceptors we can easily do this with Breeze like WCF Data Services.
odeyinka olubunmi commented
@Ward and @Jonny the only way round this is to call the service that will return the data as json from a C# code using httpWebClient or httpWebRequest downloading the json result. DeSerializing it with JsonConvert.DeserializeObject etc.back to C# objects and use the object for your reports.
I just wrote a class that u pass the endpoint to and does all this and is working for me
Not to my knowledge. The execution of the LINQ query occurs outside the controller method. There is no way to inspect the results.
The only way I know to intercept the results is to execute the query yourself after applying the client's query specification ... which is no easy task.
.. or to write a custom Web API filter (for example, modify the JSON serialization filter with a custom Json.Net serialization interceptor) ... which seem like the wrong abstractions to me and would be no easy task either.
I've been thinking about an option on the BreezeEnableQuery attribute for calling back into some method in your controller that could post-process the results before sending them out of the controller, toward the client (that is, onward to the filters). I have gotten no further than speculation.
Jonny Nguyen commented
Is it possible to get the results of the request before the return statement?
You can get the query string from the URL programmatically with code like this:
var allUrlKeyValues = ControllerContext.Request.GetQueryNameValuePairs();
string filter = allUrlKeyValues.SingleOrDefault(x => x.Key == "$filter").Value;
What you do with it is another matter.
Jonny Nguyen commented
Is it possible to get the filter data on the server side using some kind of intercept. The main idea is for exporting the data to csv. This should allow you to use the same query for your export.
Ward, I just did a file/new project with the Breeze/Knockout template and confirmed the iQueryable filter stays on the server. This is great news! For some reason i thought the flter portion of the iQueryable was also returned to the client. Thanks for your patience and clarification. I'm drinking the breeze koolaid. :)
@bap Just to be extra clear: the controller METHOD returns IQueryable but what goes to the client is JSON DATA ... the result of server-side execution of the IQueryable. The IQueryable itself never leaves the server.
@bap You made the argument against including the UserID in the query from the client. it is useless from a security perspective. So don't bother. No, whatever extra filtering you do on the server stays on the server. It is not communicated to the client. There is nothing to spoof. The client's filter criteria are ANDed with the server's so the client can't expand the query. Now it doesn't hurt (belt-and-suspenders) if you want to see if the client tried to mention UserId. You can actually do that pretty easily simply by applying a RegEx to the request text ... which you have access to within any Web API controller method. But I'm not that paranoid :)
thanks for the quick response Ward. yep, we can get to the user context from iPrincipal on the server. but if we return an iqueryable that includes userID in where statement such as "datacontext.appointments.where(a => a.userID = "joe") would breeze include the userID in the odata filter expression on the client in subsequent calls? if so, wouldn't it be easy to spoof the userID on the client? If there is a query interceptor we could always check the filter in the query expression to ensure userID="joe" prior to executing query. maybe i'm getting ahead of myself...i'm very new to breeze...thanks.
The logged in user is available to you on the server now. It should be in your identity that is in the `IPrincipal` (`System.Threading.Thread.CurrentPrincipal`). If you didn't put the `UserId` in the identity during your authentication step (which I recommend), you should be able to use that identity to acquire the `UserID`. The ASP.NET SPA Template and both Breeze SPA Templates illustrate this technique (they use the Web API controller's `User` property which is just a wrapper around `CurrentPrincipal`); all of them filter the TodoLists by `UserId`. So I don't think there is an action item for Breeze.
We have a multi-tennant application that needs to partition every query by customerID. We would like to get the customerID from the logged-in userID using forms authentication on the server then append the customerID=id filter to every query to be executed. We need to handle this on the server for security purposes. Does that make sense?
I see your point -in fact at first we created a custom action filter, later we sub-classed EFContextProvider but we removed all and did all work at EF Context level.
But Action Filter is an attribute so we cannot inject dependencies and we don't want to use static DI Container to get dependencies (at least not at library level).
If we create custom action filter the code would not be reusable, if we create custom EFContextProvider we strictly couple our code to breeze.
I want to be able to inject a request/response tracker object to api controller, and trigger it when necessary (interceptions). But most important thing for me is if we have interceptors code would be cleaner, readable, maintainable (and 1337 :))
At least, you might consider adding some clear events to EFContextProvider (like entity queried, entity added, entity read etc.).
And lastly, you are right, we can live without this, but one day if nothing is left to add to breeze you can do this :)
I think Query interceptor can be used for both To read in the incoming Request, perform some authorization(modify the Query to remove Expand clause if user is not authorized), Log the incoming Query. all this happens before Query is Executed againts Database. Once the Query is Executed you can pass the Custom Structure filled with Data. So Query Interceptors are Great Place to perform additional work. You are right OdataActionFilter may be used for that.
Yes agree before save may be good place for ChangeIntercept
I understand the interest in QueryInterceptor. What is a ChangeInterceptor? Isn't that covered in the BeforeSave... methods described in http://www.breezejs.com/documentation/server-side-interception ?
The QueryInterceptor is an interesting challenge. You can write one today by taking over the ODataActionFilter ... that's a natural interception point. Once you were in the pipeline, what would you like to do? Examine the query string (easy). Examine the retrieved entities, including the ones tucked in by $expand (harder and potentially non-performant). Tell us what kinds of things you would do in your interceptor.