I have a custom RequireHttpsAttribute and a custom AuthorizeAttribute that I apply in FilterConfig to ensure that all controllers uses HTTPS and authorizes in the same way.
I also have a controller action that need some other authorization. In that case I must first use [OverrideAuthorization] to override the global authorization filter, and then I can set the special authorization for this action.
But [OverrideAuthorization] will also override the CustomRequireHttpsAttribute since that also inherts from IAuthorizationFilter. What can I do so that I don't have to readd the CustomRequireHttpsAttribute attribute every time I override the authorization?
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomRequireHttpsAttribute());
filters.Add(new CustomAuthorizeAttribute(Role = "User"));
}
}
public class MyController : BaseController
{
public ActionResult DoSomeUserStuff()
{
}
[OverrideAuthorization]
[CustomRequireHttpsAttribute]
[CustomAuthorizeAttribute(Role = "Admin")]
public ActionResult DoSomeAdminStuff()
{
}
}
I ended up creating my own custom IFilterProvider based on a modified version of this post.
I added an extra attribute that I can use for those Controllers or Actions where I want to override the attribute that is set globally. It really does nothing else than extend CustomAuthorizeAttribute so that it carries the same functionality:
public class OverrideCustomAuthorizeAttribute : CustomAuthorizeAttribute {}
Then I create an IFilterProvider that checks the presence of any OverrideCustomAuthorizeAttributes in the list of filters. If so, remove all CustomAuthorizeAttributes from the list:
public class CustomFilterProvider : IFilterProvider
{
private readonly FilterProviderCollection _filterProviders;
public CustomFilterProvider(IList<IFilterProvider> filters)
{
_filterProviders = new FilterProviderCollection(filters);
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = _filterProviders.GetFilters(controllerContext, actionDescriptor).ToArray();
var shouldOverrideCustomAuthorizeAttribute = filters.Any(filter => filter.Instance is OverrideCustomAuthorizeAttribute);
if (shouldOverrideCustomAuthorizeAttribute)
{
// There is an OverrideCustomAuthorizeFilterAttribute present, remove all CustomAuthorizeAttributes from the list of filters
return filters.Where(filter => filter.Instance.GetType() != typeof(CustomAuthorizeAttribute));
}
return filters;
}
}
I register this IFilterProvider in Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Some other stuff first....
var providers = FilterProviders.Providers.ToArray();
FilterProviders.Providers.Clear();
FilterProviders.Providers.Add(new CustomFilterProvider(providers));
}
}
And I register the global CustomAuthorizeAttribute in FilterConfig.cs just like before:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomRequireHttpsAttribute());
filters.Add(new CustomAuthorizeAttribute(Role = "User"));
}
}
The difference is that I use OverrideCustomAuthorizeAttribute in the controller instead:
public class MyController : BaseController
{
public ActionResult DoSomeUserStuff()
{
}
[OverrideCustomAuthorizeAttribute(Role = "Admin")]
public ActionResult DoSomeAdminStuff()
{
}
}
This way, CustomRequireHttpsAttribute is always set globally and never overridden.
According to this page, you can specify which filters to override in the OverrideAuthorizationAttribute.FiltersToOverride Property.
public class MyController : BaseController
{
public ActionResult DoSomeUserStuff()
{
}
[OverrideAuthorization(FiltersToOverride = typeof(CustomAuthorizeAttribute))]
[CustomAuthorizeAttribute(Role = "Admin")]
public ActionResult DoSomeAdminStuff()
{
}
}
Related
I have a FilterAttribute that has two parameters, one defined in dependency injection and one defined on method of controller as as string
public controller : ControllerBase
{
[MyFilter("Parameter1", FromDependency)]
public ActionResult MyMethod()
{
....
}
}
and the filter
public MyFilter : Attribute
{
MyFilter(string parameter1, context fromDependency)
{
}
}
How can I inject the parameter from dependency injection?
You can implement an IFilterFactory for this purpose. The runtime checks for this interface when creating filters and calls the CreateInstance method that gets an IServiceProvider as a parameter. You can use this provider to create services and inject them into the filter.
The following sample is taken from the docs:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
new InternalResponseHeaderFilter();
private class InternalResponseHeaderFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
}
If you need to both use services from DI and values defined on the attribute, you can use the following approach:
public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
private readonly string _attrParam;
public ResponseHeaderFilterFactory(string attrParam)
{
_attrParam = attrParam;
}
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var svc = serviceProvider.GetRequiredService<IMyService>();
return new InternalResponseHeaderFilter(_attrParam, svc);
}
private class InternalResponseHeaderFilter : IActionFilter
{
private readonly string _attrParam;
private readonly IMyService _service;
public InternalResponseHeaderFilter(string attrParam, IMyService service)
{
_attrParam = attrParam;
_service = service;
}
public void OnActionExecuting(ActionExecutingContext context) =>
context.HttpContext.Response.Headers.Add(
nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));
public void OnActionExecuted(ActionExecutedContext context) { }
}
}
You can then apply the filter like this:
public controller : ControllerBase
{
[ResponseHeaderFilterFactory("Parameter1")]
public ActionResult MyMethod()
{
....
}
}
You can implement ActionFilterAttribute to get DI dependencies from HttpContext.RequestServices:
public sealed class MyAttr : ActionFilterAttribute
{
MyAttr(string parameter1)
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
//Get dependency from HttpContext services
var myDependency = context.HttpContext.RequestServices.GetService<MyDependency>();
//Use it
myDependency.DoSomething();
//....
}
}
Injecting components into action filter attributes directly is not possible but there are various workarounds to allow us to effectively accomplish the same thing. Using ServiceFilter is a relatively clean way to allow dependency injection into individual action filters.
The ServiceFilter attribute can be used at the action or controller level. Usage is very straightforward:
[ServiceFilter(typeof(MyFilter))]
And our filter:
public class MyFilter: IActionFilter
{
MyFilter(string parameter1, context fromDependency)
{
}
}
Obviously, as we are resolving our filter from the IoC container, we need to register it:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<MyFilter>(x =>
new Service(x.GetRequiredService<IOtherService>(),
"parameter1"));
...
}
more details in Paul Hiles article: here
I have my controller SomeController and its inherited from ApiController, also, I have an ActionFilter:
FilterConfig
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
GlobalConfiguration.Configuration.Filters.Add(new LogExceptionFilterAttribute());
}
}
ErrorLogService
public static class ErrorLogService
{
public static void LogError(Exception ex, string metodo, string clase)
{
Utilidades.EnviarErrorHTTP(ex, null, metodo, clase);
}
}
LogExceptionFilterAttribute
public class LogExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//TODO
}
}
Well, the session is handled by the ApiController and in my SomeController I can use it like:
var session = TokenUser;
But, there nothing in my ErrorLogService to invoke the function to know the token.
Is there a way to share this variable if it is different in each session?
(TokenUser is an object).
I found a way to do it.
In the Global.asax you must add the next code:
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
And now, you are available to use Session:
var session = System.Web.HttpContext.Current.Session;
session["token"] = sesion;
And the variable session would persist in the application.
I created a custom authorize, which is ignored when an action has [Authorize]:
public class MyGlobalAuthorizeAttribute: AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// check if action is decorated with an Authorize...
var action = filterContext.ActionDescriptor
if (action.IsDefined(typeof(AuthorizeAttribute), true))
return;
base.OnAuthorization(filterContext);
}
}
...then I configured it in the global filters, only allowing admins by default:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyGlobalAuthorizeAttribute() { Roles = "Admin" });
}
}
If I decorate an action like this:
public class MyController: Controller
{
[Authorize] // non-admins can access this action..
public ActionResult Index()
{
}
}
...it works fine. However, if I put the [Authorize] in controller, MyGlobalAuthorizeAttribute won't detect it.
I found many examples of overriding, but all of them is about an action overriding a controller or a global authorize, but not a controller overriding a global authorize.
Is it possible to achieve this?
You also need to check the ControllerDescriptor:
var action = filterContext.ActionDescriptor;
if (action.IsDefined(typeof(AuthorizeAttribute), true))
return;
if (action.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), true))
return;
Documentation for ControllerDescriptor.IsDefined(...)
We have a Asp.net MVC Project that it has over 1000 ActionResult and I need to add all of them a C# Function for check value of Session.
So What is your offer to do this?
These two following lines has some example of my Controller and ActionResult :
public partial class CRMController : Controller
{
public ActionResult OrganizationCategory()
{
//I want add a C# function here
}
}
public partial class BaseInfoController : Controller
{
public ActionResult Lead()
{
//I Want Add a C# Function here
}
}
You can use ActionFilters for this.
The base ActionFilterAttribute class has the following methods that
you can override:
OnActionExecuting – This method is called before a controller action
is executed. OnActionExecuted – This method is called after a
controller action is executed. OnResultExecuting – This method is
called before a controller action result is executed. OnResultExecuted
– This method is called after a controller action result is executed.
Here full code example !
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
[LogActionFilter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
Please create a action filter attribute, in that check sessions. Then create a Base Controller, then apply this attribute in that controller. Then inherit this base controller with your business controllers.
public class MySessionCheckFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
//Check Session Method()
//if(SessionNotAvaliable)
//{
// throw new businessException;
//}
base.OnActionExecuting(context);
}
}
[MySessionCheckFilterAttribute]
public class BaseController:Controller
{
}
public class YourController_One: BaseController
{
//Do anything
}
public class YourController_Two : BaseController
{
//Do anything
}
Since your function is on checking the Session, i thought it could relates to an authorization process. If so, you can try with AuthorizeAttribute
Example: Checking the Session["username"] on every Function that tagged with [AuthorizeAttribute]:
public class SessionAuthAttribute : AuthorizeAttribute
{
public SessionAuthAttribute() { }
public override void OnAuthorization(AuthorizationContext filterContext)
{
//base.OnAuthorization(filterContext);
var userID = filterContext.HttpContext.Session["username"];
if (userID == null)
{
filterContext.Result = new RedirectResult("/Home");
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Home",
action = "Index"
})
);
}
}
In Controller:
[SessionAuthAttribute] //Applied for whole Controller
public class HomeController : Controller
{
[SessionAuthAttribute] //Applied for 1 function
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
EDIT: You can create this class in folder Attribute of your MVC Project
I have a custom ActionFilterAttribute. For the sake of this question let's assume it's as follows:
public class CustomActionFilterAttribute : ActionFilterAttribute {
public bool success { get; private set };
public override void OnActionExecuting(HttpActionContext actionContext) {
//Do something and set success
success = DoSomething(actionContext);
}
}
My controller is then decorated with CustomActionFilter. What I am looking for is a way (in my controller method) to do something like:
[CustomActionFilter]
public class MyController : ApiController {
public ActionResult MyAction() {
//How do I get the 'success' from my attribute?
}
}
If there is a more accepted way of doing this please let me know.
I discovered I could do the following to satisfy my problem:
[CustomActionFilter]
public class MyController : ApiController {
public ActionResult MyAction() {
var myAttribute = ControllerContext
.ControllerDescriptor
.GetCustomAttributes<CustomActionFilter>()
.Single();
var success = myAttribute.success;
}
}