How do I restrict access to some actions in MVC5? - c#

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
}

Related

Implicitly declaring a parameter in an ASP.NET MVC action

If I have this action:
public ActionResult MyAction(long? personId)
{
// ...
}
I can call that action with this URL:
localhost/MyAction/?personId=x
I want to be able to send another extra parameter (token), which is going to be in all of my ajax calls from the client:
localhost/MyAction/?personId=x&token=asdf
Without having to declare this new parameter in all of my action signatures. That it: I want to avoid this:
public ActionResult MyAction(long? personId, string token)
{
// ...
}
public ActionResult MyAction2(long? animalId, string token)
{
// ...
}
etc.
But I also want to be able to have access to the token parameter from inside the method. That is:
public ActionResult MyAction(long? personId)
{
// I can check the token's value or manipulate it, eg:
if (token.Equals(...)) { .. }
}
Question:
Is there a way to declare this parameter implicitly in all (or some) of my actions? Maybe using attributes?
You could derive all your controllers from a common base controller.
Then, override OnActionExecuting in that and access the Querystring. For example:
public class BaseController : Controller
{
protected string Token { get; private set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
string token = Request.QueryString["token"] as string;
Token = token ?? string.Empty;
}
}
I find that a cleaner solution than a static class. Also, the BaseController is a useful pattern in MVC to share code that is used in many controllers.
public class Token
{
public static string Value
{
get
{
return System.Web.HttpContext.Current.Request["token"];
}
}
}
public ActionResult MyAction(long? personId)
{
// I can check the token's value or manipulate it, eg:
if (Token.Value.Equals(...)) { .. }
}
Do not forget to account for Token.Value being null.

Sessions in Asp.NET MVC 4

Can anybody please tell me how to handle sessions in asp.net MVC 4. I am aware about this Session variable and I know how to use it.
Session["login"] = true; //We can use it in controller to check the whether user logged in or not.
Above code snippet is enough to handle sessions on small web application. But, what if I have many controllers and actions and I am working on a large application, In this case I cant use session variable in each action.
Is there is any generic place where I can check my session variables or any other solution ?
1st Way:
I used to write a Base Controller class and all other Controllers inherit from it that need to authenticated before access:
public class DefaultController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session["User"] == null)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 403;
filterContext.Result = new JsonResult { Data = "LogOut", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
filterContext.Result = RedirectToAction("Login", "Account");
}
else
{
//base.Execute(filterContext.RequestContext);
}
}
}
and inherit from Base Controller in the ones for which user must be logged in:
public class LeaveController : DefaultController
{
}
Another way is to write your own authorizaion attribute.
See Filter and Attributes in asp.net mvc
2nd Way:
Here is sample for custom filter attribute, create class which inherits from ActionFilterAttribute:
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session["someValueYouLookFor"] == null)
{
filterContext.Result = new RedirectResult("~/Home/Index"); // redirect to login action
}
else
{
// continue normal execution
}
}
}
and put it on Controller or Action:
[SessionTimeout]
public ActionResult Index()
{
}
Definitely, you can use Authentication filter if you're using MVC 5.
for simplest way, you can have a baseController, and all other controller should inherit that controller, and in baseController you can override that OnActionExecuting event, to verify if session is there or not.
for ex.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Convert.ToBoolean(Session["login"]))
{
//Authenticated
}
else
{
//Kick to login page
}
}
All other controller should inherit this baseController
public class HomeController : BaseController
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
return View();
}
}
This way, before your action method start executing, it will be verified through baseController's OnActionExecuting event.

Is it possible to disable authentication Filter on one action in an MVC 5 controller?

[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();
}
}

Passing an optional object to all views

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.

How to use Custom AuthorizeAttribute for controller utilizing parameter value?

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.

Categories

Resources