Implementing IActionFilter - c#

I'm building the below filter:
public class TestflowFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
var profileId = int.Parse(ClaimsPrincipal.Current.GetClaimValue("UserId"));
var appId = int.Parse(filterContext.RouteData.Values["id"].ToString());
if (profileId != 0 && appId != 0)
{
if (CheckIfValid(profileId, appId))
{
// redirect
filterContext.Result = // url to go to
}
}
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
I actually only need OnActionExecuted, but since IActionFilter is an interface I have to implement them both. Is it ok to leave OnActionExecuting blank if I don't need anything to happen, or do I need to call a base version that MVC always runs?
Also in the OnActionExecuted method if the CheckIfValid is true I redirect the user, but if not I don't do anything. Is that ok or do I need to set some property on the filterContext instead.

I actually only need OnActionExecuted, but since IActionFilter is an interface I have to implement them both. Is it ok to leave OnActionExecuting blank if I don't need anything to happen, or do I need to call a base version that MVC always runs?
Leaving the method body empty is perfectly acceptable in this case. Looks good!
Also In the OnActionExecuted method if the CheckIfValid is true I redirect the user, but if not I don't do anything, is that ok or do I need to set some property on the filterContext instead.
Your filter is fine. MVC does offer a different abstract base class called ActionFilterAttribute, which implements these interfaces for you to override as needed. There's a nice overview that you can read about here. If you derive from that class, your filter attribute code could be simplified a little bit:
public class TestflowFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var profileId = int.Parse(ClaimsPrincipal.Current.GetClaimValue("UserId"));
var appId = int.Parse(filterContext.RouteData.Values["id"].ToString());
if (profileId != 0 && appId != 0)
{
if (CheckIfValid(profileId, appId))
{
// redirect
filterContext.Result = // url to go to
}
}
}
}

Related

OnActionExecuting Not Getting Executed

I am troubleshooting a ASP.NET MVC application and on one server the OnActionExecuting is not firing. It has been a long time since I looked at filters. What can keep the OnActionExecuting from running? The effect in our application is the user context never really gets set up (Initialize)... so everything redirects the user back to the login page.
Here is the code of the filter. Note "Jupiter" was the codename of the project
public class JupiterAuthenticationFilter : IActionFilter
{
private readonly IJupiterContext _jupiterContext;
public JupiterAuthenticationFilter(IJupiterContext jupiterContext)
{
if (jupiterContext == null)
{
throw new ArgumentNullException("jupiterContext");
}
_jupiterContext = jupiterContext;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_jupiterContext.Initialize();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
It can happen when your Controller has System.Web.MVC implementation, but ActionFilter has System.Web.Http.

Applying non-MVC related attributes to MVC actions

Our application has the notion of a PermissionAttribute. This attribute is defined in a base layer of our application and our commands and queries are decorated with that attribute. Since this attribute is defined in base layer we can't (and don't want to) to let it inherit from FilterAttribute or implement System.Web.Mvc.IActionFilter on it.
Still we would like to apply this attribute to controller actions as follows:
[Permission(Permissions.Administration.ManageUsers)]
public ActionResult Index()
{
return this.View();
}
Based on this attribute the proper security checks should be applied. I've been browsing through the MVC code base to find the proper hooks to customize MVCs behavior to allow adding these security checks based on this custom attribute. I though about creating a custom ControllerActionInvoker that returned a custom ReflectedControllerDescriptor from its GetControllerDescriptor method, which would return FilterAttribute that would be created based on the existence of the PermissionAttribute, but it feels like a lot of work, and I'm not sure this is the correct path to walk.
What would be am efficient and pleasant way to customize the MVC pipeline so that we can handle this non-MVC related attribute?
I would do it this way. First create your own implementation of the AuthorizeAttribtues like this:
public class PermissionAuthoriseAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Leaving the implementation of this to you, but you check if your
//PermissionAttribute is assigned and call it's methods.
if(...)
return true;
//You may want to check this first, depending on your requirements
return base.AuthorizeCore(httpContext);
}
}
Then apply this across your project by adding this line to the FilterConfig.cs file:
filters.Add(new PermissionAuthoriseAttribute());
I ended up doing the following:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new PermissionAuthorizationFilter(
() => Global.Container.GetInstance<IUserPermissionChecker>()), 0);
filters.Add(new HandleErrorAttribute());
}
}
public sealed class PermissionAuthorizationFilter : IAuthorizationFilter
{
private readonly Func<IUserPermissionChecker> userPermissionCheckerFactory;
public PermissionAuthorizationFilter(
Func<IUserPermissionChecker> userPermissionCheckerFactory) {
this.userPermissionCheckerFactory = userPermissionCheckerFactory;
}
public void OnAuthorization(AuthorizationContext filterContext) {
var attribute = filterContext.ActionDescriptor
.GetCustomAttributes(typeof(PermissionAttribute), true)
.OfType<PermissionAttribute>()
.SingleOrDefault();
if (attribute != null) {
VerifyPermission(filterContext, attribute.PermissionId);
}
}
private static void VerifyPermission(AuthorizationContext filterContext,
Guid permissionId) {
var permissionChecker = userPermissionCheckerFactory.Invoke();
if (!permissionChecker.HasPermission(permissionId))
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}

base method is not fired in mvc 5

I have the following base controller
public class BaseController : Controller
{
protected override void Execute(RequestContext requestContext)
{
base.Execute(requestContext);
}
}
Implementation is
public class HomeController : BaseController { }
we upgraded from mvc3 to mvc5 , in mvc5 this method is not getting called what needs to be done here ?
It's not entirely clear what your after but here's a few questions. Are your normal controllers inheriting your BaseController?
public class MyController : BaseController
I don't see what this line is meant to do....
var global = requestContext.HttpContext.Request.QueryString["global"] == null ? true : false;
You set the value then don't do anything with it. Are you meaning to store this value in the base controller so that you can access it from all your other controllers?
And surely if global is missing from the querystring then it should be false and not true.
public class BaseController : Controller
{
public bool isGlobal { get; private set; }
protected override void Execute(RequestContext requestContext)
{
this.isGlobal = requestContext.HttpContext.Request.QueryString["global"] == null ? false : true;
base.Execute(requestContext);
}
}
The title of this question led me here, but it's not really the same problem I was experiencing (i.e. the overridden Execute method in a base controller class not being called in an MVC 4/5 web site).
If that is the problem you are having too, this is the solution I found. It appears the way in which these methods are called has changed with the introduction of Async support. Instead of overriding the Execute method you should override the OnActionExecuted method instead.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
}

How to allow [Authorize] inside a custom AuthorizeAttribute

I have a custom AuthorizeAttribute like this
public class DevMode : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (myConditionToAuthorize)
{
// how to allow [Authorize] ?
}
}
}
The problem is that it is used along with [Authorize] tag like this:
[Authorize, DevMode]
public class UserController : ApiController { ... }
I need to allow [Authorize] == true inside [DevMode]
Or it is better to put them all together inside a unique authorize class? But then I dont know tho to check Authorize data.
Or it is better to put them all together inside a unique authorize class?
Oh yes, that would indeed be better. You could simply derive from the AuthorizeAttribute and call the base method:
public class DevModeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authorize = base.IsAuthorized(actionContext);
if (!authorized)
{
// the user is not authorized, no need to go any further
return false;
}
// now apply your custom authorization logic here and return true or false
...
}
}
and then:
[DevMode]
public class UserController : ApiController { ... }
I used this to add a custom IsAdmin (based on claims) using this method
public class IsAdminAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
IPrincipal principal = actionContext.RequestContext.Principal;
return principal.IsAdmin();
}
}
it kind of answers my own last comment so hope it helps someone else please note the .IsAdmin is an extension method on the IPrincipal that checks claims.

Override a Global Filter in MVC for One Method

In my filterConfig, I have registered a global attribute filter, which requires authorization on each of my methods.
However, I have one particular method where I want to apply a different authorization filter attribute. How does one accomplish this, if at all possible?
Note: I do not want to use the [AllowAnonymous] attribute (which works seamlessly and completely ignores my filter), since I want the request to be authorized, just through a different set of authorization logic on the method.
You can alter your filter to allow multiple by setting AllowMultiple = true in the AttributeUsage attribute on your attribute class, and add a check so that if the filter is present multiple times, the globally-applied one doesn't execute. The ActionExecutingContext that gets passed into OnActionExecuting() lets you get the filters applied via filterContext.ActionDescriptor.GetCustomAttributes(), so you can use that here.
Then, alter the constructor so that you can pass in a parameter (probably an enum) that it can use to decide which authorisation method to use - the normal one, or this other one. Give that parameter a default value that makes it select the normal auth method. Then, on that one method that needs a different auth method, you can apply the filter with the other value of the parameter. So it might look like this:
public class CustomAuthAttribute : AuthorizeAttribute
{
public CustomAuthAttribute(AuthMethod method = AuthMethod.StandardAuth)
{
//stuff
}
}
[CustomAuth(AuthMethod.WeirdAuth)]
public ActionResult MethodThatNeedsDifferentAuth()
{
//stuff
}
you can write your own version of the authorize attribute and pass specific parameter to depending on what action would you like your attribute to do for example
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public string currentAction { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (currentAction != "notallowed")
{
HandleUnauthorizedRequest(filterContext);
}
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext context)
{
context.Result = new RedirectResult("/home/login");
}
and then apply it to your class or action
[CustomAuthorize(currentAction = "notallowed")]
public class HomeController : Controller

Categories