MVC Action filter across all the application - asp.net-mvc

I am trying to create authorization action filter the will fire on each request to check if the user is allow to do some stuff.
So, i created the following classes/interfaces:
public interface IGlobalAuthorizationFilter : IGlobalFilter, IAuthorizationFilter
{
}
public interface IGlobalFilter
{
bool ShouldBeInvoked(ControllerContext controllerContext);
}
public class GlobalFilterActionInvoker : ControllerActionInvoker
{
protected FilterInfo GlobalFilters;
public GlobalFilterActionInvoker()
{
GlobalFilters = new FilterInfo();
}
public GlobalFilterActionInvoker(FilterInfo filters)
{
GlobalFilters = filters;
}
public GlobalFilterActionInvoker(IEnumerable<IGlobalFilter> filters)
: this(new FilterInfo())
{
foreach (IGlobalFilter filter in filters)
RegisterGlobalFilter(filter);
}
public FilterInfo Filters
{
get { return GlobalFilters; }
}
public void RegisterGlobalFilter(IGlobalFilter filter)
{
if (filter is IGlobalAuthorizationFilter)
GlobalFilters.AuthorizationFilters.Add((IGlobalAuthorizationFilter) filter);
}
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
FilterInfo definedFilters = base.GetFilters(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter filter in Filters.AuthorizationFilters)
{
var globalFilter = filter as IGlobalFilter;
if (globalFilter == null ||
(globalFilter.ShouldBeInvoked(controllerContext)))
{
definedFilters.AuthorizationFilters.Add(filter);
}
}
return definedFilters;
}
}
public class ApplicationControllerFactory : DefaultControllerFactory
{
private readonly IUnityContainer _container;
public ApplicationControllerFactory(IUnityContainer container)
{
this._container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if ( controllerType == null )
{
throw new HttpException(404, "The file " + requestContext.HttpContext.Request.FilePath + " not found.");
}
IController icontroller = _container.Resolve(controllerType) as IController;
if (typeof(Controller).IsAssignableFrom(controllerType))
{
Controller controller = icontroller as Controller;
if (controller != null)
controller.ActionInvoker = _container.Resolve<IActionInvoker>();
return icontroller;
}
return icontroller;
}
}
And the class with the function that need to be called, but its not..
public class AuthenticationActionFilter : IGlobalAuthorizationFilter
{
public bool ShouldBeInvoked(System.Web.Mvc.ControllerContext controllerContext)
{
return true;
}
public void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
}
}
And, the Global.asax registration stuff:
IUnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<IUserService, UserManager>();
unityContainer.RegisterType<IAppSettings, AppSettingsHelper>();
unityContainer.RegisterType<ICheckAccessHelper, CheckAccessHelper>().Configure<InjectedMembers>().ConfigureInjectionFor<CheckAccessHelper>(new InjectionConstructor());
unityContainer.RegisterType<IActionInvoker, GlobalFilterActionInvoker>().Configure<InjectedMembers>().ConfigureInjectionFor<GlobalFilterActionInvoker>(new InjectionConstructor());
unityContainer.RegisterType<IGlobalAuthorizationFilter, AuthenticationActionFilter>();
IControllerFactory unityControllerFactory = new ApplicationControllerFactory(unityContainer);
ControllerBuilder.Current.SetControllerFactory(unityControllerFactory);
So, as i said, my problem is the function: "ShouldBeInvoked" never called.
Any help?

I believe this filter would only be invoked on actions decorated with [Authorize] do you have that on the methods you want this filter to run?

Related

Changing the type of action parameter at runtime depending on current user in aspnet webapi

How to alter the TViewModel from within a action filter or a model binder?
[HasPriviliege]
public IHttpActionResult Get(long id)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
[HasPriviliege]
public IHttpActionResult Edit(long id, TViewModel model)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
the filter should be
public class HasPriviliege:ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if(getPrivileges()=="doctor"){
//the TViewModel(view model type to bind to) should be
// DoctorPatientViewModel should be;
}else{
//the TViewModel(view model type to bind to) should be
//ExaminationPatientViewModel
}
//base.OnActionExecuting(actionContext);
}
}
or alternativaly, the model binder
public class IPrivilegeableModelBinder: IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
//return (hasPriviliege()?DoctorPatientViewModel:ExaminationPatientViewModel) ;
}
}
Rather than write an over-bloated comment, I'll post my suggestion on how we accomplished something similar to this using a generic controller.
Controller factory:
public class ControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = typeof(GenericController<>);
Type genericType = controllerType.MakeGenericType(GetPrivilegeType());
ConstructorInfo ctor = genericType.GetConstructor(new Type[]{});
return (IController)ctor.Invoke(new object[] { });
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
...
return SessionStateBehavior.ReadOnly;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
{
((IDisposable)controller).Dispose();
}
}
private string GetPrivilegeType()
{
if (getPrivileges() == "doctor") {
return typeof(DoctorPatientViewModel);
} else {
return typeof(ExaminationPatientViewModel);
}
}
}
Register it like this:
ControllerBuilder.Current.SetControllerFactory(new ControllerFactory());
...and finally what your controller might look like
public class GenericController<TViewModel> // TViewModel will be the privilege type from the factory
where TViewModel : IPrivilege
{
[HasPriviliege]
public IHttpActionResult Edit(long id, TViewModel model)
{
var entity = AutoMapper.Mapper.Map<TViewModel, TEntity>(model);
repo.Update(id, entity);
repo.Save();
return Ok(model);
}
}
That's the most basic example to get a generic controller working for mvc which might go some way to what you're trying to accomplish.

How to get the ActionDescription UniqueId from within OnResultExecuted

In ASP.NET MVC, does anyone know a trick to access the ActionDescriptor.UniqueId from within OnResultExecuted? I need to pass information from OnActionExecuting to OnResultExecuted in a way that will work if multiple actions are executed during the one HttpRequest.
For example:
private Dictionary<string,Foo> _foos
{
get { return HttpContext.Current.Items["foos"] as Dictionary<string,Foo>; }
set { HttpContext.Current.Items["foos"] = value; }
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var foos = _foos;
foos[context.ActionDescriptor.UniqueId] = new Foo();
_foos = foos;
}
public override void OnResultExecuted(ResultExecutedContext context)
{
var actionUniqueId = ????
var foo = _foos[actionUniqueId]
}
You can custom the FilterAttributeFilterProvider and ActionFilterAttribute to implement it.
First you can create a filter that inherit the ActionFilterAttribute and contains ActionDescriptor property:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public ActionDescriptor ActionDescriptor { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var actionUniqueId = ActionDescriptor.UniqueId;
//code..
}
}
Then you need create a filter provider inherit the FilterAttributeFilterProvider and override the GetFilters method:
public class MyFilterProvider : FilterAttributeFilterProvider
{
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.Controller != null)
{
foreach (FilterAttribute attr in GetControllerAttributes(controllerContext, actionDescriptor))
{
var myAttr = attr as MyActionFilterAttribute;
if (myAttr != null)
{
myAttr.ActionDescriptor = actionDescriptor;
}
yield return new Filter(attr, FilterScope.Controller, order: null);
}
foreach (FilterAttribute attr in GetActionAttributes(controllerContext, actionDescriptor))
{
var myAttr = attr as MyActionFilterAttribute;
if (myAttr != null)
{
myAttr.ActionDescriptor = actionDescriptor;
}
yield return new Filter(attr, FilterScope.Action, order: null);
}
}
}
}
You can see at the GetFilters method, We set the ActionDescriptor property if the filter type is MyActionFilterAttribute.
Finally, At Global.asax, you need use MyFilterProvider instance to replace the FilterAttributeFilterProvider instance in Providers collection:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//replace the FilterAttributeFilterProvider in providers collection
for (int i = 0; i < FilterProviders.Providers.Count; i++)
{
if (FilterProviders.Providers[i] is FilterAttributeFilterProvider)
{
FilterProviders.Providers[i] = new MyFilterProvider();
break;
}
}
//other global init code...
}
}

Exception Filters MVC - Dependency Injection

I am not getting logger instance in OnException method. I am using IunityContainer and have initialized in Global.asax
Please help me to correct the code.
Thanks
public class InjectIntoActionInvoker : ControllerActionInvoker
{
private IUnityContainer _container;
public InjectIntoActionInvoker(IUnityContainer container)
{
_container = container;
}
protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters)
{
foreach (IActionFilter filter in filters)
{
_container.BuildUp(filter.GetType(), filter);
}
return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
}
}
private ILogger logger;
[Dependency]
public ILogger _logger
{
get
{
return this.logger;
}
set
{
if (value != null)
{
this.logger = value;
}
}
}
public void OnException(ExceptionContext filterContext)
{
if (filterContext != null && filterContext.Exception!=null)
{
_logger.LogError(filterContext.Exception.Message, filterContext.Exception);
}
}
}
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterInstance<ILogger>(new Logger());
container.RegisterType<IActionInvoker, InjectIntoActionInvoker>();
}
}
Try injecting the logger in constructor...
public InjectIntoActionInvoker(IUnityContainer container, ILogger lo)
{
_container = container;
this._logger = lo;
}
I'm really not convinced this is going to work for you without using a controller factory. I'm stealing the code example from here, but this gives you a good idea on how to create a customer controller factory, which also configures a custom action invoker:
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext context, string controllerName)
{
var controller = base.CreateController(context, controllerName);
return ReplaceActionInvoker(controller);
}
private IController ReplaceActionInvoker(IController controller)
{
var mvcController = controller as Controller;
if (mvcController != null)
mvcController.ActionInvoker = new ControllerActionInvokerWithDefaultJsonResult();
return controller;
}
}
public class ControllerActionInvokerWithDefaultJsonResult : ControllerActionInvoker
{
public const string JsonContentType = "application/json";
protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
{
if (actionReturnValue == null)
return new EmptyResult();
return (actionReturnValue as ActionResult) ?? new ContentResult()
{
ContentType = JsonContentType,
Content = JsonConvert.SerializeObject(actionReturnValue)
};
}
}
You could still have the custom action invoker, and controller factory for that matter, in a different assembly if you want.

Controller action called with old parameter

here in my company we can't instantiate a controller for each new request. We have to store it in the session and re-utilize it every time, i know this is wrong, but we have to keep the state of the controller between requests. So here's what we did:
We created this controller factory:
public class SGVControllerFactory : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
string _SessionId = controllerName + "Controller";
foreach (var _Object in HttpContext.Current.Session)
{
if (_Object.ToString() == _SessionId)
{
IController _Controller = (IController)HttpContext.Current.Session[_Object.ToString()];
return _Controller;
}
}
return null;
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
//We never release our controllers!!!
}
}
And we have this base controller:
public class SGVController : Controller
{
protected override void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException("Http context is null", "requestContext");
}
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
Dispose();
}
}
}
The only thing this controller class does differently from the default MVC controller is that it doesn't limit itself to be called just once.
Now, my problem is.. if I have this action:
public JsonResult Foo(string Bar) {
return Json(new List<string> { Bar, Bar });
}
The 'Bar' parameter will aways have the value of the first call to the action. I can't find anything that explains that. The request parameter dictionary has the right values, but the action still gets the old value.
You may try to reinit the ValueProvider and the TempData by overriding the Initialize method to have the new values being handled.
public class SGVController : Controller
{
protected override void Initialize(RequestContext requestContext)
{
this.TempData = null;
this.ValueProvider = null;
base.Initialize(requestContext);
}
protected override void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException("Http context is null", "requestContext");
}
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
Dispose();
}
}
}
Hope this will help,

Binding Ninject to child Controller - Error: did not return controller for

I'm attempting to bind some repositories to child controller, but I keep on getting en error that NinjectControllerFactory' did not return a controller for the name 'soccer'.
Base Controller:
public class TeamController<T> : Controller
{
protected readonly ITeamRepository<T> TeamRepository;
public TeamController(ITeamRepository<T> teamRepository)
{
TeamRepository = teamRepository;
}
public ViewResult Teams(string viewName, string masterName, object model)
{
return View("~/Views/Teams.aspx", TeamRepository.Team.ToList());
}
}
Then Soccer Controller:
public class SoccerController<T> : TeamController<T> where T : class
{
public SoccerController(ITeamRepository<T> teamRepository) : base(teamRepository)
{
}
}
Ninject:
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel = new StandardKernel(new MyService());
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType == null) return null;
return (IController) _kernel.Get(controllerType);
}
private class MyService : NinjectModule
{
public override void Load()
{
Bind<ITeamRepository<SoccerTeam>>().To<TeamRepository<SoccerTeam>>()
.WhenInjectedInto(typeof(SoccerController<SoccerTeam>))
.WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["dbCon"].ConnectionString);
}
}
}
Now when I hit localhost/soccer/teams I get an error stating that NinjectControllerFactory did not return a controller for the name 'soccer'. What am I missing?
Thanks in advance!
.
You want:
public class SoccerController : TeamController<SoccerTeam>
{
public SoccerController(ITeamRepository<SoccerTeam> teamRepository) : base(teamRepository)
{
}
}
and
Bind(typeof(ITeamRepository<>)).To(typeof(TeamRepository<>))
.WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["dbCon"].ConnectionString);
Do you also have the following method (or something similar) in the controller factory you created to replace the default controller factory?
protected override IController GetControllerInstance(RequestContext context, Type controllerType) {
if (controllerType == null) return null;
return ((IController)_kernel.Get(controllerType));
}
And _kernel is a private member variable of that controller factory (implements IKernel).

Resources