I have a bunch of controllers and related views that need to have role based authentication applied on them. I am thinking of having a base controller with the [authorize] property definition on it so that I can have all controllers that inherit from that base class be available only after login. I have tested this to be working. I am not sure if this is the best practice or if there will be any pit falls going ahead in this approach.
In the future I will need to have certain pages be accessible to only users within a particular role. The list of roles will be from a database table. so instead of changing all the related controllers I just make that change in the base controller that it inherits from. Is this the right way to go about doing it?
Thanks for your time.
You can combine any number of Authorize attributes.
i.e. You can have a Authorize attribute on your base controller and a more specific one on another controller (for instance specifying a role) and a most specific one on a controller action (specifying a role or a user)
[Authorize]
public class BaseController : Controller
{}
[Authorize(Roles="Administrator")]
public class AdminController : BaseController
{
[Authorize(Roles="SuperUser")]
public ActionResult SuperSecret()
{}
}
It will check all attributes and only revoke access if any of the attributes fail.
In the future I will need to have certain pages be accessible to only users within a particular role.
That's how role based authentication works.
The list of roles will be from a database table.
Load the roles into a custom IPrincipal in the method OnPostAuthenticate in global.asax.
so instead of changing all the related controllers I just make that change in the base controller that it inherits from.
I don't follow you on this requirement. Do you want to avoid specifying roles on your controllers?
It's all right to use a base controller class for your controllers.
However, I don't think you should tie your controllers' inheritance to your roles hierarchy. It doesn't seem clean to me.
I'd implement an inheritance tree of attributes, like:
class NormalUserRolesAttribute: AuthorizeAttribute
class AdvancedUserRolesAttribute: AuthorizeAttribute
class AdminUserRolesAttribute: AuthorizeAttribute
with slightly different OnAuthorization behavior and then mark your controllers with those attributes.
Related
I have been writing ASP.NET MVC for more then 10 years. I always know there is a convention for the Controller class must have it's name suffix with Controller. As I know, it's a hard limit on ASP.NET MVC 5 and before.
One day I saw one of my colleague write a controller in ASP.NET Core 6 like this:
using Microsoft.AspNetCore.Mvc;
namespace WebApplication2.Controllers;
public class Home : Controller
{
private readonly ILogger<Home> _logger;
public Home(ILogger<Home> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
}
I shocked. I can't believe there is someone would write Controller code like this. Then I see this:
A controller is an instantiable class, usually public, in which at least one of the following conditions is true:
The class name is suffixed with Controller.
The class inherits from a class whose name is suffixed with Controller.
The [Controller] attribute is applied to the class.
Then I know the Controller suffix is not a MUST. It's just a recommended naming rule. Without Controller-suffix, it might come with come drawback like this. I also found this post. I always believe naming controller with -Controller suffix is a good practices. I just want to know that if anyone know why ASP.NET Core teams decided not have to be naming controllers with Controller suffix?
why the hard limit had been removed
I think the reason may as below:
From this answer we see
By not having the controller naming convention, we'd have two types
with the same name hence would always have to provide namespaces to
distinguish between the two. But because we do have this convention
this is not necessary and no type clashes occur.
By having Controller suffix on controller types, this is not necessary.
In ASP.NET Core the namespace of the controller class is unimportant, although the tradition is maintained by tooling that continues to place controller classes always under a folder named Controllers. In fact, you can now place your controller classes in any folders and any namespaces that you wish. As long as at least one of the following conditions is true.
Besides,controllers need no Controller type name appending because after all they're just like any other class.It is a form of optimization that reduces overhead and the memory footprint.
often we attach role with action like below way
[Authorize(Roles = "Admin, SuperUser")]
[Authorize(Users="Jacquo, Steve", Roles="Admin, SuperUser")]
Users : Comma-separated list of usernames that are allowed to access the action method.
Roles : Comma-separated list of role names. To Access the action method, users must be in at least one of these roles.
[Authorize(Roles = "Producer")]
[Authorize(Roles = "Admin")]
public ActionResult Details(int id) {
// Only available to users who are Producers AND Editors
}
now see authorize and role name is hard coded with action method. suppose action Details is associated with admin role which is hard coded but how could i attach more role to details action or remove any role from details action at run time. i guess it is not possible because asp.net mvc not providing anything built in.
i search google to see that anyone does it anything such as what i am looking for. unfortunately found no similar write up.
so i need some guidance that how could i develop a UI from where admin can associate role with action instead of hard coding at development time.
so tell me your think how could i associate a role or multiple roles with action from a custom UI.
also tell me how could i check at run time that user has that role when user try to access a specific action.
please discuss in details for designing this part what i am looking for. still it is not clear to you what i am looking for then tell me i will try to explain the same in more details.
thanks
First: in every controller you should create a user object in controller constructor. like this:
public class MyController : Controller
{
ApplicationUser user;
public MyController ()
{
user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
}
Then you could use user object anywhere in your controller methods.
"how could i develop a UI from where admin can associate role": in razor view use :
#if (User.IsInRole("Admin"))
{}
"how could i associate a role or multiple roles with action from a custom UI":
No need to that. When you create user in controller, you have access user all roles in controller methods.
"how could i check at run time that user has that role":
Use
if(user.IsInRole("Admin"))
I am currently hard coding the authorized roles in the filter in my MVC applications like so:
[Authorize(Roles = "Administrator,Manager")]
I'd like to eventually have a way to map the roles to each controller, so that the site admin can handle assigning what roles can perform each set of actions.
string roles = DoSomethingToGetAllowableRoles(controllerName);
[Authorize(Roles = roles)]
I'm imagining that I need to have a database table that somehow keeps a listing of each controller, and then another table mapping the controllers to the roles. What I'd like is a page where I can list out each controller and then have a set of check boxes that lists each role that applies to that controller.
Anyone have an example or can lead me in a direction that will accomplish this?
You're going to need to write your own authorization filter (probably by extending the built in one).
The reason for this is that you can't assign attribute parameters dynamically like that.
You won't need to mess with the MVC source code - you just need to create a class which inherits from System.Web.Mvc.AuthrorizeAttribute, override AuthorizeCore, and then use your attribute in place of the default:
public class CustomAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// Put your custom logic here, returning true for success and false for failure,
// or return base.AuthorizeCore(httpContext) to defer to the base implementation
}
}
Let's have an oject structure like this :
Company -> Department -> Person
-> means 1:M relation
Should I have one fat controller which serves all request for this objects or one controller per every object ?
There will be the usual CRUD operations for each object. The details (Department,Person) will be displayed on one page using grids.
If its like an admin type thing, I would just use a single controller or use dynamic data. Otherwise I like to breakdown my controllers based on behavior for example, Room Booking, Time-sheet management etc.
Following DDD - if only Company is aggregate root - then one (but still thin) controller
I don't think there is a direct relationship between your data model and your controllers. I view controllers as groupings of related functionality, from the prespective of the application. What I mean is that I typically create a controller for each big logical chunck of functionality. So if I were creating a contacts MVC web app, I might have:
AccountController (for authentication/authorization)
ContactsController (for the real business case, contacts)
AdminController (for super users to modify the app)
By in my data model I might have tables like, User, Contact, Address, Phone, etc.
I would have a separate controller for each. If one controller (like company) has to pull people information that is fine.
If you really want Render Action. This lets you view call into another controller to load that part of the view.
I usually go with one controller per object per CRUD ... so one controller for Department and one for Person. Why?
You should either have a DTO or Repository for each. If you have a repository:
public class PersonController : Controller
{
private IPersonRepository _personRepository;
public PersonController(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
}
Then use an IoC container (I like StructureMap) for the dependencies.
Let’s say I'm developing a helpdesk application that will be used by multiple departments. Every URL in the application will include a key indicating the specific department. The key will always be the first parameter of every action in the system. For example
http://helpdesk/HR/Members
http://helpdesk/HR/Members/PeterParker
http://helpdesk/HR/Categories
http://helpdesk/Finance/Members
http://helpdesk/Finance/Members/BruceWayne
http://helpdesk/Finance/Categories
The problem is that in each action on each request, I have to take this parameter and then retrieve the Helpdesk Department model from the repository based on that key. From that model I can retrieve the list of members, categories etc., which is different for each Helpdesk Department. This obviously violates DRY.
My question is, how can I create a base controller, which does this for me so that the particular Helpdesk Department specified in the URL is available to all derived controllers, and I can just focus on the actions?
I have a similar scenario in one of my projects, and I'd tend to use a ModelBinder rather than using a separate inheritance hierarchy. You can make a ModelBinder attribute to fetch the entity/entites from the RouteData:
public class HelpdeskDepartmentBinder : CustomModelBinderAttribute, IModelBinder {
public override IModelBinder GetBinder() {
return this;
}
public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState) {
//... extract appropriate value from RouteData and fetch corresponding entity from database.
}
}
...then you can use it to make the HelpdeskDepartment available to all your actions:
public class MyController : Controller {
public ActionResult Index([HelpdeskDepartmentBinder] HelpdeskDepartment department) {
return View();
}
}
Disclaimer: I'm currently running MVC Preview 5, so some of this may be new.
The best-practices way: Just implement a static utility class that provides a method that does the model look-up, taking the RouteData from the action as a parameter. Then, call this method from all actions that require the model.
The kludgy way, for only if every single action in every single controller needs the model, and you really don't want to have an extra method call in your actions: In your Controller-implementing-base-class, override ExecuteCore(), use the RouteData to populate the model, then call the base.ExecuteCore().
You can create a base controller class via normal C# inheritance:
public abstract class BaseController : Controller
{
}
public class DerivedController : BaseController
{
}
You can use this base class only for controllers which require a department. You do not have to do anything special to instantiate a derived controller.
Technically, this works fine. There is some risk from a design point of view, however. If, as you say, all of your controllers will require a department, this is fine. If only some of them will require a department, it might still be fine. But if some controllers require a department, and other controllers require some other inherited behavior, and both subsets intersect, then you could find yourself in a multiple inheritance problem. This would suggest that inheritance would not be the best design to solve your stated problem.