I have controllers with actions, which works with some entity (Driver). Also, each Driver linked with identity profile.
public async Task<ActionResult> Details(int? id)
{
if ((id == null))
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
DriverDetailVM model = mapper.Map<DriverDetailVM>(db.Drivers.Find(id));
if ((model == null))
{
return HttpNotFound();
}
return View(model);
}
public async Task<ActionResult> Edit(int? id = null, bool isNewUser = false)
{
/////////
}
If user has role "Superadmin" then he has access to pages with any id value. If user has role "Driver" then we should have access only if id value is the same, as his profile. I try to implement it on ActionFilter:
public class DriverAccessActionFilterAttribute : ActionFilterAttribute
{
public string IdParamName { get; set; }
public int DriverID { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.IsInRole("Driver"))
{
if (filterContext.ActionParameters.ContainsKey(IdParamName))
{
var id = filterContext.ActionParameters[IdParamName] as Int32;
if (id != DriverID)
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
}
else
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
else
base.OnActionExecuting(filterContext);
}
}
but when I try to use this code:
[DriverAccessActionFilter(DriverID = currentUser.DriverId, IdParamName = "id")]
public async Task<ActionResult> Details(int? id)
{
it does not want to be compiled, because
An object reference is required for the non-static field, method, or property
how to implement it?
Attribute parameters are evaluated at compile-time, not at runtime. So they have to be compile time constants. You can't pass a value to an action attribute at run-time. i.e in [DriverAccessActionFilter(DriverID = currentUser.DriverId, IdParamName = "id")]
you are passing DriverID = currentUser.DriverId
. An attribute is used as controller/action metadata and metadata needs to be compiled in assembly. This is why attributes can take only constant values.
You have to change your attribute as follows:
You can modify your attribute as follow in case you implement CustomPrinicpal:
public class DriverAccessActionFilterAttribute : ActionFilterAttribute
{
public string IdParamName { get; set; }
private int DriverID { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.IsInRole("Driver"))
{
var customPrincipal = filterContext.HttpContext.User as CustomPrincipal;
DriverID = customPrincipal.Id;
// Rest of you logic
}
else
base.OnActionExecuting(filterContext);
}
}
In case you choose DI path then you can use the following snippet:
public class DriverAccessActionFilterAttribute : ActionFilterAttribute
{
public string IdParamName { get; set; }
private int DriverID { get; set; }
public DriverAccessActionFilterAttribute(IYourIdentityProvider provider)
{
DriverID = provider.LoggedInUserID;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Your logic
}
}
and then use your attribute as [DriverAccessActionFilter(IdParamName = "id")]