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.
Related
I have a controller that I only want authenticated users to be able to access. Do I have to put a check in each method in my controller to verify a user is authenticated, or is there another way to handle this? Can I use annotations to do this instead?
Example from my controller:
public ActionResult Index()
{
if (UserVerified())
{
...
}
return RedirectToAction("Login", "Account");
}
public ActionResult FacebookLogin()
{
if (UserVerified())
{
....
}
return RedirectToAction("Login", "Account");
}
private bool UserVerified()
{
if (User != null && User.Identity != null && User.Identity.IsAuthenticated)
{
return true;
}
return false;
}
You can use AuthorizeAttribute for it.
Put it to every action.
[Authorize]
public ActionResult Index()
{
}
[Authorize]
public ActionResult FacebookLogin()
{
}
It will do the whole work for you. It checks whether the currect user is authenticated. If he is authenticated - proceeds to the action, if he is not - returns to the home page.
You can also add this attribute to a controller. Then all actions will require authorization.
[Authorize]
public class HomeController
{
public ActionResult Index()
{
}
public ActionResult FacebookLogin()
{
}
}
Update: And, yes, as Kamil said. Read this article, please.
http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api
You spend some time now and will spend much less time having questions about ASP.NET authentication in future.
By the way, you don't need to check for
User != null && User.Identity != null
If you are using default authentication then you can be always sure that User.Identity is a proper object. You can access User.Identity.IsAuthenticated directly.
Using Authorize attribute is way to go (already answered here). In addition, if you may want to implement some other business rules or filtering checks, you can create a filter class inheriting from AuthorizeAttribute.
e.g.
public class CustomAuthorizeFilter: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false; //User not Authorized
}
else
{
//Check your conditions here
}
}
}
Then decorate your controller or Action as:
[CustomAuthorizeFilter]
public class SomeController
{
}
You can either user [Authorize] attribute which is inbuilt. Or you can develop your custom attribute for the same purpose.
You can start from here for your own attribute:
Create custom attribute
If you want to perform validation on each action method, then put that attribute on Controller level rather than each action method.
You can use [Authorize] attribute above controller methods.
Please follow this link
If you want the authentication rules to apply to all controller actions you can do this:
[someAuthAttribute]
public class HomeController : Controller
{
// pseudo
public ActionResult Index() {
return response;
}
public ActionResult FacebookLogin(){
return response;
}
}
Where Index() and FacebookLogin() will adhere to the authentication rules of [someAuthAttribute]. You can also use this "hierarchy" to apply more specific rules to your action methods. Like this:
[someAuthAttribute]
public class HomeController : Controller
{
// pseudo
public ActionResult Index() {
return response;
}
[someFBAuthAttribute]
public ActionResult FacebookLogin(){
return response;
}
}
You can inherit authorization from a base controller.
[Authorize(Roles = #"Domain\Group")]
public class BaseController : Controller
public class ChildController : BaseController
[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
}
So I have some generic actionresults that link to various views for the time being. The layout page contains a call to adfs to populate a logged in user name that has to be for each page. Looks like this:
<div class="float-right">
<section id="login">
Hello, <span class="username">#ViewBag.GivenName #ViewBag.LastName</span>!
</section>
</div>
In the home controller, what makes this logged in name work is this code here:
public ActionResult Index()
{
ClaimsIdentity claimsIdentity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
Claim claimGivenName = claimsIdentity.FindFirst("http://sts.msft.net/user/FirstName");
Claim claimLastName = claimsIdentity.FindFirst("http://sts.msft.net/user/LastName");
if (claimGivenName == null || claimLastName == null)
{
ViewBag.GivenName = "#FAIL";
}
else
{
ViewBag.GivenName = claimGivenName.Value;
ViewBag.LastName = claimLastName.Value;
}
return View();
}
But as mentioned earlier, I need this to display when a user goes to each link (actionresult). Therefore, I am having to post all the code above into each actionresult in order to achieve this.
Is there some way I can have this apply to each actionresult as a whole rather than having to duplicate code from one action to another? I did try just to register into an actionresult for my _Layout.cshtml and make the call to that partialview, but that didn't give me favorable results. I am sure it is something simple that I am missing.
Hoping some of you can help. Thanks much.
We use an abstract controller and override its OnActionExecuting method to execute code before the actual action method gets invoked. With this abstract controller, all you have to do is make any other controllers inherit from it to gain it's functionality. We also use this base controller as a place to define other helper methods that other controllers which extend it can use, such as GetUsernameForAuthenticatedUser().
public abstract class AbstractAuthenticationController : Controller
{
private readonly IAuthenticationService _authService;
protected AbstractAuthenticationController()
{
_authService = AuthenticationServiceFactory.Create();
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
EnsureUserIsAuthenticated();
}
internal void EnsureUserIsAuthenticated()
{
if (!_authService.IsUserAuthenticated())
{
_authService.Login();
}
}
protected string GetUsernameForAuthenticatedUser()
{
var identityName = System.Web.HttpContext.Current.User.Identity.Name;
var username = _authService.GetUsername(identityName);
if (username == null) throw new UsernameNotFoundException("No Username for " + identityName);
return username;
}
}
This functionality could also be implemented in an Attribute class which allows you to decorate your controllers as opposed to using inheritance but the end result is the same. Here is an example of a custom controller attribute implementation.
You could create a base controller and make all the controllers inherit from it. Move the code that sets the given and last names into a separate, protected method and call it whenever you need. I think you could call the function in the Initialize method of the base controller. This way you won't need to call it directly into the actions.
You could also make a hierarchy of models and have GivenName and LastName as properties on the base model, instead of working with the ViewBag.
Another alternative to using OnActionExecuting as this is just for a set part of the template would be to give it its own action method that returns a partial and call #Html.Action()
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.