Contextual serialization from WebApi endpoint based on permissions
Asked Answered
L

2

9

I am using the Asp.Net Web Api. I would like to be able to filter out certain fields on the response objects based on the connected clients access rights.

Example:

class Foo
{
    [AccessFilter("Uberlord")]
    string Wibble { get; set; }

    string Wobble { get; set; }
}

When returning data the filed Wibble should only be returned if the current users context can satisfy the value of "Uberlord".

There are three avenues that I am exploring but I have not got a working solution:

  1. A custom WebApi MediaTypeFormatter.
  2. A custom json.net IContractResolver.
  3. Some sort of AOP wrapper for controllers that manipulates the response object

My issue with these are:

  • The custom formatter does not feel like the right place to do it but might be the only option.
  • The custom json serializer would not have access to the current context so I would have to work that out.
  • With the first two options you would require specific implementations for each response format, json, xml, some custom format, etc. This would mean that if another response type is supported then a custom formatter / serializer is required to prevent sensitive data leaking.
  • The AOP controller wrapper would require a lot of reflection.

An additional bonus would be to strip out values from the fields on an inbound request object using the same mechanism.

Have I missed an obvious hook? Has this been solved by another way?

Libna answered 23/4, 2013 at 14:33 Comment(2)
Thinking from an AOP controller perspective, would ALL controllers need to have this 'filtering' functionality? I guess you would be using an attribute on the controller for the 'would require a lot of reflection' comment? As well as reflecting the properties on the request and responses?Jeffreys
@Monkieboy This is at a lower level than the controller. It is more about making sure certain data is not exposed. The working solution I have which I have added as an answer below allows you to inspect an object after it has been returned from the controller. This gives you the ability to manipulate the object before it gets returned and does not care about how the data is sent (JSON/XML).Libna
L
4

It was actually a lot simpler than I first thought. What I did not realise is that the DelegatingHandler can be used to manipulate the response as well as the request in the Web Api Pipeline.

Lifecycle of an ASP.NET Web API Message

Delegating Handler


Delegating handlers are an extensibility point in the message pipeline allowing you to massage the Request before passing it on to the rest of the pipeline. The response message on its way back has to pass through the Delegating Handler as well, so any response can also be monitored/filtered/updated at this extensibility point.

Delegating Handlers if required, can bypass the rest of the pipeline too and send back and Http Response themselves.

Example

Here is an example implementation of a DelegatingHandler that can either manipulate the response object or replace it altogether.

public class ResponseDataFilterHandler : DelegatingHandler
{
    protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task =>
            {
                var response = task.Result;

                //Manipulate content here
                var content = response.Content as ObjectContent;
                if (content != null && content.Value != null)
                {
                    ((SomeObject)content.Value).SomeProperty = null;
                }

                //Or replace the content
                response.Content = new ObjectContent(typeof(object), new object(), new JsonMediaTypeFormatter());

                return response;
            });
    }
}

Microsoft article on how to implement a delegating handler and add it to the pipeline.HTTP Message Handlers in ASP.NET Web API

Libna answered 4/7, 2013 at 13:55 Comment(2)
I would have liked to know where you place this code and how to use it.Holtorf
@MarcRoussel - I have updated the answer with a link to an MS article that will show you how to implement a handler and then put that into the pipeline.Libna
B
0

I have a similar question in the works over here: ASP.NET WebAPI Conditional Serialization based on User Role

A proposed solution that I came up with is to have my ApiController inherit from a BaseApiController which overrides the Initalize function to set the appropriate formatter based on the user's role. I haven't decided if I will go this way yet, but perhaps it will work for you.

protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
    base.Initialize(controllerContext);
    // If the user is in a sensitive-data access role
    controllerContext.Configuration.Formatters.Add(/*My Formatter*/);
    // Otherwise use the default ones added in global app_start that defaults to remove sensitive data
}
Backset answered 3/7, 2013 at 16:3 Comment(1)
It has been a while since I posted this and your response prompted me to post my own findings. I did actually look at implementing my own formatter but during my journey of discovery I came upon my preferred solution which I have posted.Libna

© 2022 - 2024 — McMap. All rights reserved.