I agree with your approach, the response filter would probably be the best solution to do this; and simply mark up the response DTO with an attribute describing the required roles. I haven't seen a better way to do property level permissions.
It is perfectly acceptable to remove properties from response based on roles, if it is a public API just make sure to document the properties people can expect back in each role.
Response Filter, in your AppHost:
this.ResponseFilters.Add((req, res, dto) => {
// Get the roles you are permitted to access. You will need to store these in the request Items collection
var roles = (from r in req.Items where r.Key == "Roles" select r.Value).FirstOrDefault() as string[];
// Get the type of the response dto
var dtoType = dto.GetType();
// Loop through the properties
foreach(var property in dtoType.GetPublicProperties()){
// Ignore properties that are read-only
if(!property.CanWrite)
continue;
// Get all the role attributes on the property
var attributes = property.GetCustomAttributes(typeof(RequireRoleAttribute), false) as RequireRoleAttribute[];
// Get all the permitted roles
var permittedRoles = new List<string>();
foreach(var attribute in attributes)
permittedRoles.AddRange(attribute.Roles);
// Check if there are specific permitted roles assigned to this attribute
if(permittedRoles.Count != 0)
{
bool permitted = false;
// Check if check require role against roles we may have.
foreach(var role in permittedRoles){
if(roles.Contains(role))
{
// We have a matching role
permitted = true;
break;
}
}
// No permission to the property
if(!permitted) {
var type = property.GetType();
// Set the field to a default value.
property.SetValue(dto, null);
}
}
}
});
The attribute:
public class RequireRoleAttribute : Attribute
{
public string[] Roles { get; set; }
public RequireRoleAttribute(params string[] roles) { Roles = roles; }
}
On the DTO:
[RequireRole("Spiderman","Superman","Batman")]
public string Address { get; set; }
Notes:
- You will need to save you permitted roles into the
request.Items.Add("Roles", string[])
- I haven't tested the above code, so it may not be perfect, but it should be pretty close.
I hope this helps.