If the authorize attribute has been applied to the controller is it possible to allow unauthorized access to an action/viewresult inside that controller?
Say for example I didn't want authorization to occur on Test2 in the following:
[Authorize]
public class TestController : Controller
{
public ViewResult Test()
{
return View();
}
public ViewResult Test2()
{
return View();
}
}
Thanks in advance.
No, this is not possible. You will have to apply the Authorize attribute on the Test action and not on the controller. Another possibility is to put the Test2 action on another controller which is not decorated with this attribute.
Back in MVC 3 it appears it was indeed not possible to do (as mentioned Darin Dimitrov), but if anyone using MVC 4 (and up) comes across this question, he\she should be able to use AllowAnonymous filter to achieve the result. So the code would become:
[Authorize]
public class TestController : Controller
{
public ViewResult Test()
{
return View();
}
[AllowAnonymous]
public ViewResult Test2()
{
return View();
}
}
Related
Why can't Web API Core 2 tell these apart?
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values?name=dave
[HttpGet]
public string Get(string name)
{
return $"name is {name}";
}
Here's what happens -
Both http://localhost:65528/api/values and http://localhost:65528/api/values?name=dave cause the first Get() method to execute.
This exact code works fine in Web Api 2.
I know multiple ways of getting around this, but I don't know why it happens.
Can someone explain why this has changed?
I don't think you can even compile your code in ASP.NET Core Mvc 2.0 since you have 2 actions mapped to same route [HttGet] api/values:
AmbiguousActionException: Multiple actions matched.
Remember, ASP.NET Web API uses the HTTP verb as part of the request to figure which action to call. Although it uses conventional routing (you name your actions Get, Post, Put and Delete, etc) if you don't have route attribute specify, I would highly recommend to always use routing attribute to annotate your controllers and actions.
Api Design time
Now it's up to you to design the route, as a developer. Remember the route is supposed to be a Uri that can identify a resource / resources.
Use the name as identifier along with the route
[Route("api/[controller]")]
public class CustomersController : Controller
{
// api/customers
[HttpGet]
public IActionResult Get()
{
...
}
// api/customers/dave
[HttpGet("{name:alpha}")] // constraint as a string
public IActionResult GetByName(string name)
{
...
}
}
Use the name as filter, against the resource collection
[Route("api/[controller]")]
public class CustomersController : Controller
{
// api/customers
// api/customers?name=dave
[HttpGet]
public IActionResult Get(string name)
{
...
}
}
To confuse you more
api/customers/dave will still execute GetById first!
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
[HttpGet("{name}")]
public IActionResult GetByName(string name)
{
...
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
...
}
}
Both methods GetByName and GetById are potential candidates but MVC picks GetById method first because MVC compares the method/template name {name} and {id} through case-insensitive string comparison, and i comes before n.
That's when you want to put constraints.
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
// api/customers/dave
[HttpGet("{name:alpha}")]
public IActionResult GetByName(string name)
{
...
}
// api/customers/3
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
...
}
}
You can also specify the Ordering too!
[Route("api/[controller]")]
public class CustomersController : Controller
{
[HttpGet]
public IActionResult Get()
{
...
}
// api/customers/portland
[HttpGet("{city:alpha}", Order = 2)]
public IActionResult GetByCity(string city)
{
...
}
// api/customers/dave
[HttpGet("{name:alpha}", Order = 1)]
public IActionResult GetByName(string name)
{
...
}
// api/customers/3
[HttpGet("{id:int}")]
public IActionResult GetById(int id)
{
...
}
}
Without the Order, the method GetByCity will be in favor than GetByName because character c of {city} comes before the character n of {name}. But if you specify the order, MVC will pick the action based on the Order.
Sigh the post is too long....
Because in your case the best match in the route pipeline is the default httpget attribute (the one that get all). The query is a regular string so if you don't ask what you want from the query the best match is still the one that get all.
[HttpGet]
public string Get([FromQuery]string name)
{
return $"name is {name}";
}
The [FromQuery] is pointing to the key "name" in the query string to get the value.
Your should read Routing in asp.net core
I have a Home Controller with these actions:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
public ActionResult Logoff()
{
HttpContext.Session.Remove("LogonTicket");
HttpContext.Session.Remove("PID");
return View("Index");
}
Now, when I logoff using the Logoff Action, I want the Authorize attribute of the Index to take effect but it doesn't when I return the Index View in the Logoff Action.
How would I handle this?
I'm using a custom MembershipProvider and not sure how to put Logoff functionality in it.
You should redirect, not return the view directly:
public ActionResult Logoff()
{
HttpContext.Session.Remove("LogonTicket");
HttpContext.Session.Remove("PID");
return RedirectToAction("Index");
}
Returning the view directly sends the view's markup to the client right away and the Authorize filter does not get invoked at all.
Done in the AccountController Logoff Action. They are public "globals" in a base controller now, too.
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
LogonTicket = null;
ParticipantID = null;
return RedirectToAction("Index", "Home");
}
Is There one way to make a [Authorize] attibute be ignored in one action in a controller class that has a Authorize attribute?
[Authorize]
public class MyController : Controller
{
[Authorize(Users="?")]//I tried to do that and with "*", but unsuccessfuly,
public ActionResult PublicMethod()
{
//some code
}
public ActionResult PrivateMethod()
{
//some code
}
}
Just the PrivateMethod() should have authentication required, but it has been required too.
PS: I wouldn't like to make my custom authorize filter.
[]'s
You can use [AllowAnonymous]
[Authorize]
public class MyController : Controller
{
[AllowAnonymous]
public ActionResult PublicMethod()
{
//some code
}
public ActionResult PrivateMethod()
{
//some code
}
}
By default it's impossible - if you set [Authorize] for controller then only authenticated user can access to action.
or
You can try custom decisions: stackoverflow.
A solution is in this article: Securing your ASP.NET MVC 3 Application
The article talks about a white list approach where you decorate actions with a AllowAnonymous custom attribute. It requires that you extend AuthorizeAttribute and the OnAuthorization method to skip authorization checks of AllowAnonymous -actions. (The approach is credited to Levi, a security expert on the MVC team.)
public class MyController : Controller
{
[Authorize] //it will only work for the following action
public ActionResult PublicMethod()
{
//some code
}
public ActionResult PrivateMethod() //[Authorize] will not work for this action
{
//some code
}
}
Just for future reference This is now available to be done by the the [AllowAnonymous] attribute in ASP.NET MVC 4.
More Info
is it possible to switch request validation in the view.
I would like to switch it off in the view because of this bit of code in the base controller
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
if (!this.CurrentStore.IsDefault)
{
IStoreRepository storeRepo = ObjectFactory.GetInstance<IStoreRepository>();
IStoreEntity store = storeRepo.GetById(this.CurrentStore.Id);
this.CurrentStore = store;
}
base.Execute(requestContext);
}
it fails in base.Execute(requestContext);
N.B I do not wish to switch it off for my entire site. I would like to switch it off in a few action methods.
In MVC you don't do it at the view level, you do it at the controller or a method of the controller level. You can use ValidateInput attribute for that, for example:
public class HomeController : Controller
{
[ValidateInput(false)] // prevent validation on this method
public ActionResult Index()
{
return View();
}
}
I would like to catch the AmbiguousMatchException whenever it is thrown and then write some code to resolve it. For example, I have an action ChangePassword which should be called only if the user is logged in. I have another method RenewPassword which must be called if the user is not logged in. I have given the same action name to both these methods.
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
[ActionName("ChangePassword")]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}
I want to use the same action name because I do not want the view to have to worry about which action to call. I know that I can write a custom FilterAttribute which will do the reverse of what AuthorizeAttribute does, apply it to the RenewPassword method, and thereby resolve the ambiguity. However, this seems like too much work for a very simple need.
Any better ideas? Is there a built in way to say that a particular action should be executed only for anonymous users and not for logged in users?
If you don't views having to worry about which action to call why not writing a reusable HTML helper:
public static class HtmlExtensions
{
public static MvcForm BeginChangePasswordForm(this HtmlHelper htmlHelper)
{
if (htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated)
{
return htmlHelper.BeginForm("ChangePassword", "SomeController");
}
return htmlHelper.BeginForm("RenewPassword", "SomeController");
}
}
and inside your view:
#using (Html.BeginChangePasswordForm())
{
...
}
and in the corresponding controller:
[HttpPost]
public ActionResult RenewPassword(ChangePasswordModel model)
{
...
}
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
...
}