I would like to do security validation in a custom attribute. A good example is if the user makes a GET request to retrieve an entity with a given id, I would like to intercept that request in the attribute, hand it off to an action filter, and then determine if the user has access to it. My only problem is how to retrieve the entity id. I can't pass it in the attribute declaration, because that gets initialized once instead of every request. Instead, I would like to give my custom attribute a url pattern like you would give HttpGet or HttpPost and have it resolve against the context's url parameters to result in an entity id.
Here is my attribute:
public class RequireProjectAccessAttribute : TypeFilterAttribute
{
public string UrlPattern { get; set; }
public RequireProjectAccessAttribute(string urlPattern) : base(typeof(RequireProjectAccessFilter))
{
UrlPattern = urlPattern;
Arguments = new object[] { urlPattern };
}
private class RequireProjectAccessFilter : IAsyncActionFilter
{
private readonly ICurrentSession _currentSession;
private readonly string _urlPattern;
public RequireProjectAccessFilter(ICurrentSession currentSession, string urlPattern)
{
_currentSession = currentSession;
_urlPattern = urlPattern;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var projectId = /* some magic to resolve _urlPattern against the current url parameter values */
if (/* user doesn't have access to project */)
{
context.Result = new UnauthorizedResult();
}
else
{
await next();
}
}
}
}
And here is how I would like to use it:
[Route("api/[controller]")]
public class ProjectsController : BaseController
{
public ProjectsController()
{
}
[RequireProjectAccess("{projectId}")]
[HttpGet("{projectId}")]
public JsonResult GetById(int projectId)
{
/* retrieve project */
}
}