'How to write an attribute for replacing sensitive data on serialization using System.Text.Json
I am logging my parameter models on console. In there I don't want to display the user password on login/register raw in logs, I want *. My limitation is that I need to use System.Text.Json! This is what I tried.
[AttributeUsage(AttributeTargets.Property)]
public class SensitiveDataAttribute : JsonAttribute
{
}
public class LoginModel
{
public string? Email { get; set; }
[SensitiveData]
public string? Password { get; set; }
public override string ToString() => this.ToLogJsonString();
}
public class SensitiveDataConverter : JsonConverter<string>
{
public SensitiveDataConverter()
{
}
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetString();
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
var attribute = value.GetType().GetTypeInfo().GetCustomAttribute<SensitiveDataAttribute>();
if (attribute is null)
{
writer.WriteStringValue(value);
return;
}
var secret = new String(value.Select(x => '*').ToArray());
writer.WriteStringValue(secret);
}
}
public static class LoggableObjectExtensions
{
public static string ToLogJsonString(this object value)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNameCaseInsensitive = true,
WriteIndented = false,
};
options.Converters.Add(new SensitiveDataConverter());
return JsonSerializer.Serialize(value, options);
}
}
At the end wanted to see the result
var model = new LoginModel
{
Email = "[email protected]",
Password = "Abrakadabra"
};
Console.WriteLine(model.ToLogJsonString());
I problem is that my attribute not got recognized in the SensitiveDataConverter. Any idea? thnx
Solution 1:[1]
the problem is that in your Write method
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
the argument value
has no knowledge about LoginModel
. You are doing a reflection on the primitive string
type to try and read the attribute that is in LoginModel
. Instead you need to have your Converter tied to the LoginModel
so you can inspect it's properties
public class SensitiveDataConverter : JsonConverter<LoginModel>
{
public override LoginModel? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, LoginModel value, JsonSerializerOptions options)
{
foreach (var propInfo in value.GetType().GetProperties())
{
if (propInfo.CanRead)
{
var propVal = propInfo.GetValue(value, null);
// obviously, leaving it up to you to beautify your json :)
if (propInfo.GetCustomAttribute<SensitiveDataAttribute>() != null)
{
writer.WriteStringValue("SensitiveData");
}
else
{
writer.WriteStringValue(propVal.ToString());
}
}
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |