2

In a .NET Core 3 web API, I want to serialize an object with many properties. Depending on the caller's permissions, I want to show or hide specific properties. For now, I'm working with the [JsonIgnore] attribute exclusively. This is fine for properties, that you generally don't want to serialize.

But is there something similar like a [JsonIgnore(context => context.CheckUserPermissions())]? Or maybe something that I can add to my API method?

My current code is pretty simple:

[HttpGet("PickingZoneLists/{pickingZone}")]
public async Task<ActionResult<PickingListEntry[]>> GetData(string pickingZone) {
    // Get data
    dsyWorkOrder[] workOrders;

    try {
        workOrders = await GetPickingDocList(pickingZone);
    } catch (Exception ex) {
        return Problem("Data could not be gathered. " + ex.Message);
    }

    // Validate result
    if (workOrders == null)
        return Problem("Service returned invalid data.");

    if (workOrders.Length == 0)
        return NotFound();

    // Transform data
    PickingListEntry[] result;

    try {
        result = PickingListEntry.FromServiceResponse(workOrders).ToArray();
    } catch (Exception ex) {
        return Problem("Data could not be transformed. " + ex.Message);
    }

    // Maybe apply the filters to PickingListEntry here <-------------------

    // Create response object and return
    return Ok(result);
}
dbc
  • 104,963
  • 20
  • 228
  • 340
André Reichelt
  • 1,484
  • 18
  • 52
  • Please, share your code and reproducible sample – Pavel Anikhouski Mar 03 '20 at 14:45
  • 1
    What you're looking for is "conditional serialization"; most serializers support this out of the box, usually by if you have a property called `Foo`, you just add a method `bool ShouldSerializeFoo() { ... }` (sometimes the method needs to be `public` - for example `XmlSerializer`) and return `true` or `false` depending on whether you want to include it. Does this work in this case? – Marc Gravell Mar 03 '20 at 14:45
  • I don't think `System.Text.Json` supports `ShouldSerialize...` – DavidG Mar 03 '20 at 14:51
  • @PavelAnikhouski There is not much to show. It's just a simple API method which selects an object from a database and returns it to the client. I will add the code. – André Reichelt Mar 03 '20 at 14:52
  • @MarcGravell I believe, that I've seen something like that in JSON.net. Is there something similar for Microsoft's new `System.Text.Json`? – André Reichelt Mar 03 '20 at 14:53
  • `System.Text.Json` doesn't support (yet) some advanced scenarios available in `Json.NET` – Pavel Anikhouski Mar 03 '20 at 14:55
  • @AndréReichelt yes, I'm sure Json.NET supports it; no idea about System.Text.Json, but it wouldn't amaze me if it is a gap – Marc Gravell Mar 03 '20 at 14:56
  • 1
    @PavelAnikhouski I'm biased, but if I was writing/maintaining a serializer (cough, which I have), I'd list that under the "core/minimal features" bucket, or at least a "tranche B" feature – Marc Gravell Mar 03 '20 at 14:56
  • 2
    @MarcGravell There is a [migration guide](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#conditionally-ignore-a-property) and particular part about ignoring a property. it doesn't allow you to _Ignore selected properties based on arbitrary criteria evaluated at run time_ I guess that OP wants exactly this option. Of course, you can try to cover it by creating a [custom converter](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to) – Pavel Anikhouski Mar 03 '20 at 15:00
  • @PavelAnikhouski Under those circumstances, would you suggest me to better switch to the good old JSON.net, or should I dig down that rabbit hole and write my own custom converter? – André Reichelt Mar 03 '20 at 15:04
  • @AndréReichelt I would say "it depends" :) Only you can compare the effort of both options for your codebase and estimate a time for that. I'd like to suggest you have a look a guides, which were posted above and do a basic scenario to check and compare what is better for you – Pavel Anikhouski Mar 03 '20 at 15:14
  • Just out of curiosity, why use a serialization process as a security gate keeper for your application data? Seems to me that's going to lead to issues in maintainability. are you able to scale the possible data views down to a handful and just return one based on application role? – Mike-314 Mar 03 '20 at 16:09
  • why don't you return custom models per response type? – Daniel A. White Mar 03 '20 at 16:58
  • @Mike-314 One of the core features of the application is, that you can enable and disable each property on a per-user basis. – André Reichelt Mar 04 '20 at 07:44
  • @DanielA.White There are 30 possible properties and each of them could be enabled or disabled on a per-user basis. So basically no. – André Reichelt Mar 04 '20 at 07:45
  • @AndréReichelt I guess my concerns would be: 1, I think that by using attributes you are essentially making 30 decisions (with x amount of work) for each N number of records where you should just be able to make 30 decisions once. I think you'd be better off to just get from your data source the data you need. 2, maintainability, I don't think this would be apparent to future devs, but you could fix that by thoroughly notating the code. – Mike-314 Mar 04 '20 at 11:13
  • @Mike-314 Well, I have a list of all relevant property names per request. Therefore something like `JsonResult.FromObject(myObj).FilterAttributes(stringListOfAttributeNames)` would be a (theoretical) option. – André Reichelt Mar 04 '20 at 12:07
  • @AndréReichelt if you have the list of attributes, maybe you can use something like this https://stackoverflow.com/questions/16516971/linq-dynamic-select to filter before you transform to json – Mike-314 Mar 04 '20 at 12:21
  • You can get System.Text.Json to ignore properties with NULL values, I know it's not a 100% soltuion but it might be worth considering. Properties that you dont want to be serialised just leave them as NULL. https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#exclude-all-null-value-properties Only problem is if a property doesnt have any data but you do want it to be included. – Paul Oct 05 '20 at 08:52

1 Answers1

0

I think that the best approach is to have dedicated dto types for each security level/role.

This way ensures that you never reveal information that is not meant to be revealed.

If you wish to stay with a single type you could create a method that makes all undesirable properties null and then serialise this object with JsonIgnoreCondition.WhenWritingNull. This is still safe because in the event the seraliser is not configured correctly you're only exposing the names of fields, not their values.

var options = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
tymtam
  • 31,798
  • 8
  • 86
  • 126