In my ASP.NET MVC site, my set up allows users to have roles, and roles have permissions. Generally, these permissions are set for a controller. In my site's main navigational menu, an Authenticated user can see all items, even if they aren't authorized to access that page.
Currently I can only configure the menu based off if the user is authenticated:
#if (Request.IsAuthenticated){ }
I'm wondering, what's the best way to pass the user's permissions to a view, only for the sake of configuring the menu for that user? Is there some common way of doing it, or will I have to implement this myself? I haven't found much information on it, but maybe I'm using the wrong search terms.
Thanks for any advice.
EDIT
Sorry I may not have been clear enough. This is my main nav menu, in the _Layout page. Also, permissions assigned to a role are very configurable by an admin (they can also create and delete roles), so checking if the user is in a role won't meet my needs.
You could create an action in say, the CommonController, which returns a partial view containing your navigation. This partial view can have its own model which can be populated from the controller. This allows you to use dependency injection for instance.
The action could look like this:
[ChildActionOnly]
public ActionResult Navigation()
{
var model = new NavigationModel();
// populate the model..
return PartialView("_Navigation", model);
}
You can render this partial in your view (_Layout.cshtml in your case) like this:
#Html.Action("Navigation", "Common")
For most cases, Request.IsAuthenticated is just fine. Only use this if you need something more advanced.
You can use Roles class static method IsUserInRole:
#if (Roles.IsUserInRole("Admin"))
{
// ...
}
The best way would be to have a property on the viewmodel that the view uses.
Related
I am trying to utilize Identity for a new application that I am working on. The minimum info that is available about it is making it hard to understand.
Based on what I understand about Identity, I can create roles (i.e Admin, Super User, Users...) and for each action in the controller I can restrict whether a role can access the action or not. This an a decent approach for typical application. But in my application I need a deeper control on the access level. I need to be able to have more control not a role base autherization.
In a typical application I can add this over an action method [Authorize(Roles = "Admin")] in a controller, and only people with Admin will be able authorized to access it.
But, I want to be able to add individual permission and not sure if this is possible using Identity. (Perhaps something to do with Claims?)
Here are my thoughts, I want to be able to do something like this
// In a Razor view I want to be able to do this
// Note: HasPermission is not real method, but is something I am posting
// to explain what I am looking for
if(User.HasPermission("Edit Account")){
// Show the Edit button
}
if(User.HasPermission("Add Account")){
// Show the Add Button
}
Then, in the action methods I want to do be able to do something like this.
[Authorize( HasPermission = "Edit Account")]
public ActionResult Edit()
{
// Handle the edit
}
[Authorize( HasPermission = "Add Account")]
public ActionResult Add()
{
// Handle the addition
}
The application will have many permission (i.e Edit Account, Add Account, Delete Account ....) I will assigned multiple permission to one user. The user with the permission Add Account will be able to add account.
Can something like this be done with Identity out of the box? How can I get started without complicating the problem?
You can extend the Identity workflow by adding Permission concept to the base. The most important thing you should care about is the relation between the current model and new added functionality. I did that in my latest project and publish it as an open source Permission Extension to Identity 2 membership system here.
You can read the Readme file and also feel free to ask your question or extend the library yourself.
As a short example you can use it like this:
First approach:
// GET: /Manage/Index
[AuthorizePermission(Name = "Show_Management", Description = "Show the Management Page.")]
public async Task<ActionResult> Index(ManageMessageId? message)
{
//...
}
Second approach:
// GET: /Manage/Users
public async Task<ActionResult> Users()
{
if (await HttpContext.AuthorizePermission(name: "AllUsers_Management", description: "Edit all of the users information."))
{
return View(db.GetAllUsers());
}
else if (await HttpContext.AuthorizePermission(name: "UnConfirmedUsers_Management", description: "Edit unconfirmed users information."))
{
return View(db.GetUnConfirmedUsers());
}
else
{
return View(new List<User>());
}
}
Claims tell you who the user is, not what the user can do. For example, their may be a claim for the user's first name, their email address and so on. There may also be claims for the user's roles, or groups they belong to (there is a subtle difference, but let's ignore that for now). For example, there may be role claims stating the user is an accountant and a manager.
As you noticed, these claims don't tell you what the user can do. It may be the case that an accountant can edit an account, but the claims won't tell you that. Somewhere somehow, you'll have to make a translation from roles (and other claims) to permissions (as you call them).
Unfortunately, there is no standard component in the .NET Framework to do that. The only thing that ever came close was AzMan, but that is now deprecated.
That means you have two options:
You can role your own. It's not too difficult. Basically, you need a data structure with a many to many relationship between roles and tasks, and a many to many relationship between tasks and individual, fine-grained permissions. The Azman model can be a good source of inspiration.
The alternative, which many people take, and for simple applications is probably good enough, is to hardcode the roles in the application. That's where [Authorize(Roles = "Accountant")] comes in. Obviously, this model requires recompilation whenever the organization of the business changes, but that may be OK if it doesn't happen too often.
Unfortunately, there is no better answer.
I have three types of roles for each of the menu links.
When the Billing guy is logging into the site
how can I determine dynamically the partial.html file that is shown in the content area?
I can not hardcode the content to the first actionlink in the menu, that means that always the Administration is loaded initially.
What can I do in such a case?
These types of decisions are best made in the Controller.
Example:
public HomeController: Controller
{
public ActionResult Administration()
{
// Determine the user's role.
// "GetRole()" does not really exist on the controller - use your own method.
string role = GetRole();
if (role == "Billing Guy")
return View("AdministrationBillingGuy")
else if (role == "SalesGuy")
return View("AdministrationSalesGuy")
else
return View();
// etc.
}
}
I can think of several ways to do this.
if you need all users to get the same url/action then you could do something like this
public ActionResult Custom(RoleEnum userRole)
{
switch(userRole)
{
case RoleEnum.Admin:
.....
return Partial("_adminPartial", viewModel);
// rest of you cases here
}
}
OR:
public ActionResult Custom(RoleEnum userRole)
{
var view = GetViewByRole(userRole);
// where GetViewByRole takes the enum and
// returns a string with the name of the partial
return Partial(view, viewModel);
}
Another way to do this is, and one that I'd recommend is to make an MVC Area for each user requiring a different layout and then at login you can redirect them to the proper Area, I recommend it because it allows for deeper differentiation between roles in the UI layer.
Another way to achieve the different layouts (am talking about MVC Layout Pages similar to ASP.Net Master pages) is to pass a string Layout to the view, using the ViewBag or any other method you like, then in the Razor code you could do something like this:
#model MyViewModel
#{
Layout = (string)ViewBag.Layout;
}
I leaved this last one for last as it appears a bit hacky to me. Hope this helps you
Well, you haven't provided enough information give any explicit direction, but generally, you should just alter your login post action to redirect to a different place depending on some identifying factor like a role (following is pseudocode)
// do login
if (user is "Billing")
{
// redirect to billing action
}
// etc.
The only reason you should be switching out partials or views is if you're doing a SPA (single page application) and utilizing JavaScript for routing. In that case, you would just need some endpoint you could hit with AJAX to get the user's "role".
However, I don't think that's what you're actually doing. If you're just using MVC directly, then you should be actually changing the URL, not just loading a different Razor view.
So I want to have a status page that will show a different layout of the equipment depending on who's using it which will be a variable defined in the web.config. Should I be creating a separate controller per view? Some of the background functions should be similar but some will probably be different in the future. Or should I have the same cshtml file and hide html markup depending on who's using it or not?
I was thinking of doing something like:
#if(System.Configuration.ConfigurationManager.AppSettings["IsSuperUser"] == "true")
{
Status
}
else {
Status
}
Or is this a bad idea?
There are several options, it all depends on your needs and preferences.
Your code will work, however you must also double check permission in your controller! For example, your url will be "/SuperUser/Status" and "/User/Status". Now, what's stopping non-super user to type in "/SuperUser/Status" to the address bar?
One important rule, never trust the end users! Assume that they will not do what you intend them to do.
Given all, my preference would be to include a variable in your Model to identify the user level (super vs non super), then use that to determine the layout in your views. Remember, you can also change the layout of the view based on variable/expression.
#Layout = Model.IsSuperUser ? "_SuperLayout.cshtml" : "_RegularLayout.cshtml";
Sounds like a view concern. I would pass the config data through a dependency in the controller and render partials:
#if (Model.IsSuperUser)
{
#Html.Partial("_SuperUser")
}
else
{
#Html.Partial("_User")
}
The controller can then do something like:
public ActionResult Index()
{
var vm = new MyViewModel();
vm.IsSuperUser = _config.GetSuperUser();
return View(vm);
}
In my project I have implemented ASP.NET Identity 2.x with claims based authorization plus authentication.
I have added added support for a claims authorization attribute as described here.
Here are the claims I'va come up with that would allow/disallow CRUD on employees.
public class ResourceClaimTypes
{
public const string CreateEmployee = "urn:company:Employee:Create";
public const string ReadEmployee = "urn:company:Employee:Read";
public const string UpdateEmployee = "urn:company:Employee:Update";
public const string DeleteEmployee = "urn:company:Employee:Delete";
}
An action would look like this:
[ClaimsAuthorize( ResourceClaimTypes.ReadEmployee )]
public ActionResult Index()
{
return View();
}
What I don't quite understand is how to make use of those claims in a view and its view model.
For instance, there is a view for displaying employees, which is a simple grid.
Then there are views for editing and creating employees.
What the view and view model should be capable of doing now is hiding or showing the Save/Update/Delete buttons according to the user's claims.
Approach on the views:
Index -> should display all employees if ReadEmployee claim is present, otherwise the view should still be accessible, but with a message "No premission to view employees".
Create/Edit -> the user should still be able to nvaigate to these views, but the "Create"/"Save" buttons should not be visible.
Delete -> all "Delete" buttons should be hidden.
Bottom line is, views should be accessible, but the Create/Save buttons should be hidden.
How can that be done?
* UPDATE / MY SOLUTION *
This is how I ended up doing it.
Following Derek's suggestion I have used Action/Resource based authentication. Along with ASP.NET Identity I have implemented the IUserClaimStore interface to grab the claims from the DB.
The Views and ViewModels (!) do NOT contain ANYTHING like CanRead, CanWrite!
I am using KendoUI and have created an extension method for buttons.
Inside the extension method I can access the custom ResourceAuthorizationManager (see blog from Dominik Baier). So when creating the button, I can call HttpContaxtBase.CheckAccess(...) to determine if the button should be enabled/visible, or not.
The only thing I need is a way to tell the extension method what action/resource combination to check access for.
Razor Example:
#Html.LinkButton(Action.Create, Resource.Employee)
This is all that is needed in the view to display (or not) a button that says "Create" and points to the Create view of the Employee controller.
Very clean, IMHO.
You should look at a product of Dominic Baier at Thinktecture for something like this.
The following article will explain how to achieve what your looking for quite elegantly.
http://leastprivilege.com/2014/06/24/resourceaction-based-authorization-for-owin-and-mvc-and-web-api/
They have example in there Git Hub repo.
** EDIT **
Here is the link to the GitHub Example you need to follow.
https://github.com/IdentityModel/Thinktecture.IdentityModel/tree/master/samples/OWIN/ResourceAuthorization/Chinook
You have access current user's claims everywhere in your project and views are not an exception. Simply cast current user's identity to claimsIdentity to access user's claims:
var claims= ((ClaimsIdentity)HttpContext.Current.User.Identity).Claims;
You could write an extension method for this also:
public static bool CanEdit(this IIdentity identity)
{
return identity.IsAuthenticated
&& identity is ClaimsIdentity
&& ((ClaimsIdentity)identity).HasClaim(x =>
x.Type == "EditClaim" && x.Value == "true");
}
Then you could easily write this code:
if(HttpContext.Current.User.Identity.CanEdit())
{
//your code
}
But even you could check claims in views directly, consider check claims in your controllers and send simple true or false values by view models to your views for better approach.
Since we do not want mix our logic to views. It is much better to check privileges in controller instead. Consider this:
class PrivilegesViewModel
{
public bool CanEdit{get;set;}
public bool CanRead{get;set;}
// and so on
}
class PostViewModel
{
// our model data
public PrivilegesViewModel Privileges{get;set;}
}
And in your controller:
public ActionResult Edit(int id)
{
PostViewModel model=_postManager.Get(id);
model.Privileges=new PrivilegesViewModel
{
CanEdit=HttpContext.Current.User.Identity.CanEdit(),
// and so on
}
return View(model);
}
Now in your view just check bool values;
#if(model.Privileges.CanEdit)
{
// print the button
}
I am using the Membership provider on a MVC Razor website.
After the customer creates an account and then logs in, I need to make sure they add a system account(s) to their login otherwise the following pages will have issues.
Here is what my page looks like:
As you can see, they can click on any of the tabs at the top and circumvent this screen.
What is the best way to handle this?
Should I disable the tabs? If so, where would I disable them? Or should I do a check on each page and redirect them back to this page?
Thanks for the help!
You should check whether that information has been entered before loading any other page. (probably in an action filter)
If you just disable the links, malicious users can navigate directly to the URLs.
Use RedirectToAction() if they have not created one e.g.
public ActionResult OrderGas(){
// do check here to see if they have system account
if (!hasSystemAccount()){
// this will re-display the add another account page each time
return RedirectToAction("AddAnotherAccountAction");
}
//other wise continue;
return view("OrderGas");
}
Alternatively you can use javascript to hide the buttons depending on a model
public ActionResult OrderGas(){
// do check here to see if they have system account
Boolean hasAccount = this.hasSystemAccount();
// apply to the model
model.hasSystemAccount = hasAccount;
return view("OrderGas", model);
}
Then your jquery can check this value and hide the links accordingly
if($('#hasSystemAccount').val().toLower() = "false"){
$('.myLinkClass').hide();
}
Have you considered using an additional Membership ROLE which indicates a customer has added the 'system account' information, such as CustomerWithSystemAccountInfo.
Then you can protect the controller actions with [Authorize(Roles = "CustomerWithSystemAccountInfo")]
As far as hiding or disabling the menu options in _layout based on ROLE the following article presents a way of doing it that I quite like:
http://techbrij.com/981/role-based-menu-asp-net-mvc