I have users that have one of those roles:
RoleA (Attribute: AuthorizeRoleA)
RoleB (Attribute: AuthorizeRoleB)
In my controller I want to say this:
Everyone that has role of type RoleA
can access all the methods in this controller
[AuthorizeRoleA]
public class HomeController : Controller
{
public ActionResult MethodOne()
{
return View();
}
public ActionResult MethodTwo()
{
return View();
}
//****** Make an exception ********
//So in this case, let RoleA here, but let RoleB too.
[AuthorizeRoleB]
public ActionResult MethodThree()
{
return View();
}
}
And I have another controller:
Everyone that has role of type RoleB
can access all the methods in this controller
Just RoleB! No one else.
[AuthorizeRoleB]
public class AnotherController : Controller
{
public ActionResult Index()
{
return View();
}
}
So, this should be similar with the Authorize attribute when is used to decorate the controller, and the AllowAnonymous when is used inside the same controller, but I don't know how to achieve this behavior with custom attributes(filters).
My goal is to create custom attributes, where I can say:
In AuthorizeRoleA will be included n-roles
and in AuthorizeRoleB will be included n-other roles.
But AuthorizeRoleA will have highest priority than the other attributes.
Note: Maybe this is a duplicate, but I didn't find anything similar to this question.
Related
I have been searching for similar solutions online but everything seems overcomplicating, currently, I have a UserController that I only want users that are logged in to access, my current solution involves using if statements however I was wondering if it's possible to use the [Authorize] attribute and apply it to methods or the entire controller perhaps?
public class UserController : ASessionController {
public UserController (IAmazonDynamoDB dynamoDbClient, DynamoDBContext dynamoDbContext) : base(dynamoDbClient, dynamoDbContext) {
}
// [Authorize]
public IActionResult Index () {
// check session variable
if(!UserIsLoggedIn()){ /*redirect to sign in*/ }
return View();
}
}
Perhaps I am not understanding if this is the purpose of the Authorize attribute? Thank you in advance.
You can use the Authorize attribute on endpoints and / or the Controller itself
It will force the User to be authenticated to again access to the decorated item.
In addition you can also restrict it to authenticated users with a given or multiple roles like in the example
[Authorize(Roles = "Administrator")]
public IActionResult Index()
{
...
}
[Authorize(Roles = "Administrator,Guest")]
public IActionResult NotAnIIndex()
{
...
}
But you should read the Microsoft Documentation tuturial
I want to get the below roles(Admin,IT,..) from the database without hard coding on top of the action result. Please provide any help.
[Authorize(Roles = "Admin,IT")]
public ActionResult Index()
{
}
There aren't any super-easy ways to do this. You can apply the [Authorize] attribute to a controller instead of an action, but it is still "hard-coding" it.
You could create a custom Authorization attribute ([link])1, but you would have to store the Routing values in the database, as well as the Roles that were allowed to access the route. However this just shifts the burden of making manual changes into the database from the code.
I don't really think that this should really be considered "Hard Coding" as you have to declare your authorization somewhere, and you can still have different users with different permissions in different environments. Who else but the developer should know best which routes require which authorization? Would you want to break your access control because you changed the routing somewhere?
create an Action finter
public class ValidationPermission : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(System.Web.HttpContext.Current.Session["UserName"] == null)
System.Web.HttpContext.Current.Response.RedirectToRoute("Login");
else{
// code check CheckPermission
}
}
}
Action controller
[ValidationPermission(Action = ActionEnum.Read, Module = CModule)]
public ActionResult Index()
{
// something code
}
You can try with this way
public static Role {
public static string Admin ="Admin";
public static string IT ="IT";
}
[Authorize(Roles = Role.Admin,Role.IT)]
public ActionResult Index()
{
}
In one of the controllers, every view has a fixed preprocessing. Is there a better way of doing this, instead of the below code; such that SomeFunctionAsync works without writing that line before return View() for all functions with return View() in this controller? I also have some ajax post functions.
public async Task<ActionResult> View1()
{
await SomeFunctionAsync();
return View();
}
public async Task<ActionResult> View2()
{
await SomeFunctionAsync();
return View();
}
In other words, at the end I want to be able to write the following with having the same effect
public async Task<ActionResult> View1()
{
return View();
}
public async Task<ActionResult> View2()
{
return View();
}
If Action Filter as suggested by Varun doesnt suits you, you can try another way.
Create a parent View of all the view. In the action method for your parent view. Call this Method SomeFunctionAsync(). Thus, parent view will be called for all of yours views and the required method will be executed
You can create a base class for your controller, and have have each view in your code call a generic method. I use GetView as a method name (or you could override the existing ones).
Like so:
public class MyControllerBase : Controller {
public Task<ActionResult> GetView() {
yourCommonMethod();
return View();
}
public Task<ActionResult> GetView(string viewName) {
yourCommonMethod();
return View(viewName);
}
public Task<ActionResult> GetView(object model) {
yourCommonMethod();
return View(model);
}
public Task<ActionResult> GetView(string viewName, object model) {
yourCommonMethod();
return View(viewName, model);
}
}
Then in your controller, inherit from that:
public class MyController : MyControllerBase {
public async Task<ActionResult> View1()
{
return GetView();
}
}
If the common method is the same for all controllers and has no controller-specific dependencies, it could be that simple. However, you may want to look at using generics as well:
public class MyControllerBase<T> : Controller {
// base class stuff here based on type T's interface
}
public class MyController : MyControllerBase<MyController> {
// regular class here, sending MyController to the base
}
These are pretty much building blocks of OOP. You may do well to get a book that covers the basics of OOP and work through this type of stuff.
There are tow ways :
Use a single Action with different views like return View("View1") or retrun View("View2"), you can make if else conditions there so you will call your function at a single place.
If you want to go with your current procedure(not recommended) then you have to use Action Filter attribute and decorate it on Controller level then every action would execute your logic before execution of your Action
I have implemented my own custom Authorize attribute.
The attribute is applied both at the controller level and at the action level.
Here is an example of what I need to do:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
Currently if a user has the Administrator Role but not the AdvancedUsers role, he cannot execute "Administrative Task".
How can I change this behavior to perform a security check at the action level even if the user is not authorized at the controller level?
For the moment, the only solution I can think about is to implement 2 attributes: one for securing controllers, another for securing actions. Then I would play with the Order property to execute the one at the action level first.
However, I would prefer a solution with a single attribute if possible.
Use built-in [OverrideAuthorization]:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[OverrideAuthorization]
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
OverrideAuthorization Attribute is available for MVC 5 (at least) and up. Once you decorate the Action with it, also decorate with the new Role and that will take effect over the Controller level Role.
This should not be possible. Imagine the logic which MVC uses with the authorization filters.
When the controller is determined - check if there is an authorization filter that applies to that controller and execute it.
When the action is known - do the same for the action.
In all cases a fail in authorization would short-circuit the pipeline.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions.
When you mark an action method with the Authorize attribute, access to that action method is restricted to users who are both authenticated and authorized.
//[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
{
[ClaimsAuthorize(Roles ="Administrators", "Role2","Role3")]
public ActionResult AdministrativeTask()
{
return View();
}
}
OR you can override your authorization at controller level ,
Create a new OverrideAuthorizeAttribute attribute.
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
and you can use this attribute to override your controller level autorization.
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
[OverrideAuthorizeAttribute(Roles ="xxxx")] // This role will override controller
//level authorization
public ActionResult SomeOtherAction()
{
return View();
}
}
You need two authorization attributes - a base one with all authorization logic, and a second one, derived from the base attribute, that is only used to override the base attribute.
Example authorization attributes:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
protected bool _canOverride = true;
//...custom authorization code goes here.....
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Don't authorize if the override attribute exists
if (_canOverride && actionContext.ActionDescriptor.GetCustomAttributes<OverrideClaimsAuthorizeAttribute>().Any())
{
return;
}
base.OnAuthorization(actionContext);
}
}
public class OverrideClaimsAuthorizeAttribute : ClaimsAuthorizeAttribute
{
public OverrideClaimsAuthorizeAttribute ()
: base()
{
_canOverride = false;
}
}
In the base authorization attribute we are saying to go ahead and authorize as normal, as long as the OverrideClaimsAuthorizeAttribute doesn't exist. If the OverrideClaimsAuthorizeAttribute does exist, then only run the authorization on classes where _canOverride is false (ie the OverrideClaimsAuthorizeAttribute class itself).
Example usage:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
//Ignores the controller authorization and authorizes with Roles=Administrators
[OverrideClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
//Runs both the controller and action authorization, so authorizes with Roles=Administrators AND Roles=AdvancedUsers
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdvancedAdministrativeTask()
{
return View();
}
//authorizes with controller authorization: Roles=AdvancedUsers
public ActionResult SomeOtherAction()
{
return View();
}
}
Check this previous question. (check #AndyBrown answer, case 2)
For a simple way you might also try adding (
[AllowAnonymous]) to override the controller
[Authorize]
then add a new custom filter to check for your logic for this particular action. Or you can add the code that checks for the role just inside it.
This question already has answers here:
MVC [HttpPost/HttpGet] for Action
(4 answers)
Closed 2 years ago.
Why is this incorrect?
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
}
How can I have a controlller thas answer one thing when is "getted" and one when is "posted"?
Since you cannot have two methods with the same name and signature you have to use the ActionName attribute:
[HttpGet]
public ActionResult Index()
{
// your code
return View();
}
[HttpPost]
[ActionName("Index")]
public ActionResult IndexPost()
{
// your code
return View();
}
Also see "How a Method Becomes An Action"
While ASP.NET MVC will allow you to have two actions with the same name, .NET won't allow you to have two methods with the same signature - i.e. the same name and parameters.
You will need to name the methods differently use the ActionName attribute to tell ASP.NET MVC that they're actually the same action.
That said, if you're talking about a GET and a POST, this problem will likely go away, as the POST action will take more parameters than the GET and therefore be distinguishable.
So, you need either:
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost, ActionName("ActionName")]
public ActionResult ActionNamePost() {...}
Or,
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost]
public ActionResult ActionName(string aParameter) {...}
I like to accept a form post for my POST actions, even if I don't need it. For me it just feels like the right thing to do as you're supposedly posting something.
public class HomeController : Controller
{
public ActionResult Index()
{
//Code...
return View();
}
[HttpPost]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
}
To answer your specific question, you cannot have two methods with the same name and the same arguments in a single class; using the HttpGet and HttpPost attributes doesn't distinguish the methods.
To address this, I'd typically include the view model for the form you're posting:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index(formViewModel model)
{
do work on model --
return View();
}
}
You received the good answer to this question, but I want to add my two cents. You could use one method and process requests according to request type:
public ActionResult Index()
{
if("GET"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for GET
}
else if("POST"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for POST
}
else
{
//exception
}
return View();
}
Can not multi action same name and same parameter
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(int id)
{
return View();
}
althought int id is not used
You can't have multiple actions with the same name. You could add a parameter to one method and that would be valid. For example:
public ActionResult Index(int i)
{
Some Code--Some Code---Some Code
return View();
}
There are a few ways to do to have actions that differ only by request verb. My favorite and, I think, the easiest to implement is to use the AttributeRouting package. Once installed simply add an attribute to your method as follows:
[GET("Resources")]
public ActionResult Index()
{
return View();
}
[POST("Resources")]
public ActionResult Create()
{
return RedirectToAction("Index");
}
In the above example the methods have different names but the action name in both cases is "Resources". The only difference is the request verb.
The package can be installed using NuGet like this:
PM> Install-Package AttributeRouting
If you don't want the dependency on the AttributeRouting packages you could do this by writing a custom action selector attribute.
Today I was checking some resources about the same question and I got an example very interesting.
It is possible to call the same method by GET and POST protocol, but you need to overload the parameters like that:
#using (Ajax.BeginForm("Index", "MyController", ajaxOptions, new { #id = "form-consulta" }))
{
//code
}
The action:
[ActionName("Index")]
public async Task<ActionResult> IndexAsync(MyModel model)
{
//code
}
By default a method without explicit protocol is GET, but in that case there is a declared parameter which allows the method works like a POST.
When GET is executed the parameter does not matter, but when POST is executed the parameter is required on your request.