So, basically what I'd like is to do something like:
#if(Notification!=null){
//perform javascript notification with #Notification.Text
}
And I'd like to be able to do this on any view, so I will always have the option of specifying a notification object in my controller action, that, if defined, can be handled in the view.
My dream scenario is to allow this simply by creating the Notification object somehow, and then just returning the view. Meaning, I wouldn't need to explicitly pass the Notification object to the model. Like so:
public ActionResult MyAction(){
Notification n = new Notification("Text for javascript");
return View();
}
I'm thinking, that there might be a way to do this with some ViewPage-inheritance? But I'm really unsure of how to go about this?
In an ideal world, I would also love to be able to "override" what to do. For example, if I in my 'top'-layout choose to perform a certain kind of jquery-notification if the notification object exists, but maybe in some other nested view would like to handle it differently, I'd like the option to override the top-layouts handling of the object.
I know this last thing might be a little utopian (I'm just starting out with MVC and Razor), but it would be cool :)
You could write a custom global action filter which will inject this information on all views. For example:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.Controller.ViewBag.Notification = new Notification("Text for javascript");
}
}
and then register this filter in the RegisterGlobalFilters method of your Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new MyActionFilterAttribute());
}
And then in your views:
#if(ViewBag.Notification != null)
{
//perform javascript notification with #ViewBag.Notification.Text
}
Use ViewBag for simple stuff like popup message.
public ActionResult Index()
{
ViewBag.PopupMessage = "Hello!";
return View();
}
and then in view (or layout page)
#if (ViewBag.PopupMessage != null)
{
<div class="popup">#ViewBag.PopupMessage</div>
}
For more complicated stuff you will need to either create static class and save/read from HttpContext.Current.Items or override Controller and WebViewPage and save/read from ViewBag/ViewData.
Update:
public abstract class BaseController : Controller
{
public const string NotificationKey = "_notification";
protected string Notification
{
get
{
return ViewData[NotificationKey] as string;
}
set
{
ViewData[NotificationKey] = value;
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
protected string Notification
{
get
{
return ViewData[BaseController.NotificationKey] as string;
}
}
}
Views/Web.config
<pages pageBaseType="OverrideTest.Framework.BaseViewPage">
Usage:
public class HomeController : BaseController
{
public ActionResult Index()
{
Notification = "Hello from index!";
return View();
}
}
<div>Notification: #(Notification ?? "(null)")</div>
Or get test project here
Update 2:
Check out this blog post for another way to do something similar.
Related
Suppose I have a Controller with 100's of ActionResult's Like Below
public class BasicController : Controller
{
public ActionResult Apple1()
{
}
public ActionResult Apple2()
{
}
.
.
.
public ActionResult Apple100()
{
}
public ActionResult Mango1()
{
}
public ActionResult Mango2()
{
}
.
.
.
public ActionResult Mango100()
{
}
}
Now How can I set [Authorize(Role="AppleAdmin")] to only Apple named methods and [Authorize(Role="MangoAdmin")] Mango named methods?
I know we can do it by decorating each ActionResult individually. But What I want to know is, Is there a way to set the Authorize to a Group of ActionResults once. So that makes me use the Authorize attribute only twice in my above scenario.
Assuming cleaning up the controller isn't an option, the only answer I can think of is a custom AuthorizeAttribute on the controller.
If you override the OnAuthorization method you can inspect the AuthorizationContext argument for the action name (filterContext.ActionDescriptor.ActionName) and set filterContext.Result = new HttpUnauthorizedResult if it fails your logic.
Something like
public class AppleMangoAuthorizeAttribute : AuthorizeAttibute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.ActionDescriptor.ActionName.Contains("Apple") /*&& some other failing logic*/)
{
filterContext.Result = new HttpUnauthorizedResult();
}
else if (/*same for mango*/)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
Then
[AppleMangoAttribute]
public class BasicController : Controller
{
public ActionResult Apple1()
{
}
}
I would separate the controller in 2 classes and just modify the routing to access the 2 controllers via the same route.
Note: Personally I haven't tested if this solution will crash the routing but it's the best I could come up with:
[Authorize(Role="AppleAdmin")]
[Route("BasicController")]
public class BasicControllerApple : Controller
{
public ActionResult Apple1()
{
}
}
[Authorize(Role="MangoAdmin")]
[Route("BasicController")]
public partial class BasicControllerMango : Controller
{
public ActionResult Mango1()
{
}
}
[AuthenticateUser]
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[AllowAnonymous]
public ActionResult List()
{
return View();
}
}
How to remove authentication for action named as List? Please advise....
My Custom Filter coding as follow.. i have inherited the FilterAttribute call as well.
Please advise regarding
public class AuthenticateUserAttribute: FilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext context)
{
if (this.IsAnonymousAction(context))
{
}
if (user == "user")
{
// do nothing
}
else
{
context.Result = new HttpUnauthorizedResult(); // mark unauthorized
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext context)
{
if (context.Result == null || context.Result is HttpUnauthorizedResult)
{
context.Result = new RedirectToRouteResult("Default",
new System.Web.Routing.RouteValueDictionary{
{"controller", "Home"},
{"action", "List"},
{"returnUrl", context.HttpContext.Request.RawUrl}
});
}
}
}
The below code generate the error message : Error 1 The best overloaded method match for 'MVC5Features.Filters.AuthenticateUserAttribute.IsAnonymousAction(System.Web.Mvc.AuthorizationContext)' has some invalid arguments c:\users\kirupananthan.g\documents\visual studio 2013\Projects\MVC5Features\MVC5Features\Filters\AuthenticateUserAttribute.cs 16 17 MVC5Features
Error 2 Argument 1: cannot convert from 'System.Web.Mvc.Filters.AuthenticationContext' to 'System.Web.Mvc.AuthorizationContext' c:\users\kirupananthan.g\documents\visual studio 2013\Projects\MVC5Features\MVC5Features\Filters\AuthenticateUserAttribute.cs 16 40 MVC5Features
if (this.IsAnonymousAction(context))
Since it is your custom filter, you can extend it to handle AllowAnonymous (if you don't want to use AllowAnonymous, yoy can create own f.e. NoAuthentication):
public class AuthenticateUser : IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
if (this.IsAnonymousAction(filterContext))
{
return;
}
// some code
}
private bool IsAnonymousAction(AuthenticationContext filterContext)
{
return filterContext.ActionDescriptor
.GetCustomAttributes(inherit: true)
.OfType<AllowAnonymousAttribute>()
//or any attr. you want
.Any();
}
}
Try the
[AllowAnonymous]
attribute
Maybe if you specify a specific User Group for that action and in your custom authentication filter allow this group for everything.
In MVC 5 and I quote from http://www.dotnetcurry.com/showarticle.aspx?ID=975
The class CustomOverrideAuthorizationAttribute is inherited from the FilterAttribute class and implements IOverrideFilter. This interface is used to define the filters applied on the controller. The property FiltersToOverride returns the IAuthorizationFilter type. This means that Authorize filter applied on the parent (controller or Global application class) will be overridden
I believe you should remove the attribute from the controller and put it on each action method except List.
So, reading the article that #Bilal posted (Oct 30 '14 at 12:24), it seems there's an elegant way to override filters by class (or interface). You'd have to write a custom attribute for each filter that you want to override, but that may not be a huge problem, if you consider that you probably don't want to override many filters, right?
So, in your question you want to override the AutherizationUser attribute, so you'd implement this class:
public class CustomOverrideAuthenticateUserAttribute :
FilterAttribute, IOverrideFilter
{
public Type FiltersToOverride
{
get
{
return typeof(AuthenticateUserAttribute);
}
}
}
And rewrite your controller as:
[AuthenticateUser]
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[CustomOverrideAuthenticateUser]
public ActionResult List()
{
return View();
}
}
I am developing a ASP.NET MVC5 project, with MySQL (More like MariaDB, actually) as the database and EF 6.
I decided not to use the "native" authentication method as it used SQL Server, so... Well. there are a few issues I need to solve.
Say there is a controller called "Persons" with the default EF's CRUDE. Thing is, I want the default logged-in users to access the Details and List Views, but not the Create, Delete and Edit ones. So, according to a few answers in SO, I came up with the "solution" :
Create an abstract class inheriting the Controller class, and defining an overriding method at OnActionExecuting:
public abstract class InternalAreaBaseController: Controller {
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if ( Session["UserObj"] == null ) {
Response.Redirect( #"~\AcessoNegado\Desconectado" );
}
else{
base.OnActionExecuting( filterContext );
}
}
}
I did the inheritance needed in public class PersonsController : InternalAreaBaseController ,and it works:
If the current session does not contain the "UserObj" object, which basically detects whether the user is connected or not, redirects to the "Disconnected" error page.
However, this only checks whether the user is connected; Inside PersonsController, there is the action public ActionResult Index() which can be accessed by all the users, but there is also the action public ActionResult Create() which can only be accessed by users within a determined condition.
Is there a way of passing a flag alongside the PersonController:InternalAreaBaseController so the abstract class knows when to block determined users?
Something like
public abstract class InternalAreaBaseController: Controller {
public int AccessDegree { get; set; }
public User? SessionData = Session["UserObj"];
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if ( SessionData == null ) {
Response.Redirect( #"~\AcessoNegado\Desconectado" );
}
elseif(SessionData.AccessDegree<AccessDegree){
Response.Redirect( #"~\AcessoNegado\SemCredenciais" ); //NotEnoughCredentials
}
else{
base.OnActionExecuting( filterContext );
}
}
}
You can use this way.
public ActionResult Login()
{
bool IsValidUser = // Check User Credential
if(IsValidUser) // If it is valid
{
FormsAuthentication.SetAuthCookie("username", false);
return RedirectToAction("Index");
}
else
{
//Show the message "Invalid username or password"
}
}
In the controller,
[Authorize]
public ActionResult Index()
{
// Do something
}
I am trying to secure a controller action to prevent a user from accessing an Entity that they do not have access to. I am able to do this with the following code.
public ActionResult Entity(string entityCode)
{
if (CurrentUser.VerifyEntityPermission(entityCode))
{
//populate viewModel...
return View(viewModel);
}
return RedirectToAction("NoAccessToEntity", "Error");
}
I would like to be able to add an attribute to the controller action itself. In order to validate the access to the entity, I need to see what value has been passed to the controller and what entities the user has access to. Is this possible?
[EntityAuthRequired]
public ActionResult Entity(string entityCode)
{
//populate viewModel...
return View(viewModel);
}
Something like this might help you on your way. Though you may want to add some additional properties to your attribute to allow you to specify your entityCode parameter on each action, rather than hard-code it.
public class EntityAuthRequired : FilterAttribute, IAuthorizationFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
//Make sure that this is not NULL before assigning value as string...
var entityCode = filterContext.RouteData.Values["entityCode"] as string;
// do your logic...
if (!allowed)
filterContext.Result = new HttpUnauthorizedResult();
}
}
Also, if the entityCode isn't in your RouteData, you can use filterContext.RequestContext.HttpContext.Request to look at the POST data.
I made a new action filter (attribute, similar to [Authorize]) which authorizes access to a controller action based on a session value. However, I'm basically decorating all my controller actions with that attribute (with the exception of very few).
So, I thought it would be better to have that Action Filter always executed except in cases where I attach an [ExemptFromAuthorize] attribute to a controller action? (Maybe via inheriting to my own Controller class?)
How can I do this?
Running with jeef3's answer, I came up with this. It could use more error checking and robustness like multiple delimited actions, but the general idea works.
In your specific case, you could test for the session value and decide to return out of the authorization also.
public class AuthorizeWithExemptionsAttribute : AuthorizeAttribute
{
public string Exemption { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.RouteData.GetRequiredString("action") == Exemption)
return;
base.OnAuthorization(filterContext);
}
}
Usage:
[AuthorizeWithExemptions(Roles="admin", ExemptAction="Index")]
public class AdminController : Controller
...
Check out my article on codeproject -
http://www.codeproject.com/KB/web-security/AuthorizeWithExemptions.aspx
In this article, I'll provide you with a solution for securing ASP.NET MVC application's controllers in a way that all the actions are secured except those you define as unsecure.
snipper from the code:
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionDescriptor action = filterContext.ActionDescriptor;
bool IsUnsecured = action.GetCustomAttributes(
typeof(UnsecuredActionAttribute), true).Count() > 0;
//If doesn't have UnsecuredActionAttribute - then do the authorization
filterContext.HttpContext.SkipAuthorization = IsUnsecured;
base.OnAuthorization(filterContext);
}
I understand the question is pretty outdated but anyway.. If you wish to apply filter to all actions just add following lines into Global.asax:
protected void Application_Start()
{
// your code here and then
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyActionFilterAttribute());
}
And in action filter you can just check if action has any other attributes in following way:
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof(AnotherActionAttribute), false))
{
// do what you want to do
}
}
Maybe try and add an Except property to your first attribute?
[MyAuthenticate(Exempt="View")]
public class MyController : Controller
{
public ActionResult Edit()
{
// Protected
}
public ActionResult View()
{
// Accessible by all
}
}
You can add the attribute to the class to have it apply to all methods in that class
[Authenticate]
public class AccountController : Controller
{
public ActionResult Index()
{
return View();
}
}
I don't know how to exclude a specific method from a class-level attribute. Maybe use a separate controller for unauthenticated requests?
For anyone reading this in 2013+, MVC4 now supports the use of
[AllowAnonymous]
You can put Authorize on the controller, and then Allow Anonymous on
any functions you do not want to authorize.
Example:
[Authorize]
public class HomeController : Controller {
[AllowAnonymous]
public ActionResult Index()
{
}
}
Would this work with a custom [MyAuthorize] filter or does it only work with [Authorize]
For anyone reading this in 2013+, MVC4 now supports the use of [AllowAnonymous]
You can put Authorize on the controller, and then Allow Anonymous on any functions you do not want to authorize.
Example:
[Authorize]
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
}
}